fixed cloud console crash error

This commit is contained in:
Eric 2022-09-19 18:46:08 -07:00
parent 86b931dfcc
commit 68ffe1f2bd
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
16 changed files with 151 additions and 150 deletions

View File

@ -3995,50 +3995,50 @@
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e", "assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34", "assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34",
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a", "ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/bc/d7/f65513a0b3d5fa8b5f4cfe342052", "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b3/94/954559711625a0bb6a29d41c35ff",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/06/03/381b1b4470868245225dd4e4bb67", "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/44/1e/7eec4d37c0c2afb581927efbb2dc",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/52/b0/36011f65de64a547009bef7fe6ec", "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/92/75/2d6b53a09ce1fe7cbe3b50d832de",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e2/46/384efcdbfb324b63889d567e7f14", "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cf/eb/2f2119b7c86ecbfb2a8b725b21f6",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/8b/27/7bab35742af8c101f4aab3ebbd3d", "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c2/95/be04f3219b078c92a787627b42b7",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3b/d0/c1ee5a02a09c1d676a22a20fe8a7", "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/53/f5/3bd3ba9c59b87ff7b8abc4233844",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e9/2e/3ae4fb2983bb36167d1fe3044358", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5e/21/a913309416dde18bc4c9104c3ec5",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9e/93/12e8d5357db44c97549fe2fbd7ca", "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/28/25/54314d17a5a12be51a3b340359bd",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/53/6d/06e5987cfcf47d740ead2e8c3b7f", "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/07/39/13923a2f10d8e2a8d1d12007cbed",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/1b/5b/fd0fa7104c3e2d3a22c180db47cf", "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a7/86/8cfb723071ad46355af9166ba59a",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ca/98/0e12c6aa44fc9e64492b6da67b02", "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ad/73/6013ee6879fae3c3e06b15396fb0",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e6/81/aacab39968075487c9f610e3036c", "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/da/8b/f33a546e2342591ab84ddcf4053e",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/94/bc/7a2fb10033217cac2fc04ccd0309", "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f8/b7/fffc4c52e906890b35c936441b60",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a4/da/b7095762fbdef22aff5d77c5d45d", "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/27/24/2ca5005df08ea5a7a78cd20b020a",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8b/05/2051b310f7989aa9aa04339de2f3", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cd/8d/c59fcc270b637990d12cd8a758e0",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2d/6e/3befc00833dc482ef55b70a915fe", "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/54/a9/78e4658fd4446898b11633191c01",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/82/ce/77695599588b675fac1e10febeae", "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/53/30/cd915e748f31208239b90f5a7a2c",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3a/c3/d042e59d36f71c885036ed0e73d4", "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a9/4e/c367a7a66eb17df7757dc85d59b6",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/87/48/b1551cd7b5cf44c06a3e3a1a66a4", "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/5d/13/38ab5bdd9fdb0abca878d0bbfbab",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/6d/e2/3366be4f442e6ef48b5ef5babe87", "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/02/62/c3a8023aa43567204407325d6c4c",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d6/9a/5e36fb51a797da74db8f366a8e6d", "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/17/f0d22a9a91a295ee4a9d94db2af9",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/98/d7/e827cc644d4dde0c1bddb17121b0", "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d5/4f/bfbcdd18679b4ba0df1b040e8c15",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9b/e0/a1ae5fdadff116de78727e2a8d30", "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/96/e9/0f4aa3d10f2d50fe670bb6d89ba0",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/49/76/fd27d293fd73d324ecc433b1e253", "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/26/ef/1b58d3c788441242ed9ca9352499",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1e/93/b85c7585218b5c86d766a23373c6", "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/79/77/b35c5f93f62d92fe9cdb548f6e54",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/34/0a/4547b5a8a00a35874cfbab25fc11", "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c6/53/8e678b75f456378e17e4f639fe35",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/68/5a/a4a030424d8d5900433bdb70bd83", "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ab/1d/b514c16cfda2f5840676a4c75ca3",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e1/4b/278433e2332245313d4fa3390aee", "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d7/c4/b797fd5365d34fcfb60d6275fb3c",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/22/c1/fc9726d56ac62c757e7f7eeb56e2", "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b0/6c/b8f8c1d1d8e2f772208af12e085f",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/4f/ee03bed8d42b8e12832efbd17968", "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c0/e1/f57102a8da2fe0ca7787c0a7924a",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/dc/7a/f5bae1a284d2b51e149f12da9e18", "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8f/16/19494ad899b3f1990dd2b5cdfa99",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2e/75/254680a01a85fcdc19db275cd2ff", "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a9/d5/59ba30f21d5583bdc92516c0916e",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/79/fb/65e1f1851f2dc663eb5d9dd27ae9", "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e7/82/e29dbff494964b753a08eaa66b66",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/24/5d/33ca757aa471e81ee2efe83b5c32", "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2f/06/ec124d5bfce4d3f268b1318755b8",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/83/3e/35613478ab4887b7f77126c1d910", "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3f/47/3322caea5f9a92735076c1017839",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ba/a7/b925b7eb7b2da10fdc9c88e59576", "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f5/9f/ce9a27aea9a4b7056800cf68afe9",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/0a/ae/daac4e04ef473d0da419ebe2ddf2", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/6c/56/6180e5d6db6868b7438751320faf",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/52/9d/d6c5366d82b56d1c79553ae3258b", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/71/24/e873efab9c315ec907427bd22327",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/35/db/9f6868dde4184cb3b33153981548", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/3d/d6/b1a3c97a66d3f5ad38d5f5dda476",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/0c/e0/a869977d8ee1dcf4bcbb0853fe3c", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/5a/e9/14593b84a3035a1bd7ce3980818d",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/31/c1/e51379d7cf51aec768855e6e7eb7", "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/87/5c/38b4d59ad04f6ba89dae6ca38fa5",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/0f/48/52683efe93f7311f2a81a280a87e", "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/3c/0a/6b151c9054294db58cf98957a515",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/7c/05/660a71d76685232d9bd91d979ef8", "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/e3/fb/7be0f96c82816af61849aaf4357a",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/30/5a/b2e0ce3986936938c65e4bef2d0e", "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/23/dc/91a8e0f2123a75b4d72a0d26ab25",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523", "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02", "src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02",
"src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd" "src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd"

View File

@ -1,4 +1,4 @@
### 1.7.7 (build 20862, api 7, 2022-09-17) ### 1.7.7 (build 20865, api 7, 2022-09-19)
- Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread. - Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread.
- Improved logging of missing playlist game types. - Improved logging of missing playlist game types.
- Some ba.Lstr functionality can now be used in background threads. - Some ba.Lstr functionality can now be used in background threads.
@ -39,6 +39,8 @@
- If you want to grab recent logs, you can now use `ba.app.log_handler.get_cached()`. This will give you everything that has gone through Python logging, Python stdout/stderr, and the C++ Log() call (up to the max cache size that is). - If you want to grab recent logs, you can now use `ba.app.log_handler.get_cached()`. This will give you everything that has gone through Python logging, Python stdout/stderr, and the C++ Log() call (up to the max cache size that is).
- LogHandler output now ALWAYS goes to stderr. Previously it only would if an interactive terminal was detected. This should make the binary easier to debug if run from scripts/etc. We can add a `--quiet` option if needed or whatnot. - LogHandler output now ALWAYS goes to stderr. Previously it only would if an interactive terminal was detected. This should make the binary easier to debug if run from scripts/etc. We can add a `--quiet` option if needed or whatnot.
- (build 20859) Fixed an error setting up asyncio loops under Windows related to the fact that Python is now inited in the main thread. - (build 20859) Fixed an error setting up asyncio loops under Windows related to the fact that Python is now inited in the main thread.
- (build 20864) Fatal-error message/traceback now properly prints to stderr again (I think the reject logging rejiggering caused it to stop).
- (build 20864) Fixed an issue where the app could crash when connected to the cloud console while in a network game.
### 1.7.6 (build 20687, api 7, 2022-08-11) ### 1.7.6 (build 20687, api 7, 2022-08-11)

View File

@ -1 +1 @@
84155513838284412598729205538639495871 137071025041513581787580065580079045765

View File

@ -44,7 +44,7 @@ def bootstrap() -> None:
# Give a soft warning if we're being used with a different binary # Give a soft warning if we're being used with a different binary
# version than we expect. # version than we expect.
expected_build = 20862 expected_build = 20865
running_build: int = env['build_number'] running_build: int = env['build_number']
if running_build != expected_build: if running_build != expected_build:
print( print(

View File

@ -32,7 +32,7 @@
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 kAppBuildNumber = 20862; const int kAppBuildNumber = 20865;
const char* kAppVersion = "1.7.7"; const char* kAppVersion = "1.7.7";
// Our standalone globals. // Our standalone globals.

View File

@ -27,7 +27,7 @@ auto FatalError::ReportFatalError(const std::string& message,
// error to the user and exiting the app cleanly (so we don't pollute our // error to the user and exiting the app cleanly (so we don't pollute our
// crash records with results of user tinkering). // crash records with results of user tinkering).
// Give the platform the opportunity to completely override our handling. // Give the platform the opportunity to augment or override our handling.
if (g_platform) { if (g_platform) {
auto handled = auto handled =
g_platform->ReportFatalError(message, in_top_level_exception_handler); g_platform->ReportFatalError(message, in_top_level_exception_handler);
@ -38,7 +38,8 @@ auto FatalError::ReportFatalError(const std::string& message,
std::string dialog_msg = message; std::string dialog_msg = message;
if (!dialog_msg.empty()) { if (!dialog_msg.empty()) {
dialog_msg += "\n"; // Why was this here?
// dialog_msg += "\n";
} }
auto starttime = time(nullptr); auto starttime = time(nullptr);
@ -75,9 +76,10 @@ auto FatalError::ReportFatalError(const std::string& message,
g_early_v1_cloud_log_writes = 0; g_early_v1_cloud_log_writes = 0;
// Add this to our V1CloudLog which we'll be attempting to send momentarily, // Add this to our V1CloudLog which we'll be attempting to send momentarily,
// and also try to present it directly to the user. // and also go to platform-specific logging and good ol' stderr.
Logging::V1CloudLog(logmsg); Logging::V1CloudLog(logmsg);
Logging::DisplayLog("root", LogLevel::kCritical, logmsg); Logging::DisplayLog("root", LogLevel::kCritical, logmsg);
fprintf(stderr, "%s\n", logmsg.c_str());
std::string prefix = "FATAL-ERROR-LOG:"; std::string prefix = "FATAL-ERROR-LOG:";
std::string suffix; std::string suffix;

View File

@ -1050,15 +1050,10 @@ void Logic::PushPythonRawCallable(PyObject* callable, bool fg_context) {
thread()->PushCall([this, callable, fg_context] { thread()->PushCall([this, callable, fg_context] {
assert(InLogicThread()); assert(InLogicThread());
// Lets run this in the UI context. // Run this in the UI context by default, or foreground if requested.
// (can add other options if we need later)
ScopedSetContext cp(fg_context ? GetForegroundContext() : GetUIContext()); ScopedSetContext cp(fg_context ? GetForegroundContext() : GetUIContext());
// This event contains a raw python obj with an incremented ref-count. PythonRef(callable, PythonRef::kSteal).Call();
auto call(Object::New<PythonContextCall>(callable));
Py_DECREF(callable); // now just held by call
call->Run();
}); });
} }

View File

@ -83,8 +83,8 @@ class Logic {
auto PushPythonWeakCallArgs(const Object::WeakRef<PythonContextCall>& call, auto PushPythonWeakCallArgs(const Object::WeakRef<PythonContextCall>& call,
const PythonRef& args) -> void; const PythonRef& args) -> void;
// Push a raw Python call, decrements its refcount after running. // Push a raw Python call from any thread. Refcount should be incremented
// Can be pushed from any thread. // beforehand and will be decremented after running.
auto PushPythonRawCallable(PyObject* callable, bool fg_context = false) auto PushPythonRawCallable(PyObject* callable, bool fg_context = false)
-> void; -> void;
auto PushScreenMessage(const std::string& message, const Vector3f& color) auto PushScreenMessage(const std::string& message, const Vector3f& color)

View File

@ -102,7 +102,7 @@ auto PythonClassContextCall::tp_new(PyTypeObject* type, PyObject* args,
if (!PyArg_ParseTuple(args, "O", &source_obj)) return nullptr; if (!PyArg_ParseTuple(args, "O", &source_obj)) return nullptr;
if (!InLogicThread()) { if (!InLogicThread()) {
throw Exception( throw Exception(
"ERROR: " + std::string(type_obj.tp_name) std::string(type_obj.tp_name)
+ " objects must only be created in the logic thread (current is (" + " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ")."); + GetCurrentThreadName() + ").");
} }

View File

@ -14,17 +14,17 @@ PythonContextCall* PythonContextCall::current_call_ = nullptr;
PythonContextCall::PythonContextCall(PyObject* obj_in) { PythonContextCall::PythonContextCall(PyObject* obj_in) {
assert(InLogicThread()); assert(InLogicThread());
// as a sanity test, store the current context ptr just to make sure it // As a sanity test, store the current context ptr just to make sure it
// hasn't changed when we run // hasn't changed when we run.
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
context_target_sanity_test_ = context_.target.get(); context_target_sanity_test_ = context_.target.get();
#endif // BA_DEBUG_BUILD #endif // BA_DEBUG_BUILD
BA_PRECONDITION(PyCallable_Check(obj_in)); BA_PRECONDITION(PyCallable_Check(obj_in));
object_.Acquire(obj_in); object_.Acquire(obj_in);
GetTrace(); GetTrace();
// ok now we need to register this call with whatever the context is; // Ok now we need to register this call with whatever the context is;
// it can be stored in a host-activity, a host-session, or the UI context. // it can be stored in a host-activity, a host-session, or the UI context.
// whoever it is registered with will explicitly release its contents on // Whoever it is registered with will explicitly release its contents on
// shutdown and ensure that nothing gets run after that point. // shutdown and ensure that nothing gets run after that point.
if (HostActivity* ha = context_.GetHostActivity()) { if (HostActivity* ha = context_.GetHostActivity()) {
ha->RegisterCall(this); ha->RegisterCall(this);

View File

@ -18,7 +18,7 @@ from efro.dataclassio import ioprepped
from efro.message import (Message, Response, MessageProtocol, MessageSender, from efro.message import (Message, Response, MessageProtocol, MessageSender,
BoundMessageSender, MessageReceiver, BoundMessageSender, MessageReceiver,
BoundMessageReceiver, UnregisteredMessageIDError, BoundMessageReceiver, UnregisteredMessageIDError,
EmptyResponse) EmptySysResponse)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Callable, Awaitable from typing import Any, Callable, Awaitable
@ -424,8 +424,8 @@ TEST_PROTOCOL = MessageProtocol(
0: _TResp1, 0: _TResp1,
1: _TResp2, 1: _TResp2,
}, },
receiver_returns_stack_traces=True, forward_clean_errors=True,
receiver_logs_exceptions=False, remote_errors_include_stack_traces=True,
) )
# Represents an 'evolved' TEST_PROTOCOL (one extra message type added). # Represents an 'evolved' TEST_PROTOCOL (one extra message type added).
@ -441,8 +441,8 @@ TEST_PROTOCOL_B = MessageProtocol(
0: _TResp1, 0: _TResp1,
1: _TResp2, 1: _TResp2,
}, },
receiver_returns_stack_traces=True, forward_clean_errors=True,
receiver_logs_exceptions=False, remote_errors_include_stack_traces=True,
) )
TEST_PROTOCOL_SINGLE = MessageProtocol( TEST_PROTOCOL_SINGLE = MessageProtocol(
@ -452,8 +452,7 @@ TEST_PROTOCOL_SINGLE = MessageProtocol(
response_types={ response_types={
0: _TResp1, 0: _TResp1,
}, },
receiver_returns_stack_traces=True, remote_errors_include_stack_traces=True,
receiver_logs_exceptions=False,
) )
@ -463,12 +462,16 @@ def test_protocol_creation() -> None:
# This should fail because _TMsg1 can return _TResp1 which # This should fail because _TMsg1 can return _TResp1 which
# is not given an id here. # is not given an id here.
with pytest.raises(ValueError): with pytest.raises(ValueError):
_protocol = MessageProtocol(message_types={0: _TMsg1}, _protocol = MessageProtocol(
response_types={0: _TResp2}) message_types={0: _TMsg1},
response_types={0: _TResp2},
)
# Now it should work. # Now it should work.
_protocol = MessageProtocol(message_types={0: _TMsg1}, _protocol = MessageProtocol(
response_types={0: _TResp1}) message_types={0: _TMsg1},
response_types={0: _TResp1},
)
def test_sender_module_single_emb() -> None: def test_sender_module_single_emb() -> None:
@ -777,7 +780,7 @@ def test_full_pipeline() -> None:
# Emulate forwarding unregistered messages on to some # Emulate forwarding unregistered messages on to some
# other handler... # other handler...
response_dict = self.msg.protocol.response_to_dict( response_dict = self.msg.protocol.response_to_dict(
EmptyResponse()) EmptySysResponse())
return self.msg.protocol.encode_dict(response_dict) return self.msg.protocol.encode_dict(response_dict)
raise raise
@ -909,7 +912,8 @@ def test_full_pipeline() -> None:
assert response3 is None assert response3 is None
assert isinstance(response4, _TResp1) assert isinstance(response4, _TResp1)
# Remote CleanErrors should come across locally as the same. # Remote CleanErrors should come across locally as the same
# (provided our protocol has enabled support for them).
try: try:
_response5 = obj.msg.send(_TMsg1(ival=1)) _response5 = obj.msg.send(_TMsg1(ival=1))
except Exception as exc: except Exception as exc:

View File

@ -11,15 +11,16 @@ from efro.message._protocol import MessageProtocol
from efro.message._sender import (MessageSender, BoundMessageSender) from efro.message._sender import (MessageSender, BoundMessageSender)
from efro.message._receiver import (MessageReceiver, BoundMessageReceiver) from efro.message._receiver import (MessageReceiver, BoundMessageReceiver)
from efro.message._module import (create_sender_module, create_receiver_module) from efro.message._module import (create_sender_module, create_receiver_module)
from efro.message._message import (Message, Response, EmptyResponse, from efro.message._message import (Message, Response, EmptySysResponse,
ErrorResponse, StringResponse, BoolResponse, ErrorSysResponse, StringResponse,
UnregisteredMessageIDError) BoolResponse, UnregisteredMessageIDError)
__all__ = [ __all__ = [
'Message', 'Response', 'EmptyResponse', 'ErrorResponse', 'StringResponse', 'Message', 'Response', 'EmptySysResponse', 'ErrorSysResponse',
'BoolResponse', 'MessageProtocol', 'MessageSender', 'BoundMessageSender', 'StringResponse', 'BoolResponse', 'MessageProtocol', 'MessageSender',
'MessageReceiver', 'BoundMessageReceiver', 'create_sender_module', 'BoundMessageSender', 'MessageReceiver', 'BoundMessageReceiver',
'create_receiver_module', 'UnregisteredMessageIDError' 'create_sender_module', 'create_receiver_module',
'UnregisteredMessageIDError'
] ]
# Have these things present themselves cleanly as 'thismodule.SomeClass' # Have these things present themselves cleanly as 'thismodule.SomeClass'

View File

@ -27,12 +27,12 @@ class Message:
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response]]:
"""Return all message types this Message can result in when sent. """Return all message types this Message can result in when sent.
The default implementation specifies EmptyResponse, so messages with The default implementation specifies EmptySysResponse, so messages with
no particular response needs can leave this untouched. no particular response needs can leave this untouched.
Note that ErrorMessage is handled as a special case and does not Note that ErrorMessage is handled as a special case and does not
need to be specified here. need to be specified here.
""" """
return [EmptyResponse] return [EmptySysResponse]
class Response: class Response:
@ -44,7 +44,7 @@ class Response:
@ioprepped @ioprepped
@dataclass @dataclass
class ErrorResponse(Response): class ErrorSysResponse(Response):
"""Response saying some error has occurred for the send. """Response saying some error has occurred for the send.
This type is unique in that it is not returned to the user; it This type is unique in that it is not returned to the user; it
@ -64,12 +64,12 @@ class ErrorResponse(Response):
@ioprepped @ioprepped
@dataclass @dataclass
class EmptyResponse(Response): class EmptySysResponse(Response):
"""The response equivalent of None.""" """The response equivalent of None."""
# TODO: could allow handlers to deal in raw values for these # TODO: could allow handlers to deal in raw values for these
# types similar to how we allow None in place of EmptyResponse. # types similar to how we allow None in place of EmptySysResponse.
# Though not sure if they are widely used enough to warrant the # Though not sure if they are widely used enough to warrant the
# extra code complexity. # extra code complexity.
@ioprepped @ioprepped

View File

@ -14,8 +14,9 @@ import json
from efro.error import CleanError from efro.error import CleanError
from efro.dataclassio import (is_ioprepped_dataclass, dataclass_to_dict, from efro.dataclassio import (is_ioprepped_dataclass, dataclass_to_dict,
dataclass_from_dict) dataclass_from_dict)
from efro.message._message import (Message, Response, ErrorResponse, from efro.message._message import (Message, Response, ErrorSysResponse,
EmptyResponse, UnregisteredMessageIDError) EmptySysResponse,
UnregisteredMessageIDError)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Literal from typing import Any, Literal
@ -33,29 +34,20 @@ class MessageProtocol:
def __init__(self, def __init__(self,
message_types: dict[int, type[Message]], message_types: dict[int, type[Message]],
response_types: dict[int, type[Response]], response_types: dict[int, type[Response]],
preserve_clean_errors: bool = True, forward_clean_errors: bool = False,
receiver_logs_exceptions: bool = True, remote_errors_include_stack_traces: bool = False) -> None:
receiver_returns_stack_traces: bool = False) -> None:
"""Create a protocol with a given configuration. """Create a protocol with a given configuration.
Note that common response types are automatically registered Note that common response types are automatically registered
with (unchanging negative ids) so they don't need to be passed with (unchanging negative ids) so they don't need to be passed
explicitly (but can be if a different id is desired). explicitly (but can be if a different id is desired).
If 'preserve_clean_errors' is True, efro.error.CleanError If 'forward_clean_errors' is True, efro.error.CleanError
exceptions raised on the receiver end will result in a matching exceptions raised on the receiver end will result in a matching
CleanError raised back on the sender. All other Exception types CleanError raised back on the sender. All other Exception types
come across as efro.error.RemoteError. come across as efro.error.RemoteError.
When 'receiver_logs_exceptions' is True, any uncaught Exceptions If 'remote_errors_include_stack_traces' is True, stringified stack
on the receiver end will be logged there via logging.exception()
(in addition to the usual behavior of returning an ErrorResponse
to the sender). This is good to leave enabled if your
intention is to never return ErrorResponses. Looser setups
making routine use of CleanErrors or whatnot may want to
disable this, however.
If 'receiver_returns_stack_traces' is True, stringified stack
traces will be returned to the sender for exceptions occurring traces will be returned to the sender for exceptions occurring
on the receiver end. This can make debugging easier but should on the receiver end. This can make debugging easier but should
only be used when the client is trusted to see such info. only be used when the client is trusted to see such info.
@ -88,15 +80,20 @@ class MessageProtocol:
# Go ahead and auto-register a few common response types # Go ahead and auto-register a few common response types
# if the user has not done so explicitly. Use unique negative # if the user has not done so explicitly. Use unique negative
# IDs which will never change or overlap with user ids. # IDs which will never change or overlap with user ids.
def _reg_if_not(reg_tp: type[Response], reg_id: int) -> None: def _reg_sys(reg_tp: type[Response], reg_id: int) -> None:
# If we have a positive id registered already, we still point
# negative sys id at this type but not the opposite.
if reg_tp in self.response_ids_by_type: if reg_tp in self.response_ids_by_type:
self.response_types_by_id[reg_id] = reg_tp
return return
assert self.response_types_by_id.get(reg_id) is None assert self.response_types_by_id.get(reg_id) is None
self.response_types_by_id[reg_id] = reg_tp self.response_types_by_id[reg_id] = reg_tp
self.response_ids_by_type[reg_tp] = reg_id self.response_ids_by_type[reg_tp] = reg_id
_reg_if_not(ErrorResponse, -1) _reg_sys(ErrorSysResponse, -1)
_reg_if_not(EmptyResponse, -2) _reg_sys(EmptySysResponse, -2)
# Some extra-thorough validation in debug mode. # Some extra-thorough validation in debug mode.
if __debug__: if __debug__:
@ -127,9 +124,9 @@ class MessageProtocol:
'message_types contains duplicate __name__s;' 'message_types contains duplicate __name__s;'
' all types are required to have unique names.') ' all types are required to have unique names.')
self.preserve_clean_errors = preserve_clean_errors self.forward_clean_errors = forward_clean_errors
self.receiver_logs_exceptions = receiver_logs_exceptions self.remote_errors_include_stack_traces = (
self.receiver_returns_stack_traces = receiver_returns_stack_traces remote_errors_include_stack_traces)
@staticmethod @staticmethod
def encode_dict(obj: dict) -> str: def encode_dict(obj: dict) -> str:
@ -147,21 +144,20 @@ class MessageProtocol:
def error_to_response(self, exc: Exception) -> Response: def error_to_response(self, exc: Exception) -> Response:
"""Translate an error to a response.""" """Translate an error to a response."""
# Log any errors we got during handling if so desired. # Log any errors we got during handling.
if self.receiver_logs_exceptions: logging.exception('Error in efro.message handling.')
logging.exception('Error in efro.message handling.')
# If anything goes wrong, return a ErrorResponse instead. # If anything goes wrong, return a ErrorSysResponse instead.
# (either CLEAN or generic REMOTE) # (either CLEAN or generic REMOTE)
if isinstance(exc, CleanError) and self.preserve_clean_errors: if isinstance(exc, CleanError) and self.forward_clean_errors:
return ErrorResponse( return ErrorSysResponse(
error_message=str(exc), error_message=str(exc),
error_type=ErrorResponse.ErrorType.REMOTE_CLEAN) error_type=ErrorSysResponse.ErrorType.REMOTE_CLEAN)
return ErrorResponse( return ErrorSysResponse(
error_message=(traceback.format_exc() error_message=(traceback.format_exc()
if self.receiver_returns_stack_traces else if self.remote_errors_include_stack_traces else
'An internal error has occurred.'), 'An internal error has occurred.'),
error_type=ErrorResponse.ErrorType.REMOTE) error_type=ErrorSysResponse.ErrorType.REMOTE)
def _to_dict(self, message: Any, ids_by_type: dict[type, int], def _to_dict(self, message: Any, ids_by_type: dict[type, int],
opname: str) -> dict: opname: str) -> dict:
@ -236,7 +232,7 @@ class MessageProtocol:
rsptypes.append(Response) rsptypes.append(Response)
for rsp_tp in rsptypes: for rsp_tp in rsptypes:
# Skip these as they don't actually show up in code. # Skip these as they don't actually show up in code.
if rsp_tp is EmptyResponse or rsp_tp is ErrorResponse: if rsp_tp is EmptySysResponse or rsp_tp is ErrorSysResponse:
continue continue
if (single_message_type and part == 'sender' if (single_message_type and part == 'sender'
and rsp_tp is not Response): and rsp_tp is not Response):
@ -344,9 +340,9 @@ class MessageProtocol:
f' """Protocol-specific bound sender."""\n') f' """Protocol-specific bound sender."""\n')
def _filt_tp_name(rtype: type[Response]) -> str: def _filt_tp_name(rtype: type[Response]) -> str:
# We accept None to equal EmptyResponse so reflect that # We accept None to equal EmptySysResponse so reflect that
# in the type annotation. # in the type annotation.
return 'None' if rtype is EmptyResponse else rtype.__name__ return 'None' if rtype is EmptySysResponse else rtype.__name__
# Define handler() overloads for all registered message types. # Define handler() overloads for all registered message types.
if msgtypes: if msgtypes:
@ -441,9 +437,9 @@ class MessageProtocol:
# Define handler() overloads for all registered message types. # Define handler() overloads for all registered message types.
def _filt_tp_name(rtype: type[Response]) -> str: def _filt_tp_name(rtype: type[Response]) -> str:
# We accept None to equal EmptyResponse so reflect that # We accept None to equal EmptySysResponse so reflect that
# in the type annotation. # in the type annotation.
return 'None' if rtype is EmptyResponse else rtype.__name__ return 'None' if rtype is EmptySysResponse else rtype.__name__
if msgtypes: if msgtypes:
cbgn = 'Awaitable[' if is_async else '' cbgn = 'Awaitable[' if is_async else ''

View File

@ -11,8 +11,9 @@ import inspect
import logging import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from efro.message._message import (Message, Response, EmptyResponse, from efro.message._message import (Message, Response, EmptySysResponse,
ErrorResponse, UnregisteredMessageIDError) ErrorSysResponse,
UnregisteredMessageIDError)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Callable, Awaitable from typing import Any, Callable, Awaitable
@ -123,8 +124,8 @@ class MessageReceiver:
# types.UnionType above. # types.UnionType above.
responsetypes = (ret, ) # type: ignore responsetypes = (ret, ) # type: ignore
# Return type of None translates to EmptyResponse. # Return type of None translates to EmptySysResponse.
responsetypes = tuple(EmptyResponse if r is type(None) else r responsetypes = tuple(EmptySysResponse if r is type(None) else r
for r in responsetypes) # noqa for r in responsetypes) # noqa
# Make sure our protocol has this message type registered and our # Make sure our protocol has this message type registered and our
@ -236,13 +237,13 @@ class MessageReceiver:
response: Response | None) -> str: response: Response | None) -> str:
"""Encode a response provided by the user for sending.""" """Encode a response provided by the user for sending."""
# A return value of None equals EmptyResponse. # A return value of None equals EmptySysResponse.
if response is None: if response is None:
response = EmptyResponse() response = EmptySysResponse()
assert isinstance(response, Response) assert isinstance(response, Response)
# (user should never explicitly return error-responses) # (user should never explicitly return error-responses)
assert not isinstance(response, ErrorResponse) assert not isinstance(response, ErrorSysResponse)
assert type(response) in message.get_response_types() assert type(response) in message.get_response_types()
response_dict = self.protocol.response_to_dict(response) response_dict = self.protocol.response_to_dict(response)
if self._encode_filter_call is not None: if self._encode_filter_call is not None:

View File

@ -10,7 +10,7 @@ import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from efro.error import CleanError, RemoteError, CommunicationError from efro.error import CleanError, RemoteError, CommunicationError
from efro.message._message import EmptyResponse, ErrorResponse from efro.message._message import EmptySysResponse, ErrorSysResponse
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Callable, Awaitable from typing import Any, Callable, Awaitable
@ -139,12 +139,12 @@ class MessageSender:
except Exception as exc: except Exception as exc:
# Any error in the raw send call gets recorded as either # Any error in the raw send call gets recorded as either
# a local or communication error. # a local or communication error.
return ErrorResponse( return ErrorSysResponse(
error_message= error_message=
f'Error in MessageSender @send_method ({type(exc)}): {exc}', f'Error in MessageSender @send_method ({type(exc)}): {exc}',
error_type=(ErrorResponse.ErrorType.COMMUNICATION error_type=(ErrorSysResponse.ErrorType.COMMUNICATION
if isinstance(exc, CommunicationError) else if isinstance(exc, CommunicationError) else
ErrorResponse.ErrorType.LOCAL)) ErrorSysResponse.ErrorType.LOCAL))
return self._decode_raw_response(bound_obj, message, response_encoded) return self._decode_raw_response(bound_obj, message, response_encoded)
async def send_split_part_1_async(self, bound_obj: Any, async def send_split_part_1_async(self, bound_obj: Any,
@ -166,13 +166,13 @@ class MessageSender:
except Exception as exc: except Exception as exc:
# Any error in the raw send call gets recorded as either # Any error in the raw send call gets recorded as either
# a local or communication error. # a local or communication error.
return ErrorResponse( return ErrorSysResponse(
error_message= error_message=
f'Error in MessageSender @send_async_method ({type(exc)}):' f'Error in MessageSender @send_async_method ({type(exc)}):'
f' {exc}', f' {exc}',
error_type=(ErrorResponse.ErrorType.COMMUNICATION error_type=(ErrorSysResponse.ErrorType.COMMUNICATION
if isinstance(exc, CommunicationError) else if isinstance(exc, CommunicationError) else
ErrorResponse.ErrorType.LOCAL)) ErrorSysResponse.ErrorType.LOCAL))
return self._decode_raw_response(bound_obj, message, response_encoded) return self._decode_raw_response(bound_obj, message, response_encoded)
def send_split_part_2(self, message: Message, def send_split_part_2(self, message: Message,
@ -214,13 +214,13 @@ class MessageSender:
# If we got to this point, we successfully communicated # If we got to this point, we successfully communicated
# with the other end so errors represent protocol mismatches # with the other end so errors represent protocol mismatches
# or other invalid data. For now let's just log it but perhaps # or other invalid data. For now let's just log it but perhaps
# we'd want to somehow embed it in the ErrorResponse to be # we'd want to somehow embed it in the ErrorSysResponse to be
# available directly to the user later. # available directly to the user later.
logging.exception('Error decoding raw response') logging.exception('Error decoding raw response')
response = ErrorResponse( response = ErrorSysResponse(
error_message= error_message=
'Error decoding raw response; see log for details.', 'Error decoding raw response; see log for details.',
error_type=ErrorResponse.ErrorType.LOCAL) error_type=ErrorSysResponse.ErrorType.LOCAL)
return response return response
def _unpack_raw_response(self, raw_response: Response) -> Response | None: def _unpack_raw_response(self, raw_response: Response) -> Response | None:
@ -232,25 +232,25 @@ class MessageSender:
run such that any raised Exception is active when the callback run such that any raised Exception is active when the callback
fires; not on the thread where the message was sent. fires; not on the thread where the message was sent.
""" """
# EmptyResponse translates to None # EmptySysResponse translates to None
if isinstance(raw_response, EmptyResponse): if isinstance(raw_response, EmptySysResponse):
return None return None
# Some error occurred. Raise a local Exception for it. # Some error occurred. Raise a local Exception for it.
if isinstance(raw_response, ErrorResponse): if isinstance(raw_response, ErrorSysResponse):
if (raw_response.error_type is if (raw_response.error_type is
ErrorResponse.ErrorType.COMMUNICATION): ErrorSysResponse.ErrorType.COMMUNICATION):
raise CommunicationError(raw_response.error_message) raise CommunicationError(raw_response.error_message)
# If something went wrong on *our* end of the connection, # If something went wrong on *our* end of the connection,
# don't say it was a remote error. # don't say it was a remote error.
if raw_response.error_type is ErrorResponse.ErrorType.LOCAL: if raw_response.error_type is ErrorSysResponse.ErrorType.LOCAL:
raise RuntimeError(raw_response.error_message) raise RuntimeError(raw_response.error_message)
# If they want to support clean errors, do those. # If they want to support clean errors, do those.
if (self.protocol.preserve_clean_errors and raw_response.error_type if (self.protocol.forward_clean_errors and raw_response.error_type
is ErrorResponse.ErrorType.REMOTE_CLEAN): is ErrorSysResponse.ErrorType.REMOTE_CLEAN):
raise CleanError(raw_response.error_message) raise CleanError(raw_response.error_message)
# Everything else gets lumped in as a remote error. # Everything else gets lumped in as a remote error.