diff --git a/.efrocachemap b/.efrocachemap index ebb4fc92..a99a085a 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4096,56 +4096,56 @@ "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": "c8904594aa4867a7525e6644249d5fd5", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "7d3ba05e21b198bdccbdaf9d334f96f2", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "8601ef36314294c18b686995ce9a02f6", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "36742622076742a5642de8c287cd92e4", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "b05ee46d11b618cf917cae591fe6b97a", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "a9a03ded1e4fccfdc1879bd8f70e0e7a", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "05f37deacb796ef45828233ded9325e3", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "f4b4df8f17219f81148efe3dc057856f", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "b6201f83d4f80fcc567398807c7d77b5", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "331335944425c5a48abbcf137da46f0f", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "ac2d7714d87b781659c1e3333f760888", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "1e7cc9348498df9bfb7b25cd83009d7c", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "a3c7e011ad99046d2ef8b102e7d15733", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "43fd8535ad1e2c2c64fe57dacfb3305b", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "fc17d6883ddd4486a62b75494dbb9d15", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "0a557a638ee4cb3150d90bcb5913de3e", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "85afe70f81c1f7d2de9520d498c74eb8", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "a617c0412a804bce272113585c000318", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "5642e5614711b8e106de659db2b33ac7", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "ea7018647c89777469979aa08e88573e", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "ba7dc0b4e927f32fcf15c2f738fe004c", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "fb4c90da8d46ddab5847406fa397dbd5", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "ba7dc0b4e927f32fcf15c2f738fe004c", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "fb4c90da8d46ddab5847406fa397dbd5", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "225f13c4755aa018e63168c8aca0a8fd", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "d4c6e0f0ac69af106f1227daeae2db55", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "225f13c4755aa018e63168c8aca0a8fd", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "d4c6e0f0ac69af106f1227daeae2db55", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "e16971f1da7ad7f44bb185b4446965c6", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d5638285fa481d769a5cd488a78d8269", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "e16971f1da7ad7f44bb185b4446965c6", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d5638285fa481d769a5cd488a78d8269", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "184b7e4731b09ccf3a6a3b001d8af097", - "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "507b5555ef5d067caf18fe9844ca04c7", - "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "838ee9bfd43169efe8daff8bb0efb1db", - "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "507b5555ef5d067caf18fe9844ca04c7", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "d1edb04136c433b57f3842830e9b272d", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "4e64e07e3527e0536d1dc7f2dacd681a", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "301bad929a4f3c6e9f67e57ab730bded", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "6224ca7790cfa8274c3f9c8e4461b93f", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "bedcc6b5cc2a403a65d83c6244922feb", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "545edb5e8f7cce4e7bc3cdb0e4f98b82", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "79102625aebc9df3a0834f8d571fdbac", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "9e5a350dc8f5fcd1ab96c7a4c24a7ebe", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "b20c6e3a2287f1c6a9a898e78fcab105", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "0f1eff142f1e5796be2e0340dcbb4c8f", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "f40171c1c943c10e2d2120a586a711f7", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "ea494bdeb942cbab86c0371bb0558f3b", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "fba8474fd758e8d76bc6d7e90dfb7795", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4b1a35c2e6d7c2d9a01c6e2750716c24", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "9ac4ee8aab0243a233ba8650cd0d0b73", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "c804d14d91dab05f644079ae47e61728", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "1f7011d1c77548e48b7f6d3ac09a29da", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "c50790a9a2e3001dd079a6bbd753c36f", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "52528037523bf268e2c59a19626e7f2d", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "a920948fcc94c397495ef8dd674d325b", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "d0f7ad9287edd649d97844ada500a11d", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "1073804b2d2549f9eb992666fc3640c3", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "73e6ca5aee9af5a87960b7ae174f0a71", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "c09c4fac16134d5071a88ec8d3e1e950", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "e78b8bd1cb5aa452f536c57e855bcc27", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "0f08643a492ac7bf8fd3f13460003bce", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "48e684f7db6d88a62009d773ab51b065", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "69a301acc9adfacdb8b3d200390bbfdd", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "653df60722ed096b8cc3ac3a4196e5a5", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "f82c1e575e8f23c1e278db61a740a4d5", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "653df60722ed096b8cc3ac3a4196e5a5", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "f82c1e575e8f23c1e278db61a740a4d5", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "90125afd27068280c96cf37c950b641f", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "a210d341f1842b21ebbaadd80ae4a282", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "90125afd27068280c96cf37c950b641f", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "a210d341f1842b21ebbaadd80ae4a282", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "cdf4fca1c1aaae2c2294263f0717b333", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "937b954eed2571cb4d5f9d2a0e3f77ed", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "cdf4fca1c1aaae2c2294263f0717b333", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "937b954eed2571cb4d5f9d2a0e3f77ed", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "cdd7c61d6c0efd5fb0442e625123b63f", + "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "ea327f45634a8e42259c5ecadf219e36", + "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "547362a74979fa82b854a475dffeae90", + "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "ea327f45634a8e42259c5ecadf219e36", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "828f4682e3bbdc0da5b45a0050aeb5d8", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "cf4b13af9f0846a466204afbe74b9e86", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "b43fa205beae83e030ec777d4974736b", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "aef585a4d739765b59f9a85dc27ff67f", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "559b65282ab2bfff034aa64963a7af52", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "5916245826eb4d3b68944006f82de6d9", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "6844834b7fc1030c234bee21a376a017", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "c9b29530b90bb59e8570d991072efd77", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "794d258d59fd17a61752843a9a0551ad", "src/ballistica/base/mgen/pyembed/binding_base.inc": "efa61468cf098f77cc6a234461d8b86d", - "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "97efb93f4bfd8e8b09f2db24398e29fc", + "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "f0f9dc33ecd4ef7a384f131d62c96c97", "src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3", - "src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69", + "src/ballistica/core/mgen/pyembed/binding_core.inc": "73bbc63c2a85aed75c26cfedf047fd9d", "src/ballistica/core/mgen/pyembed/env.inc": "f015d726b44d2922112fc14d9f146d8b", "src/ballistica/core/mgen/python_modules_monolithic.h": "fb967ed1c7db0c77d8deb4f00a7103c5", "src/ballistica/scene_v1/mgen/pyembed/binding_scene_v1.inc": "c25b263f2a31fb5ebe057db07d144879", diff --git a/CHANGELOG.md b/CHANGELOG.md index f3a7daa1..e3e66659 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,24 @@ -### 1.7.37 (build 22025, api 9, 2024-10-07) +### 1.7.37 (build 22040, api 9, 2024-10-19) - Bumping api version to 9. As you'll see below, there's some UI changes that will require a bit of work for any UI mods to adapt to. If your mods don't touch UI stuff at all you can simply bump your api version and call it a day. I'm hopeful that api version won't need to be bumped again for along time (if ever). +- Heavily reworked and cleaned up the logging system. There is now a 'ba' Python + logger and various logger categories under it such as 'ba.lifecycle', + 'ba.connectivity' or 'ba.v2transport'. By setting these individual loggers to + different levels such as 'debug', one can easily observe and debug specific + parts of app behavior. Over time I will better organize the logger hierarchy + and wire up more functionality to be logged this way. +- Added a 'Logging' tab to the dev-console. This allows easily setting log + levels for all existing Python loggers, as well as resetting them all to + defaults. Levels set here are restored on startup, so it is possible to debug + app startup behavior this way. Previously this sort of thing would generally + require setting cryptic environment variables which was not feasable on all + platforms, but this new system should work everywhere. +- Logs printed to both the command line and the in-app console now include + timestamps and logger names, and are color coded for severity (DEBUG=blue, + INFO=default, WARNING=orange/yellow, ERROR=red, CRITICAL=purple). - Went ahead and fully removed `efro.call.tpartial` (since we're breaking compatibility anyway by bumping api version). If you are using `efro.call.tpartial` anywhere, simply replace it with `functools.partial`. @@ -113,6 +128,8 @@ it will take you back to the co-op screen. - (build 22018) Hardened SDL joystick handling code so the app won't crash if SDL_JoystickOpen() returns nullptr for whatever reason. +- (build 22028) Fixed a longstanding issue that could cause logic thread + bg-dynamics message overflows. ### 1.7.36 (build 21944, api 8, 2024-07-26) - Wired up Tokens, BombSquad's new purchasable currency. The first thing these @@ -159,6 +176,8 @@ version and then upgrading to later builds of the same version containing incompatibilities with the older sys scripts. This should help with that problem. +- `efro.log` is now `efro.logging` which better lines up with other logging + module names. It was originally named `log` to work around a mypy bug. ### 1.7.35 (build 21889, api 8, 2024-06-20) - Fixed an issue where the engine would block at exit on some version of Linux diff --git a/config/requirements.txt b/config/requirements.txt index b8567d39..78378b79 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -2,9 +2,9 @@ cpplint==2.0.0 dmgbuild==1.6.2 filelock==3.16.1 furo==2024.8.6 -mypy==1.11.2 +mypy==1.12.0 pbxproj==4.2.1 -pdoc==14.7.0 +pdoc==15.0.0 pur==7.3.2 pylint==3.3.1 pylsp-mypy==0.6.9 @@ -13,10 +13,10 @@ python-daemon==3.0.1 python-lsp-black==2.0.0 python-lsp-server==1.12.0 requests==2.32.3 -Sphinx==8.0.2 +Sphinx==8.1.3 tomlkit==0.13.2 types-certifi==2021.10.8.3 types-filelock==3.2.7 -types-requests==2.32.0.20240914 +types-requests==2.32.0.20241016 typing_extensions==4.12.2 urllib3==2.2.3 diff --git a/src/assets/.asset_manifest_public.json b/src/assets/.asset_manifest_public.json index 4de23243..1545d690 100644 --- a/src/assets/.asset_manifest_public.json +++ b/src/assets/.asset_manifest_public.json @@ -14,6 +14,7 @@ "ba_data/python/babase/__pycache__/_assetmanager.cpython-312.opt-1.pyc", "ba_data/python/babase/__pycache__/_asyncio.cpython-312.opt-1.pyc", "ba_data/python/babase/__pycache__/_devconsole.cpython-312.opt-1.pyc", + "ba_data/python/babase/__pycache__/_devconsoletabs.cpython-312.opt-1.pyc", "ba_data/python/babase/__pycache__/_emptyappmode.cpython-312.opt-1.pyc", "ba_data/python/babase/__pycache__/_env.cpython-312.opt-1.pyc", "ba_data/python/babase/__pycache__/_error.cpython-312.opt-1.pyc", @@ -42,6 +43,7 @@ "ba_data/python/babase/_assetmanager.py", "ba_data/python/babase/_asyncio.py", "ba_data/python/babase/_devconsole.py", + "ba_data/python/babase/_devconsoletabs.py", "ba_data/python/babase/_emptyappmode.py", "ba_data/python/babase/_env.py", "ba_data/python/babase/_error.py", @@ -103,6 +105,8 @@ "ba_data/python/bacommon/__pycache__/bacloud.cpython-312.opt-1.pyc", "ba_data/python/bacommon/__pycache__/build.cpython-312.opt-1.pyc", "ba_data/python/bacommon/__pycache__/cloud.cpython-312.opt-1.pyc", + "ba_data/python/bacommon/__pycache__/loggercontrol.cpython-312.opt-1.pyc", + "ba_data/python/bacommon/__pycache__/logging.cpython-312.opt-1.pyc", "ba_data/python/bacommon/__pycache__/login.cpython-312.opt-1.pyc", "ba_data/python/bacommon/__pycache__/net.cpython-312.opt-1.pyc", "ba_data/python/bacommon/__pycache__/servermanager.cpython-312.opt-1.pyc", @@ -112,6 +116,8 @@ "ba_data/python/bacommon/bacloud.py", "ba_data/python/bacommon/build.py", "ba_data/python/bacommon/cloud.py", + "ba_data/python/bacommon/loggercontrol.py", + "ba_data/python/bacommon/logging.py", "ba_data/python/bacommon/login.py", "ba_data/python/bacommon/net.py", "ba_data/python/bacommon/servermanager.py", @@ -577,7 +583,7 @@ "ba_data/python/efro/__pycache__/cloudshell.cpython-312.opt-1.pyc", "ba_data/python/efro/__pycache__/debug.cpython-312.opt-1.pyc", "ba_data/python/efro/__pycache__/error.cpython-312.opt-1.pyc", - "ba_data/python/efro/__pycache__/log.cpython-312.opt-1.pyc", + "ba_data/python/efro/__pycache__/logging.cpython-312.opt-1.pyc", "ba_data/python/efro/__pycache__/rpc.cpython-312.opt-1.pyc", "ba_data/python/efro/__pycache__/terminal.cpython-312.opt-1.pyc", "ba_data/python/efro/__pycache__/util.cpython-312.opt-1.pyc", @@ -601,7 +607,7 @@ "ba_data/python/efro/dataclassio/extras.py", "ba_data/python/efro/debug.py", "ba_data/python/efro/error.py", - "ba_data/python/efro/log.py", + "ba_data/python/efro/logging.py", "ba_data/python/efro/message/__init__.py", "ba_data/python/efro/message/__pycache__/__init__.cpython-312.opt-1.pyc", "ba_data/python/efro/message/__pycache__/_message.cpython-312.opt-1.pyc", diff --git a/src/assets/Makefile b/src/assets/Makefile index eafb2ab8..e693a1ca 100644 --- a/src/assets/Makefile +++ b/src/assets/Makefile @@ -175,6 +175,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \ $(BUILD_DIR)/ba_data/python/babase/_assetmanager.py \ $(BUILD_DIR)/ba_data/python/babase/_asyncio.py \ $(BUILD_DIR)/ba_data/python/babase/_devconsole.py \ + $(BUILD_DIR)/ba_data/python/babase/_devconsoletabs.py \ $(BUILD_DIR)/ba_data/python/babase/_emptyappmode.py \ $(BUILD_DIR)/ba_data/python/babase/_env.py \ $(BUILD_DIR)/ba_data/python/babase/_error.py \ @@ -452,6 +453,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_assetmanager.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_asyncio.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_devconsole.cpython-312.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python/babase/__pycache__/_devconsoletabs.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_emptyappmode.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_env.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_error.cpython-312.opt-1.pyc \ @@ -732,6 +734,8 @@ SCRIPT_TARGETS_PY_PUBLIC_TOOLS = \ $(BUILD_DIR)/ba_data/python/bacommon/bacloud.py \ $(BUILD_DIR)/ba_data/python/bacommon/build.py \ $(BUILD_DIR)/ba_data/python/bacommon/cloud.py \ + $(BUILD_DIR)/ba_data/python/bacommon/loggercontrol.py \ + $(BUILD_DIR)/ba_data/python/bacommon/logging.py \ $(BUILD_DIR)/ba_data/python/bacommon/login.py \ $(BUILD_DIR)/ba_data/python/bacommon/net.py \ $(BUILD_DIR)/ba_data/python/bacommon/servermanager.py \ @@ -751,7 +755,7 @@ SCRIPT_TARGETS_PY_PUBLIC_TOOLS = \ $(BUILD_DIR)/ba_data/python/efro/dataclassio/extras.py \ $(BUILD_DIR)/ba_data/python/efro/debug.py \ $(BUILD_DIR)/ba_data/python/efro/error.py \ - $(BUILD_DIR)/ba_data/python/efro/log.py \ + $(BUILD_DIR)/ba_data/python/efro/logging.py \ $(BUILD_DIR)/ba_data/python/efro/message/__init__.py \ $(BUILD_DIR)/ba_data/python/efro/message/_message.py \ $(BUILD_DIR)/ba_data/python/efro/message/_module.py \ @@ -769,6 +773,8 @@ SCRIPT_TARGETS_PYC_PUBLIC_TOOLS = \ $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/bacloud.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/build.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/cloud.cpython-312.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/loggercontrol.cpython-312.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/logging.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/login.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/net.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/bacommon/__pycache__/servermanager.cpython-312.opt-1.pyc \ @@ -788,7 +794,7 @@ SCRIPT_TARGETS_PYC_PUBLIC_TOOLS = \ $(BUILD_DIR)/ba_data/python/efro/dataclassio/__pycache__/extras.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/efro/__pycache__/debug.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/efro/__pycache__/error.cpython-312.opt-1.pyc \ - $(BUILD_DIR)/ba_data/python/efro/__pycache__/log.cpython-312.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python/efro/__pycache__/logging.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/efro/message/__pycache__/__init__.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/efro/message/__pycache__/_message.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/efro/message/__pycache__/_module.cpython-312.opt-1.pyc \ diff --git a/src/assets/ba_data/python/babase/__init__.py b/src/assets/ba_data/python/babase/__init__.py index 2bf58b84..a32fb729 100644 --- a/src/assets/ba_data/python/babase/__init__.py +++ b/src/assets/ba_data/python/babase/__init__.py @@ -15,6 +15,7 @@ a more focused way. # dependency loops. The exception is TYPE_CHECKING blocks and # annotations since those aren't evaluated at runtime. +import logging from efro.util import set_canonical_module_names @@ -109,6 +110,7 @@ from _babase import ( supports_max_fps, supports_vsync, unlock_all_input, + update_internal_logger_levels, user_agent_string, Vec3, workspaces_in_use, @@ -185,6 +187,9 @@ from babase._plugin import PluginSpec, Plugin, PluginSubsystem from babase._stringedit import StringEditAdapter, StringEditSubsystem from babase._text import timestring +# Our standard set of loggers. +balog = logging.getLogger('ba') + _babase.app = app = App() app.postinit() @@ -215,6 +220,7 @@ __all__ = [ 'apptimer', 'AppTimer', 'asset_loads_allowed', + 'balog', 'Call', 'fullscreen_control_available', 'fullscreen_control_get', @@ -344,6 +350,7 @@ __all__ = [ 'timestring', 'UIScale', 'unlock_all_input', + 'update_internal_logger_levels', 'user_agent_string', 'utf8_all', 'Vec3', diff --git a/src/assets/ba_data/python/babase/_accountv2.py b/src/assets/ba_data/python/babase/_accountv2.py index b2ceccfe..9d313dc6 100644 --- a/src/assets/ba_data/python/babase/_accountv2.py +++ b/src/assets/ba_data/python/babase/_accountv2.py @@ -10,11 +10,12 @@ from functools import partial from typing import TYPE_CHECKING, assert_never from efro.error import CommunicationError +from efro.call import CallbackSet from bacommon.login import LoginType import _babase if TYPE_CHECKING: - from typing import Any + from typing import Any, Callable from babase._login import LoginAdapter, LoginInfo @@ -31,10 +32,12 @@ class AccountV2Subsystem: """ def __init__(self) -> None: + assert _babase.in_logic_thread() + from babase._login import LoginAdapterGPGS, LoginAdapterGameCenter - # Whether or not everything related to an initial login - # (or lack thereof) has completed. This includes things like + # Whether or not everything related to an initial sign in (or + # lack thereof) has completed. This includes things like # workspace syncing. Completion of this is what flips the app # into 'running' state. self._initial_sign_in_completed = False @@ -46,6 +49,9 @@ class AccountV2Subsystem: self._implicit_signed_in_adapter: LoginAdapter | None = None self._implicit_state_changed = False self._can_do_auto_sign_in = True + self.on_primary_account_changed_callbacks: CallbackSet[ + Callable[[AccountV2Handle | None], None] + ] = CallbackSet() adapter: LoginAdapter if _babase.using_google_play_game_services(): @@ -85,6 +91,13 @@ class AccountV2Subsystem: """ assert _babase.in_logic_thread() + # Fire any registered callbacks. + for call in self.on_primary_account_changed_callbacks.getcalls(): + try: + call(account) + except Exception: + logging.exception('Error in primary-account-changed callback.') + # Currently don't do anything special on sign-outs. if account is None: return diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py index 164dbdb6..7c520e39 100644 --- a/src/assets/ba_data/python/babase/_app.py +++ b/src/assets/ba_data/python/babase/_app.py @@ -23,6 +23,7 @@ from babase._appmodeselector import AppModeSelector from babase._appintent import AppIntentDefault, AppIntentExec from babase._stringedit import StringEditSubsystem from babase._devconsole import DevConsoleSubsystem +from babase._appconfig import AppConfig if TYPE_CHECKING: import asyncio @@ -182,6 +183,11 @@ class App: if os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') == '1': return + # Wrap our raw app config in our special wrapper and pass it to + # the native layer. + self.config = AppConfig(_babase.get_initial_app_config()) + _babase.set_app_config(self.config) + self.env: babase.Env = _babase.Env() self.state = self.State.NOT_STARTED @@ -223,7 +229,7 @@ class App: self._asyncio_loop: asyncio.AbstractEventLoop | None = None self._asyncio_tasks: set[asyncio.Task] = set() self._asyncio_timer: babase.AppTimer | None = None - self._config: babase.AppConfig | None = None + # self._config: babase.AppConfig | None = None self._pending_intent: AppIntent | None = None self._intent: AppIntent | None = None self._mode_selector: babase.AppModeSelector | None = None @@ -330,11 +336,12 @@ class App: self._asyncio_tasks.remove(task) - @property - def config(self) -> babase.AppConfig: - """The babase.AppConfig instance representing the app's config state.""" - assert self._config is not None - return self._config + # @property + # def config(self) -> babase.AppConfig: + # """The babase.AppConfig instance + # representing the app's config state.""" + # assert self._config is not None + # return self._config @property def mode_selector(self) -> babase.AppModeSelector: @@ -585,12 +592,6 @@ class App: if self._mode is not None: self._mode.on_app_active_changed() - def read_config(self) -> None: - """(internal)""" - from babase._appconfig import read_app_config - - self._config = read_app_config() - def handle_deep_link(self, url: str) -> None: """Handle a deep link URL.""" from babase._language import Lstr diff --git a/src/assets/ba_data/python/babase/_appconfig.py b/src/assets/ba_data/python/babase/_appconfig.py index 92efc73c..aca886ba 100644 --- a/src/assets/ba_data/python/babase/_appconfig.py +++ b/src/assets/ba_data/python/babase/_appconfig.py @@ -3,7 +3,6 @@ """Provides the AppConfig class.""" from __future__ import annotations -import logging from typing import TYPE_CHECKING import _babase @@ -101,43 +100,6 @@ class AppConfig(dict): self.commit() -def read_app_config() -> AppConfig: - """Read the app config.""" - import os - import json - - # NOTE: it is assumed that this only gets called once and the config - # object will not change from here on out - config_file_path = _babase.app.env.config_file_path - config_contents = '' - try: - if os.path.exists(config_file_path): - with open(config_file_path, encoding='utf-8') as infile: - config_contents = infile.read() - config = AppConfig(json.loads(config_contents)) - else: - config = AppConfig() - - except Exception: - logging.exception( - "Error reading config file '%s' at time %.3f.\n" - "Backing up broken config to'%s.broken'.", - config_file_path, - _babase.apptime(), - config_file_path, - ) - - try: - import shutil - - shutil.copyfile(config_file_path, config_file_path + '.broken') - except Exception: - logging.exception('Error copying broken config.') - config = AppConfig() - - return config - - def commit_app_config() -> None: """Commit the config to persistent storage. @@ -145,6 +107,7 @@ def commit_app_config() -> None: (internal) """ + # FIXME - this should not require plus. plus = _babase.app.plus assert plus is not None diff --git a/src/assets/ba_data/python/babase/_apputils.py b/src/assets/ba_data/python/babase/_apputils.py index e7448b24..1bd43ae2 100644 --- a/src/assets/ba_data/python/babase/_apputils.py +++ b/src/assets/ba_data/python/babase/_apputils.py @@ -11,7 +11,7 @@ from functools import partial from dataclasses import dataclass from typing import TYPE_CHECKING, override -from efro.log import LogLevel +from efro.logging import LogLevel from efro.dataclassio import ioprepped, dataclass_to_json, dataclass_from_json import _babase diff --git a/src/assets/ba_data/python/babase/_devconsole.py b/src/assets/ba_data/python/babase/_devconsole.py index d7e4eb00..92dadf2a 100644 --- a/src/assets/ba_data/python/babase/_devconsole.py +++ b/src/assets/ba_data/python/babase/_devconsole.py @@ -5,17 +5,14 @@ from __future__ import annotations import os import logging -from functools import partial from dataclasses import dataclass -from typing import TYPE_CHECKING, override +from typing import TYPE_CHECKING import _babase if TYPE_CHECKING: from typing import Callable, Any, Literal - from babase import AppMode, UIScale - class DevConsoleTab: """Defines behavior for a tab in the dev-console.""" @@ -38,6 +35,7 @@ class DevConsoleTab: label_scale: float = 1.0, corner_radius: float = 8.0, style: Literal['normal', 'light'] = 'normal', + disabled: bool = False, ) -> None: """Add a button to the tab being refreshed.""" assert _babase.app.devconsole.is_refreshing @@ -52,6 +50,7 @@ class DevConsoleTab: label_scale, corner_radius, style, + disabled, ) def text( @@ -98,151 +97,6 @@ class DevConsoleTab: return _babase.dev_console_base_scale() -class DevConsoleTabPython(DevConsoleTab): - """The Python dev-console tab.""" - - @override - def refresh(self) -> None: - self.python_terminal() - - -class DevConsoleTabAppModes(DevConsoleTab): - """Tab to switch app modes.""" - - @override - def refresh(self) -> None: - - modes = _babase.app.mode_selector.testable_app_modes() - self.text( - 'Available AppModes:', - scale=0.8, - pos=(15, 55), - h_anchor='left', - h_align='left', - v_align='none', - ) - for i, mode in enumerate(modes): - self.button( - f'{mode.__module__}.{mode.__qualname__}', - pos=(10 + i * 260, 10), - size=(250, 40), - h_anchor='left', - label_scale=0.6, - call=partial(self._set_app_mode, mode), - ) - - @staticmethod - def _set_app_mode(mode: type[AppMode]) -> None: - from babase._appintent import AppIntentDefault - - intent = AppIntentDefault() - - # Use private functionality to force a specific app-mode to - # handle this intent. Note that this should never be done - # outside of this explicit testing case. It is the app's job to - # determine which app-mode should be used to handle a given - # intent. - setattr(intent, '_force_app_mode_handler', mode) - - _babase.app.set_intent(intent) - - -class DevConsoleTabUI(DevConsoleTab): - """Tab to debug/test UI stuff.""" - - @override - def refresh(self) -> None: - from babase._mgen.enums import UIScale - - self.text( - 'Make sure all interactive UI fits in the' - ' virtual bounds at all UI-scales (not counting things' - ' that follow screen edges).\n' - 'Note that some elements may not reflect UI-scale changes' - ' until recreated.', - scale=0.6, - pos=(15, 70), - h_anchor='left', - h_align='left', - v_align='center', - ) - - ui_overlay = _babase.get_draw_ui_bounds() - self.button( - 'Virtual Bounds ON' if ui_overlay else 'Virtual Bounds OFF', - pos=(10, 10), - size=(200, 30), - h_anchor='left', - label_scale=0.6, - call=self.toggle_ui_overlay, - style='light' if ui_overlay else 'normal', - ) - x = 300 - self.text( - 'UI-Scale', - pos=(x - 5, 15), - h_anchor='left', - h_align='right', - v_align='none', - scale=0.6, - ) - - bwidth = 100 - for scale in UIScale: - self.button( - scale.name.capitalize(), - pos=(x, 10), - size=(bwidth, 30), - h_anchor='left', - label_scale=0.6, - call=partial(_babase.app.set_ui_scale, scale), - style=( - 'light' - if scale.name.lower() == _babase.get_ui_scale() - else 'normal' - ), - ) - x += bwidth + 2 - - def toggle_ui_overlay(self) -> None: - """Toggle UI overlay drawing.""" - _babase.set_draw_ui_bounds(not _babase.get_draw_ui_bounds()) - self.request_refresh() - - -class DevConsoleTabTest(DevConsoleTab): - """Test dev-console tab.""" - - @override - def refresh(self) -> None: - import random - - self.button( - f'FLOOP-{random.randrange(200)}', - pos=(10, 10), - size=(100, 30), - h_anchor='left', - label_scale=0.6, - call=self.request_refresh, - ) - self.button( - f'FLOOP2-{random.randrange(200)}', - pos=(120, 10), - size=(100, 30), - h_anchor='left', - label_scale=0.6, - style='light', - ) - self.text( - 'TestText', - scale=0.8, - pos=(15, 50), - h_anchor='left', - h_align='left', - v_align='none', - ) - - @dataclass class DevConsoleTabEntry: """Represents a distinct tab in the dev-console.""" @@ -263,28 +117,50 @@ class DevConsoleSubsystem: """ def __init__(self) -> None: + # pylint: disable=cyclic-import + from babase._devconsoletabs import ( + DevConsoleTabPython, + DevConsoleTabAppModes, + DevConsoleTabUI, + DevConsoleTabLogging, + DevConsoleTabTest, + ) + # All tabs in the dev-console. Add your own stuff here via # plugins or whatnot. self.tabs: list[DevConsoleTabEntry] = [ DevConsoleTabEntry('Python', DevConsoleTabPython), DevConsoleTabEntry('AppModes', DevConsoleTabAppModes), DevConsoleTabEntry('UI', DevConsoleTabUI), + DevConsoleTabEntry('Logging', DevConsoleTabLogging), ] if os.environ.get('BA_DEV_CONSOLE_TEST_TAB', '0') == '1': self.tabs.append(DevConsoleTabEntry('Test', DevConsoleTabTest)) self.is_refreshing = False + self._tab_instances: dict[str, DevConsoleTab] = {} def do_refresh_tab(self, tabname: str) -> None: """Called by the C++ layer when a tab should be filled out.""" assert _babase.in_logic_thread() - # FIXME: We currently won't handle multiple tabs with the same - # name. We should give a clean error or something in that case. - tab: DevConsoleTab | None = None - for tabentry in self.tabs: - if tabentry.name == tabname: - tab = tabentry.factory() - break + # Make noise if we have repeating tab names, as that breaks our + # logic. + if __debug__: + alltabnames = set[str](tabentry.name for tabentry in self.tabs) + if len(alltabnames) != len(self.tabs): + logging.error( + 'Duplicate dev-console tab names found;' + ' tabs may behave unpredictably.' + ) + + tab: DevConsoleTab | None = self._tab_instances.get(tabname) + + # If we haven't instantiated this tab yet, do so. + if tab is None: + for tabentry in self.tabs: + if tabentry.name == tabname: + tab = self._tab_instances[tabname] = tabentry.factory() + break if tab is None: logging.error( diff --git a/src/assets/ba_data/python/babase/_devconsoletabs.py b/src/assets/ba_data/python/babase/_devconsoletabs.py new file mode 100644 index 00000000..bffab22c --- /dev/null +++ b/src/assets/ba_data/python/babase/_devconsoletabs.py @@ -0,0 +1,610 @@ +# Released under the MIT License. See LICENSE for details. +# +"""Predefined tabs for the dev console.""" +from __future__ import annotations + +import math +import random +import logging +from functools import partial +from typing import TYPE_CHECKING, override, TypeVar, Generic + +import _babase + +from babase._devconsole import DevConsoleTab + +if TYPE_CHECKING: + from typing import Callable, Literal + + from bacommon.loggercontrol import LoggerControlConfig + from babase import AppMode + +T = TypeVar('T') + + +class DevConsoleTabPython(DevConsoleTab): + """The Python dev-console tab.""" + + @override + def refresh(self) -> None: + self.python_terminal() + + +class DevConsoleTabAppModes(DevConsoleTab): + """Tab to switch app modes.""" + + @override + def refresh(self) -> None: + + bwidth = 260 + bpadding = 5 + modes = _babase.app.mode_selector.testable_app_modes() + xoffs = -0.5 * bwidth * len(modes) + + self.text( + 'Available AppModes:', + scale=0.8, + pos=(0, 75), + h_align='center', + v_align='center', + ) + # pylint: disable=protected-access + for i, mode in enumerate(modes): + self.button( + f'{mode.__module__}.{mode.__qualname__}', + pos=(xoffs + i * bwidth + bpadding, 10), + size=(bwidth - 2.0 * bpadding, 40), + label_scale=0.6, + call=partial(self._set_app_mode, mode), + style=( + 'light' if isinstance(_babase.app._mode, mode) else 'normal' + ), + ) + + def _set_app_mode(self, mode: type[AppMode]) -> None: + from babase._appintent import AppIntentDefault + + intent = AppIntentDefault() + + # Use private functionality to force a specific app-mode to + # handle this intent. Note that this should never be done + # outside of this explicit testing case. It is the app's job to + # determine which app-mode should be used to handle a given + # intent. + setattr(intent, '_force_app_mode_handler', mode) + + _babase.app.set_intent(intent) + + # Slight hackish: need to wait a moment before refreshing to + # pick up the new mode, as mode switches are asynchronous. + _babase.apptimer(0.1, self.request_refresh) + + +class DevConsoleTabUI(DevConsoleTab): + """Tab to debug/test UI stuff.""" + + @override + def refresh(self) -> None: + from babase._mgen.enums import UIScale + + xoffs = -375 + + self.text( + 'Make sure all interactive UI fits in the' + ' virtual bounds at all UI-scales (not counting things' + ' that follow screen edges).\n' + 'Note that some elements may not reflect UI-scale changes' + ' until recreated.', + scale=0.6, + pos=(xoffs + 15, 70), + # h_anchor='left', + h_align='left', + v_align='center', + ) + + ui_overlay = _babase.get_draw_ui_bounds() + self.button( + 'Virtual Bounds ON' if ui_overlay else 'Virtual Bounds OFF', + pos=(xoffs + 10, 10), + size=(200, 30), + # h_anchor='left', + label_scale=0.6, + call=self.toggle_ui_overlay, + style='light' if ui_overlay else 'normal', + ) + x = 300 + self.text( + 'UI-Scale', + pos=(xoffs + x - 5, 15), + # h_anchor='left', + h_align='right', + v_align='none', + scale=0.6, + ) + + bwidth = 100 + for scale in UIScale: + self.button( + scale.name.capitalize(), + pos=(xoffs + x, 10), + size=(bwidth, 30), + # h_anchor='left', + label_scale=0.6, + call=partial(_babase.app.set_ui_scale, scale), + style=( + 'light' + if scale.name.lower() == _babase.get_ui_scale() + else 'normal' + ), + ) + x += bwidth + 2 + + def toggle_ui_overlay(self) -> None: + """Toggle UI overlay drawing.""" + _babase.set_draw_ui_bounds(not _babase.get_draw_ui_bounds()) + self.request_refresh() + + +class Table(Generic[T]): + """Used to show controls for arbitrarily large data in a grid form.""" + + def __init__( + self, + title: str, + entries: list[T], + draw_entry_call: Callable[ + [int, T, DevConsoleTab, float, float, float, float], None + ], + *, + entry_width: float = 300.0, + entry_height: float = 40.0, + margin_left_right: float = 60.0, + debug_bounds: bool = False, + max_columns: int | None = None, + ) -> None: + self._title = title + self._entry_width = entry_width + self._entry_height = entry_height + self._margin_left_right = margin_left_right + self._focus_entry_index = 0 + self._entries_per_page = 1 + self._debug_bounds = debug_bounds + self._entries = entries + self._draw_entry_call = draw_entry_call + self._max_columns = max_columns + + # Values updated on refresh (for aligning other custom + # widgets/etc.) + self.top_left: tuple[float, float] = (0.0, 0.0) + self.top_right: tuple[float, float] = (0.0, 0.0) + + def set_entries(self, entries: list[T]) -> None: + """Update table entries.""" + self._entries = entries + + # Clamp focus to new entries. + self._focus_entry_index = max( + 0, min(len(self._entries) - 1, self._focus_entry_index) + ) + + def set_focus_entry_index(self, index: int) -> None: + """Explicitly set the focused entry. + + This affects which page is shown at the next refresh. + """ + self._focus_entry_index = max(0, min(len(self._entries) - 1, index)) + + def refresh(self, tab: DevConsoleTab) -> None: + """Call to refresh the data.""" + # pylint: disable=too-many-locals + + margin_top = 50.0 + margin_bottom = 10.0 + + # Update how much we can fit on a page based on our current size. + max_entry_area_width = tab.width - (self._margin_left_right * 2.0) + max_entry_area_height = tab.height - (margin_top + margin_bottom) + columns = max(1, int(max_entry_area_width / self._entry_width)) + if self._max_columns is not None: + columns = min(columns, self._max_columns) + rows = max(1, int(max_entry_area_height / self._entry_height)) + self._entries_per_page = rows * columns + + # See which page our focus index falls in. + pagemax = math.ceil(len(self._entries) / self._entries_per_page) + + page = self._focus_entry_index // self._entries_per_page + entry_offset = page * self._entries_per_page + + entries_on_this_page = min( + self._entries_per_page, len(self._entries) - entry_offset + ) + columns_on_this_page = math.ceil(entries_on_this_page / rows) + rows_on_this_page = min(entries_on_this_page, rows) + + # We attach things to the center so resizes are smooth but we do + # some math in a left-centric way. + center_to_left = tab.width * -0.5 + + # Center our columns. + xoffs = 0.5 * ( + max_entry_area_width - columns_on_this_page * self._entry_width + ) + + # Align everything to the bottom of the dev-console. + yoffs = -1.0 * ( + tab.height + - ( + rows_on_this_page * self._entry_height + + margin_top + + margin_bottom + ) + ) + + # Keep our corners up to date for user use. + self.top_left = (center_to_left + xoffs, tab.height + yoffs) + self.top_right = ( + self.top_left[0] + + self._margin_left_right * 2.0 + + columns_on_this_page * self._entry_width, + self.top_left[1], + ) + + # Page left/right buttons. + tab.button( + '<', + pos=(center_to_left + xoffs, margin_bottom), + size=( + self._margin_left_right, + rows_on_this_page * self._entry_height, + ), + # h_anchor='left', + call=partial(self._page_left, tab), + disabled=entry_offset == 0, + ) + tab.button( + '>', + pos=( + center_to_left + + xoffs + + self._margin_left_right + + columns_on_this_page * self._entry_width, + margin_bottom, + ), + size=( + self._margin_left_right, + rows_on_this_page * self._entry_height, + ), + # h_anchor='left', + call=partial(self._page_right, tab), + disabled=( + entry_offset + entries_on_this_page >= len(self._entries) + ), + ) + + for column in range(columns): + for row in range(rows): + entry_index = entry_offset + column * rows + row + if entry_index >= len(self._entries): + break + + xpos = ( + xoffs + self._margin_left_right + self._entry_width * column + ) + ypos = ( + yoffs + + tab.height + - margin_top + - self._entry_height * (row + 1.0) + ) + # Draw debug bounds. + if self._debug_bounds: + tab.button( + str(entry_index), + pos=( + center_to_left + xpos, + ypos, + ), + size=(self._entry_width, self._entry_height), + # h_anchor='left', + ) + # Run user drawing. + self._draw_entry_call( + entry_index, + self._entries[entry_index], + tab, + center_to_left + xpos, + ypos, + self._entry_width, + self._entry_height, + ) + + if entry_index >= len(self._entries): + break + + tab.text( + f'{self._title} ({page + 1}/{pagemax})', + scale=0.8, + pos=(0, yoffs + tab.height - margin_top * 0.5), + h_align='center', + v_align='center', + ) + + def _page_right(self, tab: DevConsoleTab) -> None: + # Set focus on the first entry in the page before the current. + page = self._focus_entry_index // self._entries_per_page + page += 1 + self.set_focus_entry_index(page * self._entries_per_page) + tab.request_refresh() + + def _page_left(self, tab: DevConsoleTab) -> None: + # Set focus on the first entry in the page after the current. + page = self._focus_entry_index // self._entries_per_page + page -= 1 + self.set_focus_entry_index(page * self._entries_per_page) + tab.request_refresh() + + +class DevConsoleTabLogging(DevConsoleTab): + """Tab to wrangle logging levels.""" + + def __init__(self) -> None: + + self._table = Table( + title='Logging Levels', + entry_width=800, + debug_bounds=False, + entries=list[str](), + draw_entry_call=self._draw_entry, + max_columns=1, + ) + + @override + def refresh(self) -> None: + assert self._table is not None + + # Update table entries with the latest set of loggers (this can + # change over time). + self._table.set_entries( + ['root'] + sorted(logging.root.manager.loggerDict) + ) + + # Draw the table. + self._table.refresh(self) + + # Draw our control buttons in the corners. + tl = self._table.top_left + tr = self._table.top_right + bwidth = 140.0 + bheight = 30.0 + bvpad = 10.0 + self.button( + 'Reset', + pos=(tl[0], tl[1] - bheight - bvpad), + size=(bwidth, bheight), + label_scale=0.6, + call=self._reset, + disabled=( + not self._get_reset_logger_control_config().would_make_changes() + ), + ) + self.button( + 'Cloud Control OFF', + pos=(tr[0] - bwidth, tl[1] - bheight - bvpad), + size=(bwidth, bheight), + label_scale=0.6, + disabled=True, + ) + + def _get_reset_logger_control_config(self) -> LoggerControlConfig: + from bacommon.logging import get_base_logger_control_config_client + + return get_base_logger_control_config_client() + + def _reset(self) -> None: + + self._get_reset_logger_control_config().apply() + + # Let the native layer know that levels changed. + _babase.update_internal_logger_levels() + + # Blow away any existing values in app-config. + appconfig = _babase.app.config + if 'Log Levels' in appconfig: + del appconfig['Log Levels'] + appconfig.commit() + + self.request_refresh() + + def _set_entry_val(self, entry_index: int, entry: str, val: int) -> None: + + from bacommon.logging import get_base_logger_control_config_client + from bacommon.loggercontrol import LoggerControlConfig + + # Focus on this entry with any interaction, so if we get resized + # it'll still be visible. + self._table.set_focus_entry_index(entry_index) + + logging.getLogger(entry).setLevel(val) + + # Let the native layer know that levels changed. + _babase.update_internal_logger_levels() + + # Store only changes compared to the base config. + baseconfig = get_base_logger_control_config_client() + config = LoggerControlConfig.from_current_loggers().diff(baseconfig) + + appconfig = _babase.app.config + appconfig['Log Levels'] = config.levels + appconfig.commit() + + self.request_refresh() + + def _draw_entry( + self, + entry_index: int, + entry: str, + tab: DevConsoleTab, + x: float, + y: float, + width: float, + height: float, + ) -> None: + # pylint: disable=too-many-positional-arguments + # pylint: disable=too-many-locals + + xoffs = -15.0 + bwidth = 80.0 + btextscale = 0.5 + tab.text( + entry, + ( + x + width - bwidth * 6.5 - 10.0 + xoffs, + y + height * 0.5, + ), + h_align='right', + scale=0.7, + ) + + logger = logging.getLogger(entry) + level = logger.level + index = 0 + if entry != 'root' and level == logging.NOTSET: + # Show the level being inherited in NOTSET cases. + notsetlevelname = logging.getLevelName(logger.getEffectiveLevel()) + if notsetlevelname == 'NOTSET': + notsetname = 'Not Set' + else: + notsetname = f'Not Set ({notsetlevelname.capitalize()})' + else: + notsetname = 'Not Set' + tab.button( + notsetname, + pos=(x + width - bwidth * 6.5 + xoffs + 1.0, y + 5.0), + size=(bwidth * 1.5 - 2.0, height - 10), + label_scale=btextscale, + style='light' if level == logging.NOTSET else 'normal', + call=partial( + self._set_entry_val, entry_index, entry, logging.NOTSET + ), + ) + index += 1 + tab.button( + 'Debug', + pos=(x + width - bwidth * 5 + xoffs + 1.0, y + 5.0), + size=(bwidth - 2.0, height - 10), + label_scale=btextscale, + style='light' if level == logging.DEBUG else 'normal', + call=partial( + self._set_entry_val, entry_index, entry, logging.DEBUG + ), + ) + index += 1 + tab.button( + 'Info', + pos=(x + width - bwidth * 4 + xoffs + 1.0, y + 5.0), + size=(bwidth - 2.0, height - 10), + label_scale=btextscale, + style='light' if level == logging.INFO else 'normal', + call=partial(self._set_entry_val, entry_index, entry, logging.INFO), + ) + index += 1 + tab.button( + 'Warning', + pos=(x + width - bwidth * 3 + xoffs + 1.0, y + 5.0), + size=(bwidth - 2.0, height - 10), + label_scale=btextscale, + style='light' if level == logging.WARNING else 'normal', + call=partial( + self._set_entry_val, entry_index, entry, logging.WARNING + ), + ) + index += 1 + tab.button( + 'Error', + pos=(x + width - bwidth * 2 + xoffs + 1.0, y + 5.0), + size=(bwidth - 2.0, height - 10), + label_scale=btextscale, + style='light' if level == logging.ERROR else 'normal', + call=partial( + self._set_entry_val, entry_index, entry, logging.ERROR + ), + ) + index += 1 + tab.button( + 'Critical', + pos=(x + width - bwidth * 1 + xoffs + 1.0, y + 5.0), + size=(bwidth - 2.0, height - 10), + label_scale=btextscale, + style='light' if level == logging.CRITICAL else 'normal', + call=partial( + self._set_entry_val, entry_index, entry, logging.CRITICAL + ), + ) + + +class DevConsoleTabTest(DevConsoleTab): + """Test dev-console tab.""" + + @override + def refresh(self) -> None: + + self.button( + f'FLOOP-{random.randrange(200)}', + pos=(10, 10), + size=(100, 30), + h_anchor='left', + label_scale=0.6, + call=self.request_refresh, + ) + self.button( + f'FLOOP2-{random.randrange(200)}', + pos=(120, 10), + size=(100, 30), + h_anchor='left', + label_scale=0.6, + style='light', + ) + self.text( + 'TestText', + scale=0.8, + pos=(15, 50), + h_anchor='left', + h_align='left', + v_align='none', + ) + + # Throw little bits of text in the corners to make sure + # widths/heights are correct. + self.text( + 'BL', + scale=0.25, + pos=(0, 0), + h_anchor='left', + h_align='left', + v_align='bottom', + ) + self.text( + 'BR', + scale=0.25, + pos=(self.width, 0), + h_anchor='left', + h_align='right', + v_align='bottom', + ) + self.text( + 'TL', + scale=0.25, + pos=(0, self.height), + h_anchor='left', + h_align='left', + v_align='top', + ) + self.text( + 'TR', + scale=0.25, + pos=(self.width, self.height), + h_anchor='left', + h_align='right', + v_align='top', + ) diff --git a/src/assets/ba_data/python/babase/_env.py b/src/assets/ba_data/python/babase/_env.py index 5efc2530..41b12347 100644 --- a/src/assets/ba_data/python/babase/_env.py +++ b/src/assets/ba_data/python/babase/_env.py @@ -9,11 +9,11 @@ import logging import warnings from typing import TYPE_CHECKING, override -from efro.log import LogLevel +from efro.logging import LogLevel if TYPE_CHECKING: from typing import Any - from efro.log import LogEntry, LogHandler + from efro.logging import LogEntry, LogHandler _g_babase_imported = False # pylint: disable=invalid-name _g_babase_app_started = False # pylint: disable=invalid-name @@ -186,7 +186,10 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None: # Forward this along to the engine to display in the in-app # console, in the Android log, etc. _babase.emit_log( - name=entry.name, level=entry.level.name, message=entry.message + name=entry.name, + level=entry.level.name, + timestamp=entry.time.timestamp(), + message=entry.message, ) # We also want to feed some logs to the old v1-cloud-log system. diff --git a/src/assets/ba_data/python/baclassic/_appmode.py b/src/assets/ba_data/python/baclassic/_appmode.py index e97ef8b0..42cdb70f 100644 --- a/src/assets/ba_data/python/baclassic/_appmode.py +++ b/src/assets/ba_data/python/baclassic/_appmode.py @@ -14,6 +14,7 @@ from babase import ( AppIntentExec, AppIntentDefault, invoke_main_menu, + in_logic_thread, screenmessage, ) @@ -22,13 +23,19 @@ import _baclassic if TYPE_CHECKING: from typing import Callable - from babase import AppIntent + from efro.call import CallbackRegistration + from babase import AppIntent, AccountV2Handle from bauiv1 import UIV1AppSubsystem, MainWindow, MainWindowState class ClassicAppMode(AppMode): """AppMode for the classic BombSquad experience.""" + def __init__(self) -> None: + self._on_primary_account_changed_callback: ( + CallbackRegistration | None + ) = None + @override @classmethod def get_app_experience(cls) -> AppExperience: @@ -54,6 +61,8 @@ class ClassicAppMode(AppMode): # Let the native layer do its thing. _baclassic.classic_app_mode_activate() + assert app.plus is not None + # Wire up the root ui to do what we want. ui = app.ui_v1 ui.root_ui_calls[ui.RootUIElement.ACCOUNT_BUTTON] = ( @@ -108,9 +117,24 @@ class ClassicAppMode(AppMode): self._root_ui_chest_slot_pressed, 4 ) + # We want to be informed when primary account changes. + self._on_primary_account_changed_callback = ( + app.plus.accounts.on_primary_account_changed_callbacks.add( + self.update_for_primary_account + ) + ) + # Establish subscriptions/etc. for any current primary account. + self.update_for_primary_account(app.plus.accounts.primary) + @override def on_deactivate(self) -> None: + # Stop being informed of account changes. + self._on_primary_account_changed_callback = None + + # Remove any listeners for any current primary account. + self.update_for_primary_account(None) + # Save where we were in the UI so we return there next time. if app.classic is not None: app.classic.save_ui_state() @@ -125,6 +149,14 @@ class ClassicAppMode(AppMode): if not app.active: invoke_main_menu() + def update_for_primary_account( + self, account: AccountV2Handle | None + ) -> None: + """Update subscriptions/etc. for a new primary account state.""" + assert in_logic_thread() + del account # Unused. + # print('WOULD WIRE UP LISTENERS FOR ACCOUNT', account) + def _root_ui_menu_press(self) -> None: from babase import push_back_press diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index 7b5e0163..32ba280f 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -18,6 +18,7 @@ from __future__ import annotations import os import sys +import time import logging from pathlib import Path from dataclasses import dataclass @@ -27,7 +28,7 @@ import __main__ if TYPE_CHECKING: from typing import Any - from efro.log import LogHandler + from efro.logging import LogHandler # IMPORTANT - It is likely (and in some cases expected) that this # module's code will be exec'ed multiple times. This is because it is @@ -52,7 +53,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 22025 +TARGET_BALLISTICA_BUILD = 22040 TARGET_BALLISTICA_VERSION = '1.7.37' @@ -87,14 +88,13 @@ class EnvConfig: # stderr into the engine so they show up on in-app consoles, etc. log_handler: LogHandler | None - # Initial data from the ballisticakit-config.json file. This is - # passed mostly as an optimization to avoid reading the same config - # file twice, since config data is first needed in baenv and next in - # the engine. It will be cleared after passing it to the app's - # config management subsystem and should not be accessed by any - # other code. + # Initial data from the config.json file in the config dir. The + # config file is parsed by initial_app_config: Any + # Timestamp when we first started doing stuff. + launch_time: float + @dataclass class _EnvGlobals: @@ -172,6 +172,11 @@ def configure( are imported; the environment is locked in as soon as that happens. """ + # Measure when we start doing this stuff. We plug this in to show + # relative times in our log timestamp displays and also pass this to + # the engine to do the same there. + launch_time = time.time() + envglobals = _EnvGlobals.get() # Keep track of whether we've been *called*, not whether a config @@ -206,11 +211,19 @@ def configure( config_dir, ) - # The second thing we do is set up our logging system and pipe - # Python's stdout/stderr into it. At this point we can at least - # debug problems on systems where native stdout/stderr is not easily - # accessible such as Android. - log_handler = _setup_logging() if setup_logging else None + # Set up our log-handler and pipe Python's stdout/stderr into it. + # Later, once the engine comes up, the handler will feed its logs + # (including cached history) to the os-specific output location. + # This means anything printed or logged at this point forward should + # be visible on all platforms. + log_handler = _setup_logging(launch_time) if setup_logging else None + + # Load the raw app-config dict. + app_config = _read_app_config(os.path.join(config_dir, 'config.json')) + + # Set logging levels to stored values or defaults. + if setup_logging: + _set_log_levels(app_config) # We want to always be run in UTF-8 mode; complain if we're not. if sys.flags.utf8_mode != 1: @@ -235,10 +248,46 @@ def configure( site_python_dir=site_python_dir, log_handler=log_handler, is_user_app_python_dir=is_user_app_python_dir, - initial_app_config=None, + initial_app_config=app_config, + launch_time=launch_time, ) +def _read_app_config(config_file_path: str) -> dict: + """Read the app config.""" + import json + + config: dict | Any + config_contents = '' + try: + if os.path.exists(config_file_path): + with open(config_file_path, encoding='utf-8') as infile: + config_contents = infile.read() + config = json.loads(config_contents) + if not isinstance(config, dict): + raise RuntimeError('Got non-dict for config root.') + else: + config = {} + + except Exception: + logging.exception( + "Error reading config file '%s'.\n" + "Backing up broken config to'%s.broken'.", + config_file_path, + config_file_path, + ) + + try: + import shutil + + shutil.copyfile(config_file_path, config_file_path + '.broken') + except Exception: + logging.exception('Error copying broken config.') + config = {} + + return config + + def _calc_data_dir(data_dir: str | None) -> str: if data_dir is None: # To calc default data_dir, we assume this module was imported @@ -262,23 +311,57 @@ def _calc_data_dir(data_dir: str | None) -> str: return data_dir -def _setup_logging() -> LogHandler: - from efro.log import setup_logging, LogLevel +def _setup_logging(launch_time: float) -> LogHandler: + from efro.logging import setup_logging, LogLevel - # TODO: should set this up with individual loggers under a top level - # 'ba' logger, and at that point we can kill off the - # suppress_non_root_debug option since we'll only ever need to set - # 'ba' to DEBUG at most. log_handler = setup_logging( log_path=None, level=LogLevel.DEBUG, - suppress_non_root_debug=True, log_stdout_stderr=True, cache_size_limit=1024 * 1024, + launch_time=launch_time, ) return log_handler +def _set_log_levels(app_config: dict) -> None: + + from bacommon.logging import get_base_logger_control_config_client + from bacommon.loggercontrol import LoggerControlConfig + + try: + config = app_config.get('Log Levels', None) + + if config is None: + get_base_logger_control_config_client().apply() + return + + # Make sure data is expected types/values. + valid_levels = { + logging.NOTSET, + logging.DEBUG, + logging.INFO, + logging.WARNING, + logging.ERROR, + logging.CRITICAL, + } + for logname, loglevel in config.items(): + if ( + not isinstance(logname, str) + or not logname + or not isinstance(loglevel, int) + or not loglevel in valid_levels + ): + raise ValueError("Invalid 'Log Levels' data read from config.") + + get_base_logger_control_config_client().apply_diff( + LoggerControlConfig(levels=config) + ).apply() + + except Exception: + logging.exception('Error setting log levels.') + + def _setup_certs(contains_python_dist: bool) -> None: # In situations where we're bringing our own Python, let's also # provide our own root certs so ssl works. We can consider diff --git a/src/assets/ba_data/python/baplus/_cloud.py b/src/assets/ba_data/python/baplus/_cloud.py index 0fcc0ae8..ca446705 100644 --- a/src/assets/ba_data/python/baplus/_cloud.py +++ b/src/assets/ba_data/python/baplus/_cloud.py @@ -128,14 +128,8 @@ class CloudSubsystem(babase.AppSubsystem): The provided on_response call will be run in the logic thread and passed either the response or the error that occurred. """ - - del msg # Unused. - - babase.pushcall( - babase.Call( - on_response, - RuntimeError('Cloud functionality is not available.'), - ) + raise NotImplementedError( + 'Cloud functionality is not present in this build.' ) @overload @@ -158,7 +152,9 @@ class CloudSubsystem(babase.AppSubsystem): Must be called from a background thread. """ - raise RuntimeError('Cloud functionality is not available.') + raise NotImplementedError( + 'Cloud functionality is not present in this build.' + ) @overload async def send_message_async( @@ -175,7 +171,14 @@ class CloudSubsystem(babase.AppSubsystem): Must be called from the logic thread. """ - raise RuntimeError('Cloud functionality is not available.') + raise NotImplementedError( + 'Cloud functionality is not present in this build.' + ) + + # def subscribe( + # self, + # on_response: Callable[[Any], None], + # ) -> CallbackRegistration: def cloud_console_exec(code: str) -> None: diff --git a/src/assets/ba_data/python/bascenev1lib/activity/coopscore.py b/src/assets/ba_data/python/bascenev1lib/activity/coopscore.py index 4373d66c..98110045 100644 --- a/src/assets/ba_data/python/bascenev1lib/activity/coopscore.py +++ b/src/assets/ba_data/python/bascenev1lib/activity/coopscore.py @@ -1575,6 +1575,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]): transition_delay=1.0, ).autoretain() else: + assert rating is not None ZoomText( ( f'{rating:.1f}' diff --git a/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py b/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py index 8bc1141c..8c0ad202 100644 --- a/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py +++ b/src/assets/ba_data/python/bascenev1lib/activity/multiteamvictory.py @@ -387,8 +387,6 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity): v_offs = 0.0 tdelay += len(player_entries) * 8 * t_incr for _score, name, prec in player_entries: - if not prec.player.in_game: - continue tdelay -= 4 * t_incr v_offs -= 40 Text( diff --git a/src/assets/ba_data/python/bauiv1lib/store/browser.py b/src/assets/ba_data/python/bauiv1lib/store/browser.py index 22c2909a..27a2f3d7 100644 --- a/src/assets/ba_data/python/bauiv1lib/store/browser.py +++ b/src/assets/ba_data/python/bauiv1lib/store/browser.py @@ -627,6 +627,7 @@ class StoreBrowserWindow(bui.MainWindow): sale_title_text: str | bui.Lstr = '' sale_time_text: str | bui.Lstr = '' + call: Callable | None if purchased: title_color = (0.8, 0.7, 0.9, 1.0) color = (0.63, 0.55, 0.78) diff --git a/src/ballistica/base/app_adapter/app_adapter.cc b/src/ballistica/base/app_adapter/app_adapter.cc index ba06f953..f0ce0d5b 100644 --- a/src/ballistica/base/app_adapter/app_adapter.cc +++ b/src/ballistica/base/app_adapter/app_adapter.cc @@ -136,7 +136,7 @@ auto AppAdapter::DoClipboardGetText() -> std::string { } auto AppAdapter::GetKeyName(int keycode) -> std::string { - BA_LOG_ONCE(LogLevel::kWarning, + BA_LOG_ONCE(LogName::kBa, LogLevel::kWarning, "CorePlatform::GetKeyName not implemented here."); return "?"; } diff --git a/src/ballistica/base/app_adapter/app_adapter_apple.cc b/src/ballistica/base/app_adapter/app_adapter_apple.cc index 894a7a55..2fd6beee 100644 --- a/src/ballistica/base/app_adapter/app_adapter_apple.cc +++ b/src/ballistica/base/app_adapter/app_adapter_apple.cc @@ -169,7 +169,7 @@ auto AppAdapterApple::InGraphicsContext() -> bool { void AppAdapterApple::DoPushGraphicsContextRunnable(Runnable* runnable) { auto lock = std::scoped_lock(graphics_calls_mutex_); if (graphics_calls_.size() > 1000) { - BA_LOG_ONCE(LogLevel::kError, "graphics_calls_ got too big."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "graphics_calls_ got too big."); } graphics_calls_.push_back(runnable); } diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.cc b/src/ballistica/base/app_adapter/app_adapter_sdl.cc index 956b5815..9ca734da 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.cc +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.cc @@ -61,7 +61,7 @@ void AppAdapterSDL::OnMainThreadStartApp() { uint32_t sdl_flags{SDL_INIT_VIDEO | SDL_INIT_JOYSTICK}; if (strict_graphics_context_) { - Log(LogLevel::kWarning, + Log(LogName::kBaNetworking, LogLevel::kWarning, "AppAdapterSDL strict_graphics_context_ is enabled." " Remember to turn this off."); } @@ -309,7 +309,8 @@ void AppAdapterSDL::SleepUntilNextEventCycle_(microsecs_t cycle_start_time) { const microsecs_t min_sleep{2000}; if (now + min_sleep >= target_time) { if (debug_log_sdl_frame_timing_) { - Log(LogLevel::kDebug, "no sleep."); // 'till brooklyn! + Log(LogName::kBaNetworking, LogLevel::kDebug, + "no sleep."); // 'till brooklyn! } } else { if (debug_log_sdl_frame_timing_) { @@ -317,7 +318,7 @@ void AppAdapterSDL::SleepUntilNextEventCycle_(microsecs_t cycle_start_time) { snprintf(buf, sizeof(buf), "render %.1f sleep %.1f", (now - cycle_start_time) / 1000.0f, (target_time - now) / 1000.0f); - Log(LogLevel::kDebug, buf); + Log(LogName::kBaNetworking, LogLevel::kDebug, buf); } g_core->platform->SleepMicrosecs(target_time - now); } @@ -375,8 +376,9 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) { g_base->input->PushJoystickEvent(event, js); } } else { - Log(LogLevel::kError, "Unable to get SDL Joystick for event type " - + std::to_string(event.type)); + Log(LogName::kBaInput, LogLevel::kError, + "Unable to get SDL Joystick for event type " + + std::to_string(event.type)); } break; } @@ -563,7 +565,7 @@ void AppAdapterSDL::OnSDLJoystickAdded_(int device_index) { try { j = Object::NewDeferred(device_index); } catch (const std::exception& exc) { - Log(LogLevel::kError, + Log(LogName::kBaInput, LogLevel::kError, std::string("Error creating JoystickInput for SDL device-index " + std::to_string(device_index) + ": ") + exc.what()); @@ -602,7 +604,7 @@ void AppAdapterSDL::RemoveSDLInputDevice_(int index) { // Note: am running into this with a PS5 controller on macOS Sequoia beta. if (!j) { - Log(LogLevel::kError, + Log(LogName::kBaInput, LogLevel::kError, "GetSDLJoystickInput_() returned nullptr on RemoveSDLInputDevice_();" " joysticks size is " + std::to_string(sdl_joysticks_.size()) + "; index is " @@ -614,9 +616,10 @@ void AppAdapterSDL::RemoveSDLInputDevice_(int index) { if (static_cast_check_fit(sdl_joysticks_.size()) > index) { sdl_joysticks_[index] = nullptr; } else { - Log(LogLevel::kError, "Invalid index on RemoveSDLInputDevice: size is " - + std::to_string(sdl_joysticks_.size()) - + "; index is " + std::to_string(index) + "."); + Log(LogName::kBaInput, LogLevel::kError, + "Invalid index on RemoveSDLInputDevice: size is " + + std::to_string(sdl_joysticks_.size()) + "; index is " + + std::to_string(index) + "."); } g_base->input->PushRemoveInputDeviceCall(j, true); } @@ -785,7 +788,8 @@ void AppAdapterSDL::DoPushGraphicsContextRunnable(Runnable* runnable) { if (strict_graphics_context_) { auto lock = std::scoped_lock(strict_graphics_calls_mutex_); if (strict_graphics_calls_.size() > 1000) { - BA_LOG_ONCE(LogLevel::kError, "strict_graphics_calls_ got too big."); + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kError, + "strict_graphics_calls_ got too big."); } strict_graphics_calls_.push_back(runnable); } else { diff --git a/src/ballistica/base/app_adapter/app_adapter_vr.cc b/src/ballistica/base/app_adapter/app_adapter_vr.cc index 703dbca2..571177ad 100644 --- a/src/ballistica/base/app_adapter/app_adapter_vr.cc +++ b/src/ballistica/base/app_adapter/app_adapter_vr.cc @@ -52,7 +52,8 @@ void AppAdapterVR::VRPreDraw() { assert(g_base->app_adapter->InGraphicsContext()); // FIXME - this is internal graphics-server details that the render-server // should handle. - Log(LogLevel::kWarning, "FIXME: Have GraphicsServer handle VR drawing."); + Log(LogName::kBaGraphics, LogLevel::kWarning, + "FIXME: Have GraphicsServer handle VR drawing."); // if (FrameDef* frame_def = g_base->graphics_server->GetRenderFrameDef()) { // // Note: this could be part of PreprocessRenderFrameDef but the non-vr // // path needs it to be separate since preprocess doesn't happen @@ -75,7 +76,8 @@ void AppAdapterVR::VRPostDraw() { g_base->graphics_server->FinishRenderFrameDef(vr_render_frame_def_); vr_render_frame_def_ = nullptr; } - Log(LogLevel::kWarning, "WOULD RUN RENDER UPKEEP CYCLE"); + Log(LogName::kBaGraphics, LogLevel::kWarning, + "WOULD RUN RENDER UPKEEP CYCLE"); // RunRenderUpkeepCycle(); } diff --git a/src/ballistica/base/assets/assets.cc b/src/ballistica/base/assets/assets.cc index f513dcf8..262feda6 100644 --- a/src/ballistica/base/assets/assets.cc +++ b/src/ballistica/base/assets/assets.cc @@ -302,7 +302,7 @@ void Assets::PrintLoadInfo() { snprintf(buffer, sizeof(buffer), " %-50s %10s %10s", "FILE", "PRELOAD_TIME", "LOAD_TIME"); s += buffer; - Log(LogLevel::kInfo, s); + Log(LogName::kBaAssets, LogLevel::kInfo, s); millisecs_t total_preload_time = 0; millisecs_t total_load_time = 0; assert(asset_lists_locked_); @@ -315,7 +315,7 @@ void Assets::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(LogLevel::kInfo, buffer); + Log(LogName::kBaAssets, LogLevel::kInfo, buffer); num++; } assert(asset_lists_locked_); @@ -328,7 +328,7 @@ void Assets::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(LogLevel::kInfo, buffer); + Log(LogName::kBaAssets, LogLevel::kInfo, buffer); num++; } assert(asset_lists_locked_); @@ -341,7 +341,7 @@ void Assets::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(LogLevel::kInfo, buffer); + Log(LogName::kBaAssets, LogLevel::kInfo, buffer); num++; } assert(asset_lists_locked_); @@ -354,7 +354,7 @@ void Assets::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(LogLevel::kInfo, buffer); + Log(LogName::kBaAssets, LogLevel::kInfo, buffer); num++; } assert(asset_lists_locked_); @@ -367,7 +367,7 @@ void Assets::PrintLoadInfo() { i.second->file_name_full().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(LogLevel::kInfo, buffer); + Log(LogName::kBaAssets, LogLevel::kInfo, buffer); num++; } snprintf(buffer, sizeof(buffer), @@ -375,7 +375,7 @@ void Assets::PrintLoadInfo() { "(feeding data to OpenGL, etc): %i", static_cast(total_preload_time), static_cast(total_load_time)); - Log(LogLevel::kInfo, buffer); + Log(LogName::kBaAssets, LogLevel::kInfo, buffer); } void Assets::MarkAllAssetsForLoad() { @@ -1029,35 +1029,35 @@ void Assets::Prune(int level) { if (kShowPruningInfo) { assert(asset_lists_locked_); if (textures_.size() != old_texture_count) { - Log(LogLevel::kInfo, "Textures pruned from " - + std::to_string(old_texture_count) + " to " - + std::to_string(textures_.size())); + Log(LogName::kBaAssets, LogLevel::kInfo, + "Textures pruned from " + std::to_string(old_texture_count) + " to " + + std::to_string(textures_.size())); } if (text_textures_.size() != old_text_texture_count) { - Log(LogLevel::kInfo, "TextTextures pruned from " - + std::to_string(old_text_texture_count) + " to " - + std::to_string(text_textures_.size())); + Log(LogName::kBaAssets, LogLevel::kInfo, + "TextTextures pruned from " + std::to_string(old_text_texture_count) + + " to " + std::to_string(text_textures_.size())); } if (qr_textures_.size() != old_qr_texture_count) { - Log(LogLevel::kInfo, "QrTextures pruned from " - + std::to_string(old_qr_texture_count) + " to " - + std::to_string(qr_textures_.size())); + Log(LogName::kBaAssets, LogLevel::kInfo, + "QrTextures pruned from " + std::to_string(old_qr_texture_count) + + " to " + std::to_string(qr_textures_.size())); } if (meshes_.size() != old_mesh_count) { - Log(LogLevel::kInfo, "Meshes pruned from " - + std::to_string(old_mesh_count) + " to " - + std::to_string(meshes_.size())); + Log(LogName::kBaAssets, LogLevel::kInfo, + "Meshes pruned from " + std::to_string(old_mesh_count) + " to " + + std::to_string(meshes_.size())); } if (collision_meshes_.size() != old_collision_mesh_count) { - Log(LogLevel::kInfo, "CollisionMeshes pruned from " - + std::to_string(old_collision_mesh_count) - + " to " - + std::to_string(collision_meshes_.size())); + Log(LogName::kBaAssets, LogLevel::kInfo, + "CollisionMeshes pruned from " + + std::to_string(old_collision_mesh_count) + " to " + + std::to_string(collision_meshes_.size())); } if (sounds_.size() != old_sound_count) { - Log(LogLevel::kInfo, "Sounds pruned from " - + std::to_string(old_sound_count) + " to " - + std::to_string(sounds_.size())); + Log(LogName::kBaAssets, LogLevel::kInfo, + "Sounds pruned from " + std::to_string(old_sound_count) + " to " + + std::to_string(sounds_.size())); } } } @@ -1164,11 +1164,11 @@ auto Assets::FindAssetFile(FileType type, const std::string& name) // We wanna fail gracefully for some types. if (type == FileType::kSound && name != "blank") { - Log(LogLevel::kError, + Log(LogName::kBaAssets, LogLevel::kError, "Unable to load audio: '" + name + "'; trying fallback..."); return FindAssetFile(type, "blank"); } else if (type == FileType::kTexture && name != "white") { - Log(LogLevel::kError, + Log(LogName::kBaAssets, LogLevel::kError, "Unable to load texture: '" + name + "'; trying fallback..."); return FindAssetFile(type, "white"); } @@ -1228,7 +1228,8 @@ void Assets::AddPackage(const std::string& name, const std::string& path) { assert(g_base->InLogicThread()); if (g_buildconfig.debug_build()) { if (packages_.find(name) != packages_.end()) { - Log(LogLevel::kWarning, "adding duplicate package: '" + name + "'"); + Log(LogName::kBaAssets, LogLevel::kWarning, + "adding duplicate package: '" + name + "'"); } } packages_[name] = path; @@ -1386,7 +1387,7 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { printed = true; char* c = cJSON_Print(obj); BA_LOG_ONCE( - LogLevel::kError, + LogName::kBaAssets, LogLevel::kError, "found long key 'resource' in raw lstr json: " + std::string(c)); free(c); } @@ -1406,7 +1407,7 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { printed = true; char* c = cJSON_Print(obj); BA_LOG_ONCE( - LogLevel::kError, + LogName::kBaAssets, LogLevel::kError, "found long key 'fallback' in raw lstr json: " + std::string(c)); free(c); } @@ -1431,7 +1432,7 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { printed = true; char* c = cJSON_Print(obj); BA_LOG_ONCE( - LogLevel::kError, + LogName::kBaAssets, LogLevel::kError, "found long key 'translate' in raw lstr json: " + std::string(c)); free(c); } @@ -1470,7 +1471,7 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { printed = true; char* c = cJSON_Print(obj); BA_LOG_ONCE( - LogLevel::kError, + LogName::kBaAssets, LogLevel::kError, "found long key 'value' in raw lstr json: " + std::string(c)); free(c); } @@ -1501,8 +1502,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { if (!printed) { printed = true; char* c = cJSON_Print(obj); - BA_LOG_ONCE(LogLevel::kError, "found long key 'subs' in raw lstr json: " - + std::string(c)); + BA_LOG_ONCE( + LogName::kBaAssets, LogLevel::kError, + "found long key 'subs' in raw lstr json: " + std::string(c)); free(c); } } @@ -1571,8 +1573,9 @@ auto Assets::CompileResourceString(const std::string& s, const std::string& loc, cJSON* root = cJSON_Parse(s.c_str()); if (root == nullptr) { - Log(LogLevel::kError, "CompileResourceString failed (loc " + loc - + "); invalid json: '" + s + "'"); + Log(LogName::kBaAssets, LogLevel::kError, + "CompileResourceString failed (loc " + loc + "); invalid json: '" + s + + "'"); *valid = false; return ""; } @@ -1581,8 +1584,9 @@ auto Assets::CompileResourceString(const std::string& s, const std::string& loc, result = DoCompileResourceString(root); *valid = true; } catch (const std::exception& e) { - Log(LogLevel::kError, "CompileResourceString failed (loc " + loc + "): " - + std::string(e.what()) + "; str='" + s + "'"); + Log(LogName::kBaAssets, LogLevel::kError, + "CompileResourceString failed (loc " + loc + + "): " + std::string(e.what()) + "; str='" + s + "'"); result = ""; *valid = false; } diff --git a/src/ballistica/base/assets/assets_server.cc b/src/ballistica/base/assets/assets_server.cc index c24eda65..146cdca7 100644 --- a/src/ballistica/base/assets/assets_server.cc +++ b/src/ballistica/base/assets/assets_server.cc @@ -56,7 +56,7 @@ void AssetsServer::PushBeginWriteReplayCall(uint16_t protocol_version) { // We only allow writing one replay at once; make sure that's actually // the case. if (writing_replay_) { - Log(LogLevel::kError, + Log(LogName::kBaAssets, LogLevel::kError, "AssetsServer got BeginWriteReplayCall while already writing"); WriteReplayMessages(); if (replay_out_file_) { @@ -76,7 +76,7 @@ void AssetsServer::PushBeginWriteReplayCall(uint16_t protocol_version) { replay_bytes_written_ = 0; if (!replay_out_file_) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "unable to open output-stream file: '" + file_path + "'"); } else { // Write file id and protocol-version. @@ -88,8 +88,9 @@ void AssetsServer::PushBeginWriteReplayCall(uint16_t protocol_version) { || (fwrite(&version, sizeof(version), 1, replay_out_file_) != 1)) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log(LogLevel::kError, "error writing replay file header: " - + g_core->platform->GetErrnoString()); + Log(LogName::kBa, LogLevel::kError, + "error writing replay file header: " + + g_core->platform->GetErrnoString()); } replay_bytes_written_ = 5; } @@ -109,7 +110,7 @@ void AssetsServer::PushAddMessageToReplayCall( // Sanity check. if (!writing_replay_) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "AssetsServer got AddMessageToReplayCall while not writing replay"); replays_broken_ = true; return; @@ -120,7 +121,7 @@ void AssetsServer::PushAddMessageToReplayCall( // If we've got too much data built up (lets go with 10 megs for now), // abort. if (replay_message_bytes_ > 10000000) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "replay output buffer exceeded 10 megs; aborting replay"); fclose(replay_out_file_); replay_out_file_ = nullptr; @@ -142,7 +143,8 @@ void AssetsServer::PushEndWriteReplayCall() { // Sanity check. if (!writing_replay_) { - Log(LogLevel::kError, "_finishWritingReplay called while not writing"); + Log(LogName::kBa, LogLevel::kError, + "_finishWritingReplay called while not writing"); replays_broken_ = true; return; } @@ -179,8 +181,9 @@ void AssetsServer::WriteReplayMessages() { if (fwrite(&len8, 1, 1, replay_out_file_) != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log(LogLevel::kError, "error writing replay file: " - + g_core->platform->GetErrnoString()); + Log(LogName::kBaAudio, LogLevel::kError, + "error writing replay file: " + + g_core->platform->GetErrnoString()); return; } } @@ -191,16 +194,18 @@ void AssetsServer::WriteReplayMessages() { if (fwrite(&len16, 2, 1, replay_out_file_) != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log(LogLevel::kError, "error writing replay file: " - + g_core->platform->GetErrnoString()); + Log(LogName::kBaAudio, LogLevel::kError, + "error writing replay file: " + + g_core->platform->GetErrnoString()); return; } } else { if (fwrite(&len32, 4, 1, replay_out_file_) != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log(LogLevel::kError, "error writing replay file: " - + g_core->platform->GetErrnoString()); + Log(LogName::kBaAudio, LogLevel::kError, + "error writing replay file: " + + g_core->platform->GetErrnoString()); return; } } @@ -211,7 +216,7 @@ void AssetsServer::WriteReplayMessages() { if (result != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "error writing replay file: " + g_core->platform->GetErrnoString()); return; } diff --git a/src/ballistica/base/assets/sound_asset.cc b/src/ballistica/base/assets/sound_asset.cc index 84841660..2b908420 100644 --- a/src/ballistica/base/assets/sound_asset.cc +++ b/src/ballistica/base/assets/sound_asset.cc @@ -62,8 +62,9 @@ static auto LoadOgg(const char* file_name, std::vector* buffer, f = g_core->platform->FOpen(file_name, "rb"); if (f == nullptr) { fallback = true; - Log(LogLevel::kError, std::string("Can't open sound file '") + file_name - + "' for reading..."); + Log(LogName::kBaAudio, LogLevel::kError, + std::string("Can't open sound file '") + file_name + + "' for reading..."); // Attempt a fallback standin; if that doesn't work, throw in the towel. file_name = "data/global/audio/blank.ogg"; @@ -83,7 +84,7 @@ static auto LoadOgg(const char* file_name, std::vector* buffer, // Try opening the given file if (ov_open_callbacks(f, &ogg_file, nullptr, 0, callbacks) != 0) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, std::string("Error decoding sound file '") + file_name + "'"); fclose(f); @@ -207,9 +208,9 @@ static void LoadCachedOgg(const char* file_name, std::vector* buffer, // with invalid formats of 0 once. Report and ignore if we see // something like that. if (*format != AL_FORMAT_MONO16 && *format != AL_FORMAT_STEREO16) { - Log(LogLevel::kError, std::string("Ignoring invalid audio cache of ") - + file_name + " with format " - + std::to_string(*format)); + Log(LogName::kBaAudio, LogLevel::kError, + std::string("Ignoring invalid audio cache of ") + file_name + + " with format " + std::to_string(*format)); } else { return; // SUCCESS!!!! } diff --git a/src/ballistica/base/assets/texture_asset.cc b/src/ballistica/base/assets/texture_asset.cc index a7fabefc..9802069f 100644 --- a/src/ballistica/base/assets/texture_asset.cc +++ b/src/ballistica/base/assets/texture_asset.cc @@ -82,7 +82,7 @@ TextureAsset::TextureAsset(const std::string& qr_url) : is_qr_code_(true) { "QR code url byte length %zu exceeds soft-limit of %zu;" " please use shorter urls. (url=%s)", qr_url.size(), soft_limit, qr_url.c_str()); - Log(LogLevel::kWarning, buffer); + Log(LogName::kBaAssets, LogLevel::kWarning, buffer); } file_name_ = qr_url; valid_ = true; diff --git a/src/ballistica/base/audio/al_sys.cc b/src/ballistica/base/audio/al_sys.cc index 579b4c90..d1aa4045 100644 --- a/src/ballistica/base/audio/al_sys.cc +++ b/src/ballistica/base/audio/al_sys.cc @@ -18,14 +18,15 @@ namespace ballistica::base { void _check_al_error(const char* file, int line) { if (g_base->audio_server->paused()) { - Log(LogLevel::kError, Utils::BaseName(file) + ":" + std::to_string(line) - + ": Checking OpenAL error while paused."); + Log(LogName::kBaAudio, LogLevel::kError, + Utils::BaseName(file) + ":" + std::to_string(line) + + ": Checking OpenAL error while paused."); } ALenum al_err = alGetError(); if (al_err != AL_NO_ERROR) { - Log(LogLevel::kError, Utils::BaseName(file) + ":" + std::to_string(line) - + ": OpenAL Error: " + GetALErrorString(al_err) - + ";"); + Log(LogName::kBaAudio, LogLevel::kError, + Utils::BaseName(file) + ":" + std::to_string(line) + + ": OpenAL Error: " + GetALErrorString(al_err) + ";"); } } diff --git a/src/ballistica/base/audio/audio.cc b/src/ballistica/base/audio/audio.cc index f37f4632..7c8f7b4d 100644 --- a/src/ballistica/base/audio/audio.cc +++ b/src/ballistica/base/audio/audio.cc @@ -166,19 +166,19 @@ auto Audio::SafePlaySysSound(SysSoundID sound_id) -> std::optional { return {}; } if (!g_base->InLogicThread()) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Audio::SafePlaySysSound called from non-logic thread. id=" + std::to_string(static_cast(sound_id))); return {}; } if (!g_base->assets->sys_assets_loaded()) { - Log(LogLevel::kWarning, + Log(LogName::kBaAudio, LogLevel::kWarning, "Audio::SafePlaySysSound called before sys assets loaded. id=" + std::to_string(static_cast(sound_id))); return {}; } if (!g_base->assets->IsValidSysSound(sound_id)) { - Log(LogLevel::kWarning, + Log(LogName::kBaAudio, LogLevel::kWarning, "Audio::SafePlaySysSound called with invalid sound_id. id=" + std::to_string(static_cast(sound_id))); return {}; diff --git a/src/ballistica/base/audio/audio_server.cc b/src/ballistica/base/audio/audio_server.cc index 23f771b3..e07fb02c 100644 --- a/src/ballistica/base/audio/audio_server.cc +++ b/src/ballistica/base/audio/audio_server.cc @@ -179,8 +179,9 @@ static void ALEventCallback_(ALenum eventType, ALuint object, ALuint param, [] { g_base->audio_server->OnDeviceDisconnected(); }); } } else { - Log(LogLevel::kWarning, "Got unexpected OpenAL callback event " - + std::to_string(static_cast(eventType))); + Log(LogName::kBaAudio, LogLevel::kWarning, + "Got unexpected OpenAL callback event " + + std::to_string(static_cast(eventType))); } } @@ -244,7 +245,7 @@ void AudioServer::OnAppStartInThread_() { if (g_core->platform->GetOSVersionString().compare(0, prefix2.size(), prefix2) == 0) { - Log(LogLevel::kInfo, + Log(LogName::kBaAudio, LogLevel::kInfo, "Xiaomi Android 11 detected; using OpenSL instead of AAudio."); g_core->platform->SetEnv("BA_OBOE_USE_OPENSLES", "1"); } @@ -260,7 +261,8 @@ void AudioServer::OnAppStartInThread_() { ALboolean enumeration = alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"); if (enumeration == AL_FALSE) { - Log(LogLevel::kError, "OpenAL enumeration extensions missing."); + Log(LogName::kBaAudio, LogLevel::kError, + "OpenAL enumeration extensions missing."); } else { const ALCchar* devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); @@ -298,7 +300,7 @@ void AudioServer::OnAppStartInThread_() { if (!device) { if (g_buildconfig.ostype_android()) { std::scoped_lock lock(openalsoft_android_log_mutex_); - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "------------------------" " OPENALSOFT-FATAL-ERROR-LOG-BEGIN ----------------------\n" + openalsoft_android_log_ @@ -315,11 +317,11 @@ void AudioServer::OnAppStartInThread_() { // Android special case: if we fail, try again after a few seconds. if (!impl_->alc_context && g_buildconfig.ostype_android()) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Failed creating AL context; waiting and trying again."); { std::scoped_lock lock(openalsoft_android_log_mutex_); - Log(LogLevel::kWarning, + Log(LogName::kBaAudio, LogLevel::kWarning, "------------------------" " OPENALSOFT-ERROR-LOG-BEGIN ----------------------\n" + openalsoft_android_log_ @@ -334,7 +336,7 @@ void AudioServer::OnAppStartInThread_() { if (!device) { std::scoped_lock lock(openalsoft_android_log_mutex_); - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "------------------------" " OPENALSOFT-FATAL-ERROR-LOG-BEGIN ----------------------\n" + openalsoft_android_log_ @@ -346,17 +348,18 @@ void AudioServer::OnAppStartInThread_() { impl_->alc_context = alcCreateContext(device, nullptr); if (impl_->alc_context) { // For now want to explicitly know if this works. - Log(LogLevel::kWarning, "Backup AL context creation successful!"); + Log(LogName::kBaAudio, LogLevel::kWarning, + "Backup AL context creation successful!"); } } // Android special case: if we fail, try OpenSL back-end. if (!impl_->alc_context && g_buildconfig.ostype_android()) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Failed second time creating AL context; trying OpenSL backend."); { std::scoped_lock lock(openalsoft_android_log_mutex_); - Log(LogLevel::kWarning, + Log(LogName::kBaAudio, LogLevel::kWarning, "------------------------" " OPENALSOFT-ERROR-LOG-BEGIN ----------------------\n" + openalsoft_android_log_ @@ -370,7 +373,7 @@ void AudioServer::OnAppStartInThread_() { alGetError(); // Clear any errors. if (!device) { std::scoped_lock lock(openalsoft_android_log_mutex_); - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "------------------------" " OPENALSOFT-FATAL-ERROR-LOG-BEGIN ----------------------\n" + openalsoft_android_log_ @@ -382,7 +385,8 @@ void AudioServer::OnAppStartInThread_() { impl_->alc_context = alcCreateContext(device, nullptr); if (impl_->alc_context) { // For now want to explicitly know if this works. - Log(LogLevel::kWarning, "Backup AL context creation 2 successful!"); + Log(LogName::kBaAudio, LogLevel::kWarning, + "Backup AL context creation 2 successful!"); } } @@ -390,7 +394,7 @@ void AudioServer::OnAppStartInThread_() { if (!impl_->alc_context) { if (g_buildconfig.ostype_android()) { std::scoped_lock lock(openalsoft_android_log_mutex_); - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "------------------------" " OPENALSOFT-FATAL-ERROR-LOG-BEGIN ----------------------\n" + openalsoft_android_log_ @@ -459,8 +463,9 @@ void AudioServer::OnAppStartInThread_() { sound_source_refs_.push_back(s); sources_.push_back(&(*s)); } else { - Log(LogLevel::kError, "Made " + std::to_string(i) + " sources; (wanted " - + std::to_string(target_source_count) + ")."); + Log(LogName::kBaAudio, LogLevel::kError, + "Made " + std::to_string(i) + " sources; (wanted " + + std::to_string(target_source_count) + ")."); break; } } @@ -498,20 +503,23 @@ void AudioServer::CompleteShutdown_() { #if BA_ENABLE_AUDIO ALCboolean check = alcMakeContextCurrent(nullptr); if (!check) { - Log(LogLevel::kWarning, "Error on alcMakeContextCurrent at shutdown."); + Log(LogName::kBaAudio, LogLevel::kWarning, + "Error on alcMakeContextCurrent at shutdown."); } auto* device = alcGetContextsDevice(impl_->alc_context); if (!device) { - Log(LogLevel::kWarning, "Unable to get ALCdevice at shutdown."); + Log(LogName::kBaAudio, LogLevel::kWarning, + "Unable to get ALCdevice at shutdown."); } else { alcDestroyContext(impl_->alc_context); ALenum err = alcGetError(device); if (err != ALC_NO_ERROR) { - Log(LogLevel::kWarning, "Error on AL shutdown."); + Log(LogName::kBaAudio, LogLevel::kWarning, "Error on AL shutdown."); } check = alcCloseDevice(device); if (!check) { - Log(LogLevel::kWarning, "Error on alcCloseDevice at shutdown."); + Log(LogName::kBaAudio, LogLevel::kWarning, + "Error on alcCloseDevice at shutdown."); } } #endif @@ -534,7 +542,7 @@ struct AudioServer::SoundFadeNode_ { void AudioServer::SetSuspended_(bool suspend) { if (!suspended_) { if (!suspend) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Got audio unsuspend request when already unsuspended."); } else { #if BA_OSTYPE_IOS_TVOS @@ -557,7 +565,7 @@ void AudioServer::SetSuspended_(bool suspend) { + std::to_string(g_core->GetAppTimeSeconds())); alcDevicePauseSOFT(device); } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Error in alcDevicePauseSOFT at time " + std::to_string(g_core->GetAppTimeSeconds()) + "( playing since " @@ -565,7 +573,8 @@ void AudioServer::SetSuspended_(bool suspend) { + "): " + g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " " + e.what()); } catch (...) { - Log(LogLevel::kError, "Unknown error in alcDevicePauseSOFT"); + Log(LogName::kBaAudio, LogLevel::kError, + "Unknown error in alcDevicePauseSOFT"); } #endif @@ -574,7 +583,7 @@ void AudioServer::SetSuspended_(bool suspend) { } else { // Unsuspend if requested. if (suspend) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Got audio suspend request when already suspended."); } else { #if BA_OSTYPE_IOS_TVOS @@ -599,13 +608,14 @@ void AudioServer::SetSuspended_(bool suspend) { + std::to_string(g_core->GetAppTimeSeconds())); alcDeviceResumeSOFT(device); } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Error in alcDeviceResumeSOFT at time " + std::to_string(g_core->GetAppTimeSeconds()) + ": " + g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " " + e.what()); } catch (...) { - Log(LogLevel::kError, "Unknown error in alcDeviceResumeSOFT"); + Log(LogName::kBaAudio, LogLevel::kError, + "Unknown error in alcDeviceResumeSOFT"); } #endif last_started_playing_time_ = g_core->GetAppTimeSeconds(); @@ -777,7 +787,7 @@ void AudioServer::UpdateAvailableSources_() { // that probably means somebody's grabbing a source but never // resubmitting it. if (t - i->client_source()->last_lock_time() > 10000) { - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Client audio source has been locked for too long; " "probably leaked. (debug id " + std::to_string(i->client_source()->lock_debug_id()) + ")"); @@ -926,7 +936,8 @@ void AudioServer::ProcessDeviceDisconnects_(seconds_t real_time_seconds) { if (connected == 0 && real_time_seconds - last_reset_attempt_time_ >= retry_interval) { - Log(LogLevel::kInfo, "OpenAL device disconnected; resetting..."); + Log(LogName::kBaAudio, LogLevel::kInfo, + "OpenAL device disconnected; resetting..."); if (g_buildconfig.ostype_android()) { std::scoped_lock lock(openalsoft_android_log_mutex_); openalsoft_android_log_ += @@ -995,7 +1006,7 @@ void AudioServer::ProcessDeviceDisconnects_(seconds_t real_time_seconds) { shipped_reconnect_logs_ = true; if (g_buildconfig.ostype_android()) { std::scoped_lock lock(openalsoft_android_log_mutex_); - Log(LogLevel::kWarning, + Log(LogName::kBaAudio, LogLevel::kWarning, "Have been disconnected for a while; dumping OpenAL log.\n" "------------------------" " OPENALSOFT-RECONNECT-LOG-BEGIN ----------------------\n" @@ -1153,8 +1164,9 @@ AudioServer::ThreadSource_::ThreadSource_(AudioServer* audio_server_in, ALenum err = alGetError(); valid_ = (err == AL_NO_ERROR); if (!valid_) { - Log(LogLevel::kError, std::string("AL Error ") + GetALErrorString(err) - + " on source creation."); + Log(LogName::kBaAudio, LogLevel::kError, + std::string("AL Error ") + GetALErrorString(err) + + " on source creation."); } else { // In vr mode we keep the microphone a bit closer to the camera // for realism purposes, so we need stuff louder in general. @@ -1347,7 +1359,7 @@ void AudioServer::ThreadSource_::SetPosition(float x, float y, float z) { z = 500; } if (oob) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaAudio, LogLevel::kError, "AudioServer::ThreadSource::SetPosition" " got out-of-bounds value."); } diff --git a/src/ballistica/base/audio/audio_source.cc b/src/ballistica/base/audio/audio_source.cc index 07d0bbdf..633e53b8 100644 --- a/src/ballistica/base/audio/audio_source.cc +++ b/src/ballistica/base/audio/audio_source.cc @@ -42,7 +42,8 @@ void AudioSource::SetPosition(float x, float y, float z) { assert(client_queue_size_ > 0); #if BA_DEBUG_BUILD if (std::isnan(x) || std::isnan(y) || std::isnan(z)) { - Log(LogLevel::kError, "Got nan value in AudioSource::SetPosition."); + Log(LogName::kBaAudio, LogLevel::kError, + "Got nan value in AudioSource::SetPosition."); } #endif g_base->audio_server->PushSourceSetPositionCall(play_id_, Vector3f(x, y, z)); diff --git a/src/ballistica/base/audio/audio_streamer.cc b/src/ballistica/base/audio/audio_streamer.cc index 3867558d..71bf2629 100644 --- a/src/ballistica/base/audio/audio_streamer.cc +++ b/src/ballistica/base/audio/audio_streamer.cc @@ -89,9 +89,9 @@ void AudioStreamer::Update() { // A fun anomaly in the linux version; we sometimes get more // "processed" buffers than we have queued. if (queued < processed) { - Log(LogLevel::kError, "Streamer oddness: queued(" + std::to_string(queued) - + "); processed(" + std::to_string(processed) - + ")"); + Log(LogName::kBaAudio, LogLevel::kError, + "Streamer oddness: queued(" + std::to_string(queued) + "); processed(" + + std::to_string(processed) + ")"); processed = queued; } diff --git a/src/ballistica/base/audio/ogg_stream.cc b/src/ballistica/base/audio/ogg_stream.cc index 486780d5..14e27146 100644 --- a/src/ballistica/base/audio/ogg_stream.cc +++ b/src/ballistica/base/audio/ogg_stream.cc @@ -93,7 +93,7 @@ void OggStream::DoStream(char* pcm, int* size, unsigned int* rate) { static bool reported_error = false; if (!reported_error) { reported_error = true; - Log(LogLevel::kError, + Log(LogName::kBaAudio, LogLevel::kError, "Error streaming ogg file: '" + file_name() + "'."); } if (loops()) { diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc index 3d153d65..cf9cbc56 100644 --- a/src/ballistica/base/base.cc +++ b/src/ballistica/base/base.cc @@ -31,10 +31,10 @@ #include "ballistica/base/support/plus_soft.h" #include "ballistica/base/support/stdio_console.h" #include "ballistica/base/ui/ui_delegate.h" -#include "ballistica/core/python/core_python.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/foundation/logging.h" #include "ballistica/shared/generic/utils.h" +#include "ballistica/shared/math/vector4f.h" #include "ballistica/shared/python/python_command.h" namespace ballistica::base { @@ -125,11 +125,6 @@ void BaseFeatureSet::OnModuleExec(PyObject* module) { FatalError("babase._env.on_native_module_import() call failed."); } - // ..and because Python is now feeding us logs, we can push any logs - // through that we've been holding on to and start forwarding log calls as - // they happen. - g_core->python->EnablePythonLoggingCalls(); - // A marker we pop down at the very end so other modules can run sanity // checks to make sure we aren't importing them reciprocally when they // import us. @@ -160,7 +155,7 @@ void BaseFeatureSet::SuccessScreenMessage() { python->objs().Get(BasePython::ObjID::kSuccessMessageCall).Call(); }); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SuccessScreenMessage called without logic event_loop in place."); } } @@ -171,7 +166,7 @@ void BaseFeatureSet::ErrorScreenMessage() { python->objs().Get(BasePython::ObjID::kErrorMessageCall).Call(); }); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "ErrorScreenMessage called without logic event_loop in place."); } } @@ -186,7 +181,8 @@ auto BaseFeatureSet::GetV2AccountID() -> std::optional { } return result.ValueAsString(); } else { - Log(LogLevel::kError, "GetV2AccountID() py call errored."); + Log(LogName::kBaAccount, LogLevel::kError, + "GetV2AccountID() py call errored."); return {}; } } @@ -219,7 +215,7 @@ void BaseFeatureSet::StartApp() { // Read in ba.app.config for anyone who wants to start looking at it // (though we don't explicitly ask anyone to apply it until later). - python->ReadConfig(); + // python->ReadConfig(); // Allow our subsystems to start doing work in their own threads and // communicating with other subsystems. Note that we may still want to run @@ -262,7 +258,7 @@ void BaseFeatureSet::StartApp() { char buffer[128]; snprintf(buffer, sizeof(buffer), "StartApp() took too long (%.2lf seconds).", duration); - Log(LogLevel::kWarning, buffer); + Log(LogName::kBa, LogLevel::kWarning, buffer); } } @@ -271,7 +267,7 @@ void BaseFeatureSet::SuspendApp() { assert(g_core->InMainThread()); if (app_suspended_) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "AppAdapter::SuspendApp() called with app already suspended."); return; } @@ -313,7 +309,7 @@ void BaseFeatureSet::SuspendApp() { // running_loop_count = loops.size(); if (running_loops.empty()) { if (g_buildconfig.debug_build()) { - Log(LogLevel::kDebug, + Log(LogName::kBa, LogLevel::kDebug, "SuspendApp() completed in " + std::to_string(core::CorePlatform::GetCurrentMillisecs() - start_time) @@ -374,7 +370,7 @@ void BaseFeatureSet::SuspendApp() { } msg += ")."; - Log(LogLevel::kError, msg); + Log(LogName::kBa, LogLevel::kError, msg); } void BaseFeatureSet::UnsuspendApp() { @@ -382,7 +378,7 @@ void BaseFeatureSet::UnsuspendApp() { assert(g_core->InMainThread()); if (!app_suspended_) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "AppAdapter::UnsuspendApp() called with app not in suspendedstate."); return; } @@ -400,7 +396,7 @@ void BaseFeatureSet::UnsuspendApp() { g_base->networking->OnAppUnsuspend(); if (g_buildconfig.debug_build()) { - Log(LogLevel::kDebug, + Log(LogName::kBa, LogLevel::kDebug, "UnsuspendApp() completed in " + std::to_string(core::CorePlatform::GetCurrentMillisecs() - start_time) @@ -431,7 +427,7 @@ void BaseFeatureSet::LogVersionInfo_() { snprintf(buffer, sizeof(buffer), "BallisticaKit %s build %d.", kEngineVersion, kEngineBuildNumber); } - Log(LogLevel::kInfo, buffer); + Log(LogName::kBa, LogLevel::kInfo, buffer); } void BaseFeatureSet::set_app_mode(AppMode* mode) { @@ -440,7 +436,7 @@ void BaseFeatureSet::set_app_mode(AppMode* mode) { // Redundant sets should not happen (make an exception here for empty mode // since that's in place before any app mode is officially set). if (mode == app_mode_ && mode != EmptyAppMode::GetSingleton()) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "set_app_mode called with already-current app-mode; unexpected."); } @@ -568,7 +564,8 @@ auto BaseFeatureSet::GetAppInstanceUUID() -> const std::string& { if (!have_app_instance_uuid) { // As an emergency fallback simply use a single random number. We // should probably simply disallow this before Python is up. - Log(LogLevel::kWarning, "GetSessionUUID() using rand fallback."); + Log(LogName::kBa, LogLevel::kWarning, + "GetSessionUUID() using rand fallback."); srand(static_cast( core::CorePlatform::GetCurrentMillisecs())); // NOLINT app_instance_uuid = @@ -576,7 +573,8 @@ auto BaseFeatureSet::GetAppInstanceUUID() -> const std::string& { have_app_instance_uuid = true; } if (app_instance_uuid.size() >= 100) { - Log(LogLevel::kWarning, "session id longer than it should be."); + Log(LogName::kBa, LogLevel::kWarning, + "session id longer than it should be."); } } return app_instance_uuid; @@ -668,6 +666,7 @@ void BaseFeatureSet::DoV1CloudLog(const std::string& msg) { static bool warned = false; if (!warned) { warned = true; + printf("MSG %s\n", msg.c_str()); printf( "WARNING: V1CloudLog called before babase fully imported; " "ignoring.\n"); @@ -725,8 +724,9 @@ void BaseFeatureSet::DoV1CloudLog(const std::string& msg) { plus()->DirectSendV1CloudLogs(logprefix, logsuffix, false, nullptr); } -void BaseFeatureSet::PushDevConsolePrintCall(const std::string& msg) { - ui->PushDevConsolePrintCall(msg); +void BaseFeatureSet::PushDevConsolePrintCall(const std::string& msg, + float scale, Vector4f color) { + ui->PushDevConsolePrintCall(msg, scale, color); } PyObject* BaseFeatureSet::GetPyExceptionType(PyExcType exctype) { @@ -833,7 +833,7 @@ void BaseFeatureSet::DoPushObjCall(const PythonObjectSetBase* objset, int id) { }); } else { BA_LOG_ONCE( - LogLevel::kError, + LogName::kBa, LogLevel::kError, "BaseFeatureSet::DoPushObjCall called before event loop created."); } } @@ -968,8 +968,9 @@ void BaseFeatureSet::SetAppActive(bool active) { // Issue a gentle warning if they are feeding us the same state twice in a // row; might imply faulty logic on an app-adapter or whatnot. if (app_active_set_ && app_active_ == active) { - Log(LogLevel::kWarning, "SetAppActive called with state " - + std::to_string(active) + " twice in a row."); + Log(LogName::kBa, LogLevel::kWarning, + "SetAppActive called with state " + std::to_string(active) + + " twice in a row."); } app_active_set_ = true; app_active_ = active; diff --git a/src/ballistica/base/base.h b/src/ballistica/base/base.h index 40866f5c..9ce9d9d5 100644 --- a/src/ballistica/base/base.h +++ b/src/ballistica/base/base.h @@ -731,7 +731,8 @@ class BaseFeatureSet : public FeatureSetNativeComponent, -> PyObject* override; auto FeatureSetFromData(PyObject* obj) -> FeatureSetNativeComponent* override; void DoV1CloudLog(const std::string& msg) override; - void PushDevConsolePrintCall(const std::string& msg) override; + void PushDevConsolePrintCall(const std::string& msg, float scale, + Vector4f color) override; auto GetPyExceptionType(PyExcType exctype) -> PyObject* override; auto PrintPythonStackTrace() -> bool override; auto GetPyLString(PyObject* obj) -> std::string override; diff --git a/src/ballistica/base/dynamics/bg/bg_dynamics.cc b/src/ballistica/base/dynamics/bg/bg_dynamics.cc index 232241bb..98449d53 100644 --- a/src/ballistica/base/dynamics/bg/bg_dynamics.cc +++ b/src/ballistica/base/dynamics/bg/bg_dynamics.cc @@ -72,8 +72,8 @@ void BGDynamics::Step(const Vector3f& cam_pos, int step_millisecs) { return; } - // Pass a newly allocated raw pointer to the bg-dynamics thread; it takes care - // of disposing it when done. + // Pass a newly allocated raw pointer to the bg-dynamics thread; it takes + // care of disposing it when done. auto d = Object::NewDeferred(); d->graphics_quality = Graphics::GraphicsQualityFromRequest( g_base->graphics->settings()->graphics_quality, diff --git a/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc b/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc index 631bac78..ace3ab35 100644 --- a/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc +++ b/src/ballistica/base/dynamics/bg/bg_dynamics_server.cc @@ -1372,7 +1372,7 @@ void BGDynamicsServer::Emit(const BGDynamicsEmission& def) { } default: { int t = static_cast(def.emit_type); - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Invalid bg-dynamics emit type: " + std::to_string(t)); break; } @@ -2361,9 +2361,9 @@ void BGDynamicsServer::Step(StepData* step_data) { // Math sanity check. if (step_count_ < 0) { - BA_LOG_ONCE(LogLevel::kWarning, "BGDynamics step_count too low (" - + std::to_string(step_count_) - + "); should not happen."); + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kWarning, + "BGDynamics step_count too low (" + std::to_string(step_count_) + + "); should not happen."); } } @@ -2376,9 +2376,9 @@ void BGDynamicsServer::PushStep(StepData* data) { // Client thread should stop feeding us if we get clogged up. if (step_count_ > 5) { - BA_LOG_ONCE(LogLevel::kWarning, "BGDynamics step_count too high (" - + std::to_string(step_count_) - + "); should not happen."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kWarning, + "BGDynamics step_count too high (" + std::to_string(step_count_) + + "); should not happen."); } event_loop()->PushCall([this, data] { Step(data); }); diff --git a/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h b/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h index 5190a191..daf2fad6 100644 --- a/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h +++ b/src/ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h @@ -80,7 +80,7 @@ class RendererGL::MeshAssetDataGL : public MeshAssetRendererData { } case 4: { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBaGraphics, LogLevel::kWarning, "GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!"); elem_count_ = static_cast(model.indices32().size()); index_type_ = GL_UNSIGNED_INT; diff --git a/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h b/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h index 2ba8ffcf..1b30874f 100644 --- a/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h +++ b/src/ballistica/base/graphics/gl/mesh/mesh_data_gl.h @@ -126,7 +126,7 @@ class RendererGL::MeshDataGL : public MeshRendererData { dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); index_state_ = data->state; have_index_data_ = true; - BA_LOG_ONCE(LogLevel::kWarning, + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kWarning, "GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!"); index_type_ = GL_UNSIGNED_INT; } diff --git a/src/ballistica/base/graphics/gl/program/program_blur_gl.h b/src/ballistica/base/graphics/gl/program/program_blur_gl.h index 05a212b6..f7d4f546 100644 --- a/src/ballistica/base/graphics/gl/program/program_blur_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_blur_gl.h @@ -86,7 +86,7 @@ class RendererGL::ProgramBlurGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } @@ -121,7 +121,7 @@ class RendererGL::ProgramBlurGL : public RendererGL::ProgramGL { "(colorTex,vUV8));\n" "}"; if (flags & SHD_DEBUG_PRINT) { - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); } return s; diff --git a/src/ballistica/base/graphics/gl/program/program_gl.h b/src/ballistica/base/graphics/gl/program/program_gl.h index dff358a8..6e4784a3 100644 --- a/src/ballistica/base/graphics/gl/program/program_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_gl.h @@ -49,7 +49,7 @@ class RendererGL::ShaderGL : public Object { const char* renderer = (const char*)glGetString(GL_RENDERER); // Let's not crash here. We have a better chance of calling home this // way and theres a chance the game will still be playable. - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, std::string("Compile failed for ") + GetTypeName() + " shader:\n------------SOURCE BEGIN-------------\n" + src_fin + "\n-----------SOURCE END-------------\n" + GetInfo() @@ -65,7 +65,7 @@ class RendererGL::ShaderGL : public Object { const char* version = (const char*)glGetString(GL_VERSION); const char* vendor = (const char*)glGetString(GL_VENDOR); const char* renderer = (const char*)glGetString(GL_RENDERER); - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, std::string("WARNING: info returned for ") + GetTypeName() + " shader:\n------------SOURCE BEGIN-------------\n" + src_fin + "\n-----------SOURCE END-------------\n" + info @@ -166,7 +166,7 @@ class RendererGL::ProgramGL { GLint linkStatus; glGetProgramiv(program_, GL_LINK_STATUS, &linkStatus); if (linkStatus == GL_FALSE) { - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, "Link failed for program '" + name_ + "':\n" + GetInfo()); } else { assert(linkStatus == GL_TRUE); @@ -176,8 +176,9 @@ class RendererGL::ProgramGL { && (strstr(info.c_str(), "error:") || strstr(info.c_str(), "warning:") || strstr(info.c_str(), "Error:") || strstr(info.c_str(), "Warning:"))) { - Log(LogLevel::kError, "WARNING: program using frag shader '" + name_ - + "' returned info:\n" + info); + Log(LogName::kBaGraphics, LogLevel::kError, + "WARNING: program using frag shader '" + name_ + + "' returned info:\n" + info); } } @@ -314,9 +315,9 @@ class RendererGL::ProgramGL { assert(IsBound()); int c = glGetUniformLocation(program_, tex_name); if (c == -1) { - Log(LogLevel::kError, "ShaderGL: " + name_ - + ": Can't set texture unit for texture '" - + tex_name + "'"); + Log(LogName::kBaGraphics, LogLevel::kError, + "ShaderGL: " + name_ + ": Can't set texture unit for texture '" + + tex_name + "'"); BA_DEBUG_CHECK_GL_ERROR; } else { glUniform1i(c, unit); diff --git a/src/ballistica/base/graphics/gl/program/program_object_gl.h b/src/ballistica/base/graphics/gl/program/program_object_gl.h index d04cb2c7..fe03290c 100644 --- a/src/ballistica/base/graphics/gl/program/program_object_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_object_gl.h @@ -235,7 +235,7 @@ class RendererGL::ProgramObjectGL : public RendererGL::ProgramGL { } s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } @@ -321,7 +321,7 @@ class RendererGL::ProgramObjectGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } diff --git a/src/ballistica/base/graphics/gl/program/program_post_process_gl.h b/src/ballistica/base/graphics/gl/program/program_post_process_gl.h index 1a4e12b1..c1d6c532 100644 --- a/src/ballistica/base/graphics/gl/program/program_post_process_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_post_process_gl.h @@ -158,7 +158,7 @@ class RendererGL::ProgramPostProcessGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } @@ -306,7 +306,7 @@ class RendererGL::ProgramPostProcessGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } diff --git a/src/ballistica/base/graphics/gl/program/program_shield_gl.h b/src/ballistica/base/graphics/gl/program/program_shield_gl.h index 2c2d9806..1d6f3e48 100644 --- a/src/ballistica/base/graphics/gl/program/program_shield_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_shield_gl.h @@ -54,7 +54,7 @@ class RendererGL::ProgramShieldGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } @@ -114,7 +114,7 @@ class RendererGL::ProgramShieldGL : public RendererGL::ProgramGL { //" " BA_GLSL_FRAGCOLOR " = vec4(vec3(depth),1);\n" if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } diff --git a/src/ballistica/base/graphics/gl/program/program_simple_gl.h b/src/ballistica/base/graphics/gl/program/program_simple_gl.h index 45215fbf..d96a94f0 100644 --- a/src/ballistica/base/graphics/gl/program/program_simple_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_simple_gl.h @@ -234,7 +234,7 @@ class RendererGL::ProgramSimpleGL : public RendererGL::ProgramGL { "}"; if (flags & SHD_DEBUG_PRINT) { - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); } @@ -383,7 +383,7 @@ class RendererGL::ProgramSimpleGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) { - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); } diff --git a/src/ballistica/base/graphics/gl/program/program_smoke_gl.h b/src/ballistica/base/graphics/gl/program/program_smoke_gl.h index 9d2cd0bb..8020d495 100644 --- a/src/ballistica/base/graphics/gl/program/program_smoke_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_smoke_gl.h @@ -114,7 +114,7 @@ class RendererGL::ProgramSmokeGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } @@ -160,7 +160,7 @@ class RendererGL::ProgramSmokeGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } diff --git a/src/ballistica/base/graphics/gl/program/program_sprite_gl.h b/src/ballistica/base/graphics/gl/program/program_sprite_gl.h index 0177ef69..66f5cebb 100644 --- a/src/ballistica/base/graphics/gl/program/program_sprite_gl.h +++ b/src/ballistica/base/graphics/gl/program/program_sprite_gl.h @@ -9,6 +9,7 @@ #include "ballistica/base/graphics/gl/program/program_gl.h" #include "ballistica/base/graphics/gl/renderer_gl.h" +#include "ballistica/base/graphics/graphics.h" namespace ballistica::base { @@ -121,7 +122,7 @@ class RendererGL::ProgramSpriteGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) { - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); } return s; @@ -163,7 +164,7 @@ class RendererGL::ProgramSpriteGL : public RendererGL::ProgramGL { } s += "}"; if (flags & SHD_DEBUG_PRINT) { - Log(LogLevel::kInfo, + Log(LogName::kBaGraphics, LogLevel::kInfo, "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); } return s; diff --git a/src/ballistica/base/graphics/gl/render_target_gl.h b/src/ballistica/base/graphics/gl/render_target_gl.h index d5ddf747..f4c87f9c 100644 --- a/src/ballistica/base/graphics/gl/render_target_gl.h +++ b/src/ballistica/base/graphics/gl/render_target_gl.h @@ -60,7 +60,7 @@ class RendererGL::RenderTargetGL : public RenderTarget { // this needs to be on for glClear to work on depth. if (!renderer_->depth_writing_enabled_) { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBaGraphics, LogLevel::kWarning, "RendererGL: depth-writing not enabled when clearing depth"); } clear_mask |= GL_DEPTH_BUFFER_BIT; diff --git a/src/ballistica/base/graphics/gl/renderer_gl.cc b/src/ballistica/base/graphics/gl/renderer_gl.cc index a0814019..b434723e 100644 --- a/src/ballistica/base/graphics/gl/renderer_gl.cc +++ b/src/ballistica/base/graphics/gl/renderer_gl.cc @@ -90,7 +90,7 @@ void RendererGL::CheckGLError(const char* file, int line) { BA_PRECONDITION_FATAL(vendor); const char* renderer = (const char*)glGetString(GL_RENDERER); BA_PRECONDITION_FATAL(renderer); - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, "OpenGL Error at " + std::string(file) + " line " + std::to_string(line) + ": " + GLErrorToString(err) + "\nrenderer: " + renderer + "\nvendor: " + vendor + "\nversion: " + version @@ -174,7 +174,7 @@ void RendererGL::CheckGLVersion() { if (gl_is_es()) { // GL ES version strings start with 'OpenGL ES X' with X being version. const char* prefix = "OpenGL ES "; - int prefixlen = strlen(prefix); + auto prefixlen = strlen(prefix); BA_PRECONDITION_FATAL(!strncmp(version_str, prefix, prefixlen)); if (version_str[prefixlen] != '3') { FatalError( @@ -223,11 +223,9 @@ void RendererGL::CheckGLCapabilities_() { basestr = "OpenGL"; } - if (g_buildconfig.debug_build()) { - Log(LogLevel::kInfo, std::string("Using ") + basestr + " (vendor: " + vendor - + ", renderer: " + renderer - + ", version: " + version_str + ")."); - } + Log(LogName::kBaGraphics, LogLevel::kInfo, + std::string("Using ") + basestr + " (vendor: " + vendor + + ", renderer: " + renderer + ", version: " + version_str + ")."); // Build a vector of extensions. Newer GLs give us extensions as lists // already, but on older ones we may need to break a single string apart @@ -246,7 +244,8 @@ void RendererGL::CheckGLCapabilities_() { extensions.push_back(extension); } } else { - Log(LogLevel::kWarning, "Falling back on legacy GL_EXTENSIONS parsing."); + Log(LogName::kBaGraphics, LogLevel::kWarning, + "Falling back on legacy GL_EXTENSIONS parsing."); // Fall back on parsing the single giant string if need be. // (Can probably kill this). auto* ex = reinterpret_cast(glGetString(GL_EXTENSIONS)); @@ -292,7 +291,8 @@ void RendererGL::CheckGLCapabilities_() { c_types.push_back(TextureCompressionType::kETC1); } else { if (g_buildconfig.ostype_android()) { - Log(LogLevel::kError, "Android device missing ETC1 support."); + Log(LogName::kBaGraphics, LogLevel::kError, + "Android device missing ETC1 support."); } } @@ -360,7 +360,8 @@ void RendererGL::CheckGLCapabilities_() { &samples[0]); msaa_max_samples_rgb565_ = samples[0]; } else { - BA_LOG_ONCE(LogLevel::kError, "Got 0 samplecounts for RGB565"); + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kError, + "Got 0 samplecounts for RGB565"); msaa_max_samples_rgb565_ = 0; } @@ -374,7 +375,8 @@ void RendererGL::CheckGLCapabilities_() { &samples[0]); msaa_max_samples_rgb8_ = samples[0]; } else { - BA_LOG_ONCE(LogLevel::kError, "Got 0 samplecounts for RGB8"); + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kError, + "Got 0 samplecounts for RGB8"); msaa_max_samples_rgb8_ = 0; } } else { @@ -2385,7 +2387,7 @@ void RendererGL::UpdateVignetteTex_(bool force) { if (err != GL_NO_ERROR) { static bool reported = false; if (!reported) { - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, "32-bit vignette creation failed; falling back to 16."); reported = true; } @@ -2443,7 +2445,8 @@ void RendererGL::UpdateVignetteTex_(bool force) { auto RendererGL::GetFunkyDepthIssue_() -> bool { if (!funky_depth_issue_set_) { - BA_LOG_ONCE(LogLevel::kError, "fetching funky depth issue but not set"); + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kError, + "fetching funky depth issue but not set"); } return funky_depth_issue_; } diff --git a/src/ballistica/base/graphics/gl/texture_data_gl.h b/src/ballistica/base/graphics/gl/texture_data_gl.h index 3ba25ac1..ba6ff65d 100644 --- a/src/ballistica/base/graphics/gl/texture_data_gl.h +++ b/src/ballistica/base/graphics/gl/texture_data_gl.h @@ -27,7 +27,8 @@ class RendererGL::TextureDataGL : public TextureAssetRendererData { ~TextureDataGL() override { if (!g_base->app_adapter->InGraphicsContext()) { - Log(LogLevel::kError, "TextureDataGL dying outside of graphics thread."); + Log(LogName::kBaGraphics, LogLevel::kError, + "TextureDataGL dying outside of graphics thread."); } else { // If we're currently bound as anything, clear that out (otherwise a // new texture with that same ID won't be bindable). diff --git a/src/ballistica/base/graphics/graphics.cc b/src/ballistica/base/graphics/graphics.cc index 892170f1..8ffda286 100644 --- a/src/ballistica/base/graphics/graphics.cc +++ b/src/ballistica/base/graphics/graphics.cc @@ -204,7 +204,7 @@ auto Graphics::TextureQualityFromAppConfig() -> TextureQualityRequest { } else if (texqualstr == "Low") { texture_quality_requested = TextureQualityRequest::kLow; } else { - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, "Invalid texture quality: '" + texqualstr + "'; defaulting to low."); texture_quality_requested = TextureQualityRequest::kLow; } @@ -221,7 +221,8 @@ auto Graphics::VSyncFromAppConfig() -> VSyncRequest { } else if (v_sync == "Never") { return VSyncRequest::kNever; } - Log(LogLevel::kError, "Invalid 'Vertical Sync' value: '" + v_sync + "'"); + Log(LogName::kBaGraphics, LogLevel::kError, + "Invalid 'Vertical Sync' value: '" + v_sync + "'"); return VSyncRequest::kNever; } @@ -240,7 +241,7 @@ auto Graphics::GraphicsQualityFromAppConfig() -> GraphicsQualityRequest { } else if (gqualstr == "Low") { graphics_quality_requested = GraphicsQualityRequest::kLow; } else { - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, "Invalid graphics quality: '" + gqualstr + "'; defaulting to auto."); graphics_quality_requested = GraphicsQualityRequest::kAuto; } @@ -627,7 +628,7 @@ void Graphics::FadeScreen(bool to, millisecs_t time, PyObject* endcall) { // (otherwise, overlapping fades can cause things to get lost) if (fade_end_call_.Exists()) { if (g_buildconfig.debug_build()) { - Log(LogLevel::kWarning, + Log(LogName::kBaGraphics, LogLevel::kWarning, "2 fades overlapping; running first fade-end-call early."); } fade_end_call_->Schedule(); @@ -861,7 +862,7 @@ void Graphics::BuildAndPushFrameDef() { if (g_core->vr_mode()) { if (frame_def->GetOverlayFlatPass()->HasDrawCommands()) { if (!g_base->ui->MainMenuVisible()) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kError, "Drawing in overlay pass in VR mode with no UI present; " "shouldn't happen!"); } @@ -1015,7 +1016,7 @@ void Graphics::DrawFades(FrameDef* frame_def) { // TEMP HACK - don't trigger this while inactive. // Need to make overall fade logic smarter. if (faded_time > 15000 && g_base->app_active()) { - Log(LogLevel::kError, "FORCE-ENDING STUCK FADE"); + Log(LogName::kBaGraphics, LogLevel::kError, "FORCE-ENDING STUCK FADE"); fade_out_ = false; fade_ = 1.0f; fade_time_ = 1000; @@ -1513,8 +1514,9 @@ void Graphics::DrawRadialMeter(MeshIndexedSimpleFull* m, float amt) { void Graphics::OnScreenSizeChange() {} -void Graphics::CalcVirtualRes_(float* x, float* y) { - assert(g_base); +void Graphics::GetBaseVirtualRes(float* x, float* y) { + assert(x); + assert(y); float base_virtual_res_x; float base_virtual_res_y; if (g_base->ui->scale() == UIScale::kSmall) { @@ -1524,6 +1526,15 @@ void Graphics::CalcVirtualRes_(float* x, float* y) { base_virtual_res_x = kBaseVirtualResX; base_virtual_res_y = kBaseVirtualResY; } + *x = base_virtual_res_x; + *y = base_virtual_res_y; +} + +void Graphics::CalcVirtualRes_(float* x, float* y) { + assert(g_base); + float base_virtual_res_x; + float base_virtual_res_y; + GetBaseVirtualRes(&base_virtual_res_x, &base_virtual_res_y); float x_in = *x; float y_in = *y; @@ -1662,7 +1673,7 @@ auto Graphics::ReflectionTypeFromString(const std::string& s) void Graphics::LanguageChanged() { assert(g_base && g_base->InLogicThread()); if (building_frame_def_) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "Graphics::LanguageChanged() called during draw; should not happen."); } screenmessages->ClearScreenMessageTranslations(); @@ -1683,8 +1694,9 @@ auto Graphics::GraphicsQualityFromRequest(GraphicsQualityRequest request, case GraphicsQualityRequest::kAuto: return auto_val; default: - Log(LogLevel::kError, "Unhandled GraphicsQualityRequest value: " - + std::to_string(static_cast(request))); + Log(LogName::kBa, LogLevel::kError, + "Unhandled GraphicsQualityRequest value: " + + std::to_string(static_cast(request))); return GraphicsQuality::kLow; } } @@ -1702,8 +1714,9 @@ auto Graphics::TextureQualityFromRequest(TextureQualityRequest request, case TextureQualityRequest::kAuto: return auto_val; default: - Log(LogLevel::kError, "Unhandled TextureQualityRequest value: " - + std::to_string(static_cast(request))); + Log(LogName::kBaGraphics, LogLevel::kError, + "Unhandled TextureQualityRequest value: " + + std::to_string(static_cast(request))); return TextureQuality::kLow; } } @@ -1747,13 +1760,8 @@ void Graphics::DrawUIBounds(RenderPass* pass) { auto xf = c.ScopedTransform(); float width, height; - if (g_base->ui->scale() == UIScale::kSmall) { - width = kBaseVirtualResSmallX; - height = kBaseVirtualResSmallY; - } else { - width = kBaseVirtualResX; - height = kBaseVirtualResY; - } + + GetBaseVirtualRes(&width, &height); // Slight offset in z to reduce z fighting. c.Translate(0.5f * pass->virtual_width(), 0.5f * pass->virtual_height(), diff --git a/src/ballistica/base/graphics/graphics.h b/src/ballistica/base/graphics/graphics.h index a8e8e116..2b898e1d 100644 --- a/src/ballistica/base/graphics/graphics.h +++ b/src/ballistica/base/graphics/graphics.h @@ -153,6 +153,7 @@ class Graphics { } void DrawUIBounds(RenderPass* pass); + static void GetBaseVirtualRes(float* x, float* y); // Enable progress bar drawing locally. void EnableProgressBar(bool fade_in); diff --git a/src/ballistica/base/graphics/graphics_server.cc b/src/ballistica/base/graphics/graphics_server.cc index 1941fa11..161dff8d 100644 --- a/src/ballistica/base/graphics/graphics_server.cc +++ b/src/ballistica/base/graphics/graphics_server.cc @@ -178,7 +178,7 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* { millisecs_t t = g_core->GetAppTimeMillisecs() - start_time; if (t >= 1000) { if (g_buildconfig.debug_build()) { - Log(LogLevel::kWarning, + Log(LogName::kBaGraphics, LogLevel::kWarning, "GraphicsServer: timed out at " + std::to_string(t) + "ms waiting for logic thread to send us a FrameDef."); } @@ -266,7 +266,8 @@ void GraphicsServer::ReloadLostRenderer() { assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_) { - Log(LogLevel::kError, "No renderer on GraphicsServer::ReloadLostRenderer."); + Log(LogName::kBaGraphics, LogLevel::kError, + "No renderer on GraphicsServer::ReloadLostRenderer."); return; } @@ -323,11 +324,12 @@ void GraphicsServer::set_renderer(Renderer* renderer) { void GraphicsServer::LoadRenderer() { assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_) { - Log(LogLevel::kError, "LoadRenderer() called with no renderer present."); + Log(LogName::kBaGraphics, LogLevel::kError, + "LoadRenderer() called with no renderer present."); return; } if (renderer_loaded_) { - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, "LoadRenderer() called with an already-loaded renderer present."); return; } @@ -369,11 +371,12 @@ void GraphicsServer::LoadRenderer() { void GraphicsServer::UnloadRenderer() { assert(g_base->app_adapter->InGraphicsContext()); if (!renderer_) { - Log(LogLevel::kError, "UnloadRenderer() called with no renderer present."); + Log(LogName::kBaGraphics, LogLevel::kError, + "UnloadRenderer() called with no renderer present."); return; } if (!renderer_loaded_) { - Log(LogLevel::kError, + Log(LogName::kBaGraphics, LogLevel::kError, "UnloadRenderer() called with an already unloaded renderer present."); return; } @@ -532,7 +535,7 @@ void GraphicsServer::PushRemoveRenderHoldCall() { assert(render_hold_); render_hold_--; if (render_hold_ < 0) { - Log(LogLevel::kError, "RenderHold < 0"); + Log(LogName::kBaGraphics, LogLevel::kError, "RenderHold < 0"); render_hold_ = 0; } }); diff --git a/src/ballistica/base/graphics/mesh/mesh_data.h b/src/ballistica/base/graphics/mesh/mesh_data.h index 83667c4c..02eb7c50 100644 --- a/src/ballistica/base/graphics/mesh/mesh_data.h +++ b/src/ballistica/base/graphics/mesh/mesh_data.h @@ -17,7 +17,8 @@ class MeshData { : type_(type), draw_type_(draw_type) {} virtual ~MeshData() { if (renderer_data_) { - Log(LogLevel::kError, "MeshData going down with rendererData intact!"); + Log(LogName::kBaGraphics, LogLevel::kError, + "MeshData going down with rendererData intact!"); } } std::list::iterator iterator_; diff --git a/src/ballistica/base/graphics/mesh/mesh_indexed_base.h b/src/ballistica/base/graphics/mesh/mesh_indexed_base.h index 3323b3e0..675dbe6e 100644 --- a/src/ballistica/base/graphics/mesh/mesh_indexed_base.h +++ b/src/ballistica/base/graphics/mesh/mesh_indexed_base.h @@ -76,7 +76,7 @@ class MeshIndexedBase : public Mesh { // For use by subclasses in their IsValid() overrides auto IndexSizeIsValid(size_t data_size) const -> bool { if (index_data_size() == 2 && data_size > 65535) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kError, "Got mesh data with > 65535 elems and 16 bit indices: " + GetObjectDescription() + ". This case requires 32 bit indices."); diff --git a/src/ballistica/base/graphics/mesh/mesh_indexed_static_dynamic.h b/src/ballistica/base/graphics/mesh/mesh_indexed_static_dynamic.h index 9fb0b46d..4f12ae6a 100644 --- a/src/ballistica/base/graphics/mesh/mesh_indexed_static_dynamic.h +++ b/src/ballistica/base/graphics/mesh/mesh_indexed_static_dynamic.h @@ -32,7 +32,7 @@ class MeshIndexedStaticDynamic : public MeshIndexedBase { // Static and dynamic data sizes should always match, right? if (static_data_->elements.size() != dynamic_data_->elements.size()) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kError, "Mesh static and dynamic data sizes do not match"); return false; } diff --git a/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc b/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc index 90ca292a..953b354a 100644 --- a/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc +++ b/src/ballistica/base/graphics/mesh/nine_patch_mesh.cc @@ -13,7 +13,8 @@ NinePatchMesh::NinePatchMesh(float x, float y, float z, float width, || (border_bottom + border_top) > 1.0f) || (border_left < 0.0f || border_right < 0.0f || (border_left + border_right) > 1.0f)) { - BA_LOG_ONCE(LogLevel::kWarning, "Invalid nine-patch values provided."); + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kWarning, + "Invalid nine-patch values provided."); } } // Statically allocate enough for a full 9 patches even though we may diff --git a/src/ballistica/base/graphics/renderer/render_target.h b/src/ballistica/base/graphics/renderer/render_target.h index 7c28ce29..72c6c928 100644 --- a/src/ballistica/base/graphics/renderer/render_target.h +++ b/src/ballistica/base/graphics/renderer/render_target.h @@ -24,7 +24,7 @@ class RenderTarget : public Object { void DrawBegin(bool clear, const Vector4f& clear_color = {0.0f, 0.0f, 0.0f, 1.0f}) { DrawBegin(clear, clear_color.x, clear_color.y, clear_color.z, - clear_color.w); + clear_color.a); } void OnScreenSizeChange(); diff --git a/src/ballistica/base/graphics/support/screen_messages.cc b/src/ballistica/base/graphics/support/screen_messages.cc index 79c52707..4c04c36f 100644 --- a/src/ballistica/base/graphics/support/screen_messages.cc +++ b/src/ballistica/base/graphics/support/screen_messages.cc @@ -152,7 +152,7 @@ void ScreenMessages::DrawMiscOverlays(FrameDef* frame_def) { if (i->translation_dirty) { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBaGraphics, LogLevel::kWarning, "Found dirty translation on screenmessage draw pass 1; raw=" + i->s_raw); } @@ -261,7 +261,7 @@ void ScreenMessages::DrawMiscOverlays(FrameDef* frame_def) { } if (i->translation_dirty) { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBaGraphics, LogLevel::kWarning, "Found dirty translation on screenmessage draw pass 2; raw=" + i->s_raw); } @@ -495,7 +495,7 @@ void ScreenMessages::ClearScreenMessageTranslations() { auto ScreenMessages::ScreenMessageEntry::GetText() -> TextGroup& { if (translation_dirty) { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBaGraphics, LogLevel::kWarning, "Found dirty translation on screenmessage GetText; raw=" + s_raw); } if (!s_mesh_.Exists()) { diff --git a/src/ballistica/base/graphics/text/text_graphics.cc b/src/ballistica/base/graphics/text/text_graphics.cc index 3f3bbbc4..ba9f76c8 100644 --- a/src/ballistica/base/graphics/text/text_graphics.cc +++ b/src/ballistica/base/graphics/text/text_graphics.cc @@ -349,7 +349,8 @@ TextGraphics::TextGraphics() { if (g.tex_max_x > 1.0f || g.tex_max_x < 0.0f || g.tex_min_x > 1.0 || g.tex_min_x < 0.0f || g.tex_max_y > 1.0f || g.tex_max_y < 0.0 || g.tex_min_y > 1.0f || g.tex_min_y < 0.0f) { - BA_LOG_ONCE(LogLevel::kWarning, "glyph bounds error"); + BA_LOG_ONCE(LogName::kBaGraphics, LogLevel::kWarning, + "glyph bounds error"); } } } @@ -1042,7 +1043,7 @@ void TextGraphics::GetOSTextSpanBoundsAndWidth(const std::string& s, Rect* r, g_core->platform->GetTextBoundsAndWidth(s, &entry->r, &entry->width); } else { BA_LOG_ONCE( - LogLevel::kError, + LogName::kBaGraphics, LogLevel::kError, "FIXME: GetOSTextSpanBoundsAndWidth unimplemented on this platform"); r->l = 0.0f; r->r = 1.0f; diff --git a/src/ballistica/base/input/device/joystick_input.cc b/src/ballistica/base/input/device/joystick_input.cc index 14b0cecc..88fd1e0d 100644 --- a/src/ballistica/base/input/device/joystick_input.cc +++ b/src/ballistica/base/input/device/joystick_input.cc @@ -289,7 +289,7 @@ auto JoystickInput::GetButtonName(int index) -> std::string { JoystickInput::~JoystickInput() { if (!g_base->InLogicThread()) { - Log(LogLevel::kError, "Joystick dying in wrong thread."); + Log(LogName::kBaInput, LogLevel::kError, "Joystick dying in wrong thread."); } // Kill our child if need be. @@ -311,7 +311,7 @@ JoystickInput::~JoystickInput() { [joystick] { SDL_JoystickClose(joystick); }); sdl_joystick_ = nullptr; #else - Log(LogLevel::kError, + Log(LogName::kBaInput, LogLevel::kError, "sdl_joystick_ set in non-sdl-joystick build destructor."); #endif // BA_ENABLE_SDL_JOYSTICKS } @@ -654,7 +654,7 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) { hat_held_ = true; break; default: - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaInput, LogLevel::kError, "Invalid hat value: " + std::to_string(static_cast(e->jhat.value))); break; @@ -1231,7 +1231,7 @@ void JoystickInput::UpdateMapping() { auto* cl{g_base->HaveClassic() ? g_base->classic() : nullptr}; if (!cl) { - Log(LogLevel::kWarning, + Log(LogName::kBaInput, LogLevel::kWarning, "Classic not present; can't config joystick mapping."); } diff --git a/src/ballistica/base/input/device/touch_input.cc b/src/ballistica/base/input/device/touch_input.cc index 5b2a8add..dc236b9d 100644 --- a/src/ballistica/base/input/device/touch_input.cc +++ b/src/ballistica/base/input/device/touch_input.cc @@ -852,7 +852,7 @@ void TouchInput::UpdateMapping() { } else if (touch_movement_type == "joystick") { movement_control_type_ = TouchInput::MovementControlType::kJoystick; } else { - Log(LogLevel::kError, + Log(LogName::kBaInput, LogLevel::kError, "Invalid touch-movement-type: " + touch_movement_type); movement_control_type_ = TouchInput::MovementControlType::kSwipe; } @@ -863,7 +863,8 @@ void TouchInput::UpdateMapping() { } else if (touch_action_type == "buttons") { action_control_type_ = TouchInput::ActionControlType::kButtons; } else { - Log(LogLevel::kError, "Invalid touch-action-type: " + touch_action_type); + Log(LogName::kBaInput, LogLevel::kError, + "Invalid touch-action-type: " + touch_action_type); action_control_type_ = TouchInput::ActionControlType::kSwipe; } diff --git a/src/ballistica/base/input/input.cc b/src/ballistica/base/input/input.cc index 1d5417d1..72e532ed 100644 --- a/src/ballistica/base/input/input.cc +++ b/src/ballistica/base/input/input.cc @@ -34,7 +34,7 @@ void Input::PushCreateKeyboardInputDevices() { void Input::CreateKeyboardInputDevices_() { assert(g_base->InLogicThread()); if (keyboard_input_ != nullptr || keyboard_input_2_ != nullptr) { - Log(LogLevel::kError, + Log(LogName::kBaInput, LogLevel::kError, "CreateKeyboardInputDevices called with existing kbs."); return; } @@ -53,7 +53,7 @@ void Input::PushDestroyKeyboardInputDevices() { void Input::DestroyKeyboardInputDevices_() { assert(g_base->InLogicThread()); if (keyboard_input_ == nullptr || keyboard_input_2_ == nullptr) { - Log(LogLevel::kError, + Log(LogName::kBaInput, LogLevel::kError, "DestroyKeyboardInputDevices called with null kb(s)."); return; } @@ -558,7 +558,7 @@ void Input::StepDisplayTime() { // If input has been locked an excessively long amount of time, unlock it. if (input_lock_count_temp_) { if (real_time - last_input_temp_lock_time_ > 10000) { - Log(LogLevel::kError, + Log(LogName::kBaInput, LogLevel::kError, "Input has been temp-locked for 10 seconds; unlocking."); input_lock_count_temp_ = 0; PrintLockLabels_(); @@ -664,7 +664,7 @@ void Input::UnlockAllInput(bool permanent, const std::string& label) { input_lock_count_temp_--; input_unlock_temp_labels_.push_back(label); if (input_lock_count_temp_ < 0) { - Log(LogLevel::kWarning, + Log(LogName::kBaInput, LogLevel::kWarning, "temp input unlock at time " + std::to_string(g_core->GetAppTimeMillisecs()) + " with no active lock: '" + label + "'"); @@ -719,7 +719,7 @@ void Input::PrintLockLabels_() { s += "\n " + std::to_string(num++) + ": " + recent_input_locks_unlock; } - Log(LogLevel::kError, s); + Log(LogName::kBaInput, LogLevel::kError, s); } void Input::PushTextInputEvent(const std::string& text) { @@ -757,7 +757,8 @@ void Input::PushTextInputEvent(const std::string& text) { // platforms) but make a stink if they sent us something that we can't // at least translate to unicode. if (!Utils::IsValidUTF8(text)) { - Log(LogLevel::kWarning, "PushTextInputEvent passed invalid utf-8 text."); + Log(LogName::kBaInput, LogLevel::kWarning, + "PushTextInputEvent passed invalid utf-8 text."); return; } @@ -844,7 +845,8 @@ void Input::CaptureKeyboardInput(HandleKeyPressCall* press_call, HandleKeyReleaseCall* release_call) { assert(g_base->InLogicThread()); if (keyboard_input_capture_press_ || keyboard_input_capture_release_) { - Log(LogLevel::kError, "Setting key capture redundantly."); + Log(LogName::kBaInput, LogLevel::kError, + "Setting key capture redundantly."); } keyboard_input_capture_press_ = press_call; keyboard_input_capture_release_ = release_call; @@ -859,7 +861,8 @@ void Input::ReleaseKeyboardInput() { void Input::CaptureJoystickInput(HandleJoystickEventCall* call) { assert(g_base->InLogicThread()); if (joystick_input_capture_) { - Log(LogLevel::kError, "Setting joystick capture redundantly."); + Log(LogName::kBaInput, LogLevel::kError, + "Setting joystick capture redundantly."); } joystick_input_capture_ = call; } @@ -930,7 +933,7 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) { count++; if (count > 10) { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBaInput, LogLevel::kWarning, "Input::HandleKeyPress_ seems to be getting passed repeat key" " press events. Only initial press events should be passed."); } @@ -1442,7 +1445,7 @@ void Input::HandleTouchEvent_(const TouchEvent& e) { // overall multitouch gesture, it should always be winding up as our // single_touch_. if (e.type == TouchEvent::Type::kDown && single_touch_ != nullptr) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaInput, LogLevel::kError, "Got touch labeled first but will not be our single."); } @@ -1452,7 +1455,7 @@ void Input::HandleTouchEvent_(const TouchEvent& e) { if ((e.type == TouchEvent::Type::kUp || e.type == TouchEvent::Type::kCanceled) && single_touch_ != nullptr && single_touch_ != e.touch) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaInput, LogLevel::kError, "Last touch coming up is not single touch!"); } } @@ -1567,7 +1570,7 @@ void Input::LsInputDevices() { ++index; } - Log(LogLevel::kInfo, out); + Log(LogName::kBaInput, LogLevel::kInfo, out); } auto Input::ShouldAllowInputInAttractMode_(InputDevice* device) const -> bool { diff --git a/src/ballistica/base/input/support/remote_app_server.cc b/src/ballistica/base/input/support/remote_app_server.cc index b127a942..856bf59f 100644 --- a/src/ballistica/base/input/support/remote_app_server.cc +++ b/src/ballistica/base/input/support/remote_app_server.cc @@ -73,7 +73,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, } case BA_PACKET_REMOTE_ID_REQUEST: { if (amt < 5 || amt > 127) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaInput, LogLevel::kError, "Received invalid BA_PACKET_REMOTE_ID_REQUEST of length " + std::to_string(amt)); break; @@ -214,7 +214,8 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, // Each state is 2 bytes. So make sure our length adds up. if (amt != 4 + state_count * 3) { - BA_LOG_ONCE(LogLevel::kError, "Invalid state packet"); + BA_LOG_ONCE(LogName::kBaInput, LogLevel::kError, + "Invalid state packet"); return; } RemoteAppClient* client = clients_ + joystick_id; diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index 75d74e6a..a4eaf1c5 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -384,7 +384,7 @@ void Logic::OnAppModeChanged() { // long sleep we're currently in the middle of. if (g_core->HeadlessMode()) { if (debug_log_display_time_) { - Log(LogLevel::kDebug, + Log(LogName::kBa, LogLevel::kDebug, "Resetting headless display step timer due to app-mode change."); } assert(headless_display_time_step_timer_); @@ -419,7 +419,7 @@ void Logic::UpdateDisplayTimeForHeadlessMode_() { char buffer[256]; snprintf(buffer, sizeof(buffer), "stepping display-time at app-time %.4f", static_cast(app_time_microsecs) / 1000000.0); - Log(LogLevel::kDebug, buffer); + Log(LogName::kBa, LogLevel::kDebug, buffer); } } @@ -441,7 +441,7 @@ void Logic::PostUpdateDisplayTimeForHeadlessMode_() { snprintf(buffer, sizeof(buffer), "will try to sleep for %.4f at app-time %.4f (until %.4f)", sleepsecs, apptimesecs, apptimesecs + sleepsecs); - Log(LogLevel::kDebug, buffer); + Log(LogName::kBa, LogLevel::kDebug, buffer); } auto sleep_microsecs = headless_display_step_microsecs; @@ -548,7 +548,7 @@ void Logic::UpdateDisplayTimeForFrameDraw_() { snprintf(buffer, sizeof(buffer), "trailing_dist %.6f > trail_buffer %.6f; will offset %.6f).", trailing_dist, trail_buffer, offs); - Log(LogLevel::kDebug, buffer); + Log(LogName::kBa, LogLevel::kDebug, buffer); } display_time_increment_ = display_time_increment_ + offs; } @@ -559,7 +559,7 @@ void Logic::UpdateDisplayTimeForFrameDraw_() { "final %.5f current(%s) %.5f sample %.5f chaos %.5f", display_time_increment_, use_avg ? "avg" : "sample", used, this_increment, chaos); - Log(LogLevel::kDebug, buffer); + Log(LogName::kBa, LogLevel::kDebug, buffer); } } @@ -660,7 +660,7 @@ void Logic::SetAppTimerLength(int timer_id, microsecs_t length) { if (t) { t->SetLength(length); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Logic::SetAppTimerLength() called on nonexistent timer."); } } @@ -687,7 +687,7 @@ void Logic::SetDisplayTimerLength(int timer_id, microsecs_t length) { if (t) { t->SetLength(length); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Logic::SetDisplayTimerLength() called on nonexistent timer."); } } diff --git a/src/ballistica/base/networking/network_reader.cc b/src/ballistica/base/networking/network_reader.cc index 560e667c..c77ac749 100644 --- a/src/ballistica/base/networking/network_reader.cc +++ b/src/ballistica/base/networking/network_reader.cc @@ -61,8 +61,9 @@ void NetworkReader::OnAppUnsuspend() { void NetworkReader::PokeSelf_() { int sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { - Log(LogLevel::kError, "Unable to create sleep ping socket; errno " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBaNetworking, LogLevel::kError, + "Unable to create sleep ping socket; errno " + + g_core->platform->GetSocketErrorString()); } else { struct sockaddr_in serv_addr{}; memset(&serv_addr, 0, sizeof(serv_addr)); @@ -71,8 +72,9 @@ void NetworkReader::PokeSelf_() { serv_addr.sin_port = 0; // any int bresult = ::bind(sd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); if (bresult == 1) { - Log(LogLevel::kError, "Unable to bind sleep socket: " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBaNetworking, LogLevel::kError, + "Unable to bind sleep socket: " + + g_core->platform->GetSocketErrorString()); } else { struct sockaddr_in t_addr{}; memset(&t_addr, 0, sizeof(t_addr)); @@ -83,8 +85,9 @@ void NetworkReader::PokeSelf_() { ssize_t sresult = sendto(sd, b, 1, 0, (struct sockaddr*)(&t_addr), sizeof(t_addr)); if (sresult == -1) { - Log(LogLevel::kError, "Error on sleep self-sendto: " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error on sleep self-sendto: " + + g_core->platform->GetSocketErrorString()); } } g_core->platform->CloseSocket(sd); @@ -115,7 +118,7 @@ void NetworkReader::DoPoll_(bool* can_read_4, bool* can_read_6) { // Aint no thang. } else { // Let's complain for anything else though. - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Error on select: " + g_core->platform->GetSocketErrorString()); } } else { @@ -123,7 +126,8 @@ void NetworkReader::DoPoll_(bool* can_read_4, bool* can_read_6) { *can_read_6 = index_6 != -1 && fds[index_6].revents & POLLIN; } } else { - BA_LOG_ONCE(LogLevel::kError, "DoPoll called with neither sd4 or sd6 set."); + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, + "DoPoll called with neither sd4 or sd6 set."); } } @@ -167,7 +171,7 @@ void NetworkReader::DoSelect_(bool* can_read_4, bool* can_read_6) { // Aint no thang. } else { // Let's complain for anything else though. - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Error on select: " + g_core->platform->GetSocketErrorString()); } } else { @@ -235,7 +239,7 @@ auto NetworkReader::RunThread_() -> int { recvfrom(sd, buffer, sizeof(buffer), 0, reinterpret_cast(&from), &from_size); if (rresult == 0) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "NetworkReader Recv got length 0; this shouldn't " "happen"); } else if (rresult == -1) { @@ -384,7 +388,7 @@ void NetworkReader::PushIncomingUDPPacketCall_(const std::vector& data, // these are unreliable messages so its ok to just drop them. if (!g_base->logic->event_loop()->CheckPushSafety()) { BA_LOG_ONCE( - LogLevel::kError, + LogName::kBaNetworking, LogLevel::kError, "Ignoring excessive udp-connection input packets; (could this be a " "flood attack?)."); return; @@ -413,8 +417,9 @@ void NetworkReader::OpenSockets_() { sd4_ = socket(AF_INET, SOCK_DGRAM, 0); if (sd4_ < 0) { - Log(LogLevel::kError, "Unable to open host socket; errno " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBaNetworking, LogLevel::kError, + "Unable to open host socket; errno " + + g_core->platform->GetSocketErrorString()); } else { g_core->platform->SetSocketNonBlocking(sd4_); @@ -465,8 +470,9 @@ void NetworkReader::OpenSockets_() { // available everywhere (win XP, etc) so let's do this for now. sd6_ = socket(AF_INET6, SOCK_DGRAM, 0); if (sd6_ < 0) { - Log(LogLevel::kError, "Unable to open ipv6 socket: " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBaNetworking, LogLevel::kError, + "Unable to open ipv6 socket: " + + g_core->platform->GetSocketErrorString()); } else { // Since we're explicitly creating both a v4 and v6 socket, tell the v6 // to *not* do both itself (not sure if this is necessary; on mac it @@ -475,7 +481,8 @@ void NetworkReader::OpenSockets_() { if (setsockopt(sd6_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&on), sizeof(on)) == -1) { - Log(LogLevel::kError, "Error setting socket as ipv6-only"); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error setting socket as ipv6-only"); } g_core->platform->SetSocketNonBlocking(sd6_); @@ -522,9 +529,9 @@ void NetworkReader::OpenSockets_() { + std::to_string(initial_requested_port) + "; some network functionality may fail.", {1, 0.5f, 0}); - Log(LogLevel::kWarning, "Unable to bind udp port " - + std::to_string(initial_requested_port) - + "; some network functionality may fail."); + Log(LogName::kBaNetworking, LogLevel::kWarning, + "Unable to bind udp port " + std::to_string(initial_requested_port) + + "; some network functionality may fail."); } } diff --git a/src/ballistica/base/networking/network_writer.cc b/src/ballistica/base/networking/network_writer.cc index 2de1ebb8..c4823856 100644 --- a/src/ballistica/base/networking/network_writer.cc +++ b/src/ballistica/base/networking/network_writer.cc @@ -24,7 +24,7 @@ void NetworkWriter::PushSendToCall(const std::vector& msg, // Avoid buffer-full errors if something is causing us to write too often; // these are unreliable messages so its ok to just drop them. if (!event_loop()->CheckPushSafety()) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "Excessive send-to calls in net-write-module."); return; } diff --git a/src/ballistica/base/platform/apple/base_platform_apple.cc b/src/ballistica/base/platform/apple/base_platform_apple.cc index 4acc20b2..f9e80c5e 100644 --- a/src/ballistica/base/platform/apple/base_platform_apple.cc +++ b/src/ballistica/base/platform/apple/base_platform_apple.cc @@ -116,7 +116,7 @@ void BasePlatformApple::LoginAdapterGetSignInToken( if (login_type == "game_center") { BallisticaKit::GameCenterContext::getSignInToken(attempt_id); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Got unexpected get-sign-in-token login-type: " + login_type); } #else @@ -130,7 +130,7 @@ void BasePlatformApple::LoginAdapterBackEndActiveChange( if (login_type == "game_center") { BallisticaKit::GameCenterContext::backEndActiveChange(active); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Got unexpected back-end-active-change login-type: " + login_type); } #else diff --git a/src/ballistica/base/platform/base_platform.cc b/src/ballistica/base/platform/base_platform.cc index 1bd1c068..ca581171 100644 --- a/src/ballistica/base/platform/base_platform.cc +++ b/src/ballistica/base/platform/base_platform.cc @@ -104,12 +104,12 @@ void BasePlatform::DoPurchase(const std::string& item) { } void BasePlatform::RestorePurchases() { - Log(LogLevel::kError, "RestorePurchases() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "RestorePurchases() unimplemented"); } void BasePlatform::PurchaseAck(const std::string& purchase, const std::string& order_id) { - Log(LogLevel::kError, "PurchaseAck() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "PurchaseAck() unimplemented"); } void BasePlatform::OpenURL(const std::string& url) { @@ -134,7 +134,7 @@ void BasePlatform::OverlayWebBrowserOpenURL(const std::string& url) { std::scoped_lock lock(web_overlay_mutex_); if (web_overlay_open_) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "OverlayWebBrowserOnClose called with already existing overlay."); return; } @@ -155,7 +155,7 @@ auto BasePlatform::OverlayWebBrowserIsOpen() -> bool { void BasePlatform::OverlayWebBrowserOnClose() { std::scoped_lock lock(web_overlay_mutex_); if (!web_overlay_open_) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "OverlayWebBrowserOnClose called with no known overlay."); } web_overlay_open_ = false; @@ -174,12 +174,13 @@ void BasePlatform::OverlayWebBrowserClose() { } void BasePlatform::DoOverlayWebBrowserOpenURL(const std::string& url) { - Log(LogLevel::kError, "DoOpenURLInOverlayBrowser unimplemented"); + Log(LogName::kBa, LogLevel::kError, + "DoOpenURLInOverlayBrowser unimplemented"); } void BasePlatform::DoOverlayWebBrowserClose() { // As a default, use Python's webbrowser module functionality. - Log(LogLevel::kError, "DoOverlayWebBrowserClose unimplemented"); + Log(LogName::kBa, LogLevel::kError, "DoOverlayWebBrowserClose unimplemented"); } #if !BA_OSTYPE_WINDOWS @@ -188,7 +189,7 @@ static void HandleSIGINT(int s) { g_base->logic->event_loop()->PushCall( [] { g_base->logic->HandleInterruptSignal(); }); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SigInt handler called before g_base->logic->event_loop exists."); } } @@ -197,7 +198,7 @@ static void HandleSIGTERM(int s) { g_base->logic->event_loop()->PushCall( [] { g_base->logic->HandleTerminateSignal(); }); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SigInt handler called before g_base->logic->event_loop exists."); } } @@ -276,17 +277,18 @@ void BasePlatform::StringEditorCancel() { void BasePlatform::DoInvokeStringEditor(const std::string& title, const std::string& value, std::optional max_chars) { - Log(LogLevel::kError, "FIXME: DoInvokeStringEditor() unimplemented"); + Log(LogName::kBa, LogLevel::kError, + "FIXME: DoInvokeStringEditor() unimplemented"); } auto BasePlatform::SupportsOpenDirExternally() -> bool { return false; } void BasePlatform::OpenDirExternally(const std::string& path) { - Log(LogLevel::kError, "OpenDirExternally() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "OpenDirExternally() unimplemented"); } void BasePlatform::OpenFileExternally(const std::string& path) { - Log(LogLevel::kError, "OpenFileExternally() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "OpenFileExternally() unimplemented"); } auto BasePlatform::SafeStdinFGetS(char* s, int n, FILE* iop) -> char* { diff --git a/src/ballistica/base/platform/linux/base_platform_linux.cc b/src/ballistica/base/platform/linux/base_platform_linux.cc index f4b37acc..50b8386e 100644 --- a/src/ballistica/base/platform/linux/base_platform_linux.cc +++ b/src/ballistica/base/platform/linux/base_platform_linux.cc @@ -23,8 +23,9 @@ void BasePlatformLinux::OpenDirExternally(const std::string& path) { std::string cmd = std::string("xdg-open \"") + path + "\""; int result = system(cmd.c_str()); if (result != 0) { - Log(LogLevel::kError, "Got return value " + std::to_string(result) - + " on xdg-open cmd '" + cmd + "'"); + Log(LogName::kBa, LogLevel::kError, + "Got return value " + std::to_string(result) + " on xdg-open cmd '" + + cmd + "'"); } } @@ -32,8 +33,9 @@ void BasePlatformLinux::OpenFileExternally(const std::string& path) { std::string cmd = std::string("xdg-open \"") + path + "\""; int result = system(cmd.c_str()); if (result != 0) { - Log(LogLevel::kError, "Got return value " + std::to_string(result) - + " on xdg-open cmd '" + cmd + "'"); + Log(LogName::kBa, LogLevel::kError, + "Got return value " + std::to_string(result) + " on xdg-open cmd '" + + cmd + "'"); } } diff --git a/src/ballistica/base/platform/support/min_sdl_key_names.h b/src/ballistica/base/platform/support/min_sdl_key_names.h index df8172b7..16b5248b 100644 --- a/src/ballistica/base/platform/support/min_sdl_key_names.h +++ b/src/ballistica/base/platform/support/min_sdl_key_names.h @@ -348,7 +348,7 @@ static const char* GetScancodeName(SDL_Scancode scancode) { const char* name; if (static_cast(scancode) < SDL_SCANCODE_UNKNOWN || scancode >= SDL_NUM_SCANCODES) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaInput, LogLevel::kError, "GetScancodeName passed invalid scancode " + std::to_string(static_cast(scancode))); return ""; diff --git a/src/ballistica/base/platform/windows/base_platform_windows.cc b/src/ballistica/base/platform/windows/base_platform_windows.cc index 6ce7330b..c72012e1 100644 --- a/src/ballistica/base/platform/windows/base_platform_windows.cc +++ b/src/ballistica/base/platform/windows/base_platform_windows.cc @@ -36,7 +36,7 @@ void BasePlatformWindows::DoOpenURL(const std::string& url) { // This should return > 32 on success. if (r <= 32) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Error " + std::to_string(r) + " opening URL '" + url + "'"); } } @@ -49,7 +49,8 @@ BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) { g_base->logic->event_loop()->PushCall( [] { g_base->logic->HandleInterruptSignal(); }); } else { - Log(LogLevel::kError, "SigInt handler called before g_logic exists."); + Log(LogName::kBa, LogLevel::kError, + "SigInt handler called before g_logic exists."); } return TRUE; @@ -61,7 +62,7 @@ BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) { void BasePlatformWindows::SetupInterruptHandling() { // Set up Ctrl-C handling. if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { - Log(LogLevel::kError, "Error on SetConsoleCtrlHandler()"); + Log(LogName::kBa, LogLevel::kError, "Error on SetConsoleCtrlHandler()"); } } @@ -73,8 +74,9 @@ void BasePlatformWindows::OpenDirExternally(const std::string& path) { core::CorePlatformWindows::UTF8Decode(path).c_str(), nullptr, SW_SHOWNORMAL)); if (r <= 32) { - Log(LogLevel::kError, "Error " + std::to_string(r) - + " on open_dir_externally for '" + path + "'"); + Log(LogName::kBa, LogLevel::kError, + "Error " + std::to_string(r) + " on open_dir_externally for '" + path + + "'"); } } @@ -84,8 +86,9 @@ void BasePlatformWindows::OpenFileExternally(const std::string& path) { core::CorePlatformWindows::UTF8Decode(path).c_str(), nullptr, SW_SHOWNORMAL)); if (r <= 32) { - Log(LogLevel::kError, "Error " + std::to_string(r) - + " on open_file_externally for '" + path + "'"); + Log(LogName::kBa, LogLevel::kError, + "Error " + std::to_string(r) + " on open_file_externally for '" + path + + "'"); } } diff --git a/src/ballistica/base/python/base_python.cc b/src/ballistica/base/python/base_python.cc index 8afe4a06..edaf6297 100644 --- a/src/ballistica/base/python/base_python.cc +++ b/src/ballistica/base/python/base_python.cc @@ -106,27 +106,8 @@ void BasePython::SoftImportClassic() { } } -// void BasePython::SoftImportUIV1() { -// // To keep our init order clean, we want to root out any attempted uses -// // of this before _babase/babase has been fully imported. -// assert(g_base); -// assert(g_base->IsBaseCompletelyImported()); - -// auto gil{Python::ScopedInterpreterLock()}; -// auto result = PythonRef::StolenSoft(PyImport_ImportModule("_bauiv1")); -// if (!result.Exists()) { -// // Ignore any errors here for now. All that will matter is whether plus -// // gave us its interface. -// PyErr_Clear(); -// } -// } - -void BasePython::ReadConfig() { - auto gil{Python::ScopedInterpreterLock()}; - // Read the config file and store the config dict for easy access. - objs().Get(ObjID::kAppReadConfigCall).Call(); - objs_.Store(ObjID::kConfig, *objs().Get(ObjID::kApp).GetAttr("config")); - assert(PyDict_Check(*objs().Get(ObjID::kConfig))); +void BasePython::SetConfig(PyObject* config) { + objs_.Store(ObjID::kConfig, config); } void BasePython::Reset() { @@ -360,7 +341,7 @@ auto BasePython::GetRawConfigValue(const char* name, float default_value) try { return Python::GetPyFloat(value); } catch (const std::exception&) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "expected a float for config value '" + std::string(name) + "'"); return default_value; } @@ -382,7 +363,7 @@ auto BasePython::GetRawConfigValue(const char* name, } return Python::GetPyFloat(value); } catch (const std::exception&) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "expected a float for config value '" + std::string(name) + "'"); return default_value; } @@ -399,7 +380,7 @@ auto BasePython::GetRawConfigValue(const char* name, int default_value) -> int { try { return static_cast_check_fit(Python::GetPyInt64(value)); } catch (const std::exception&) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Expected an int value for config value '" + std::string(name) + "'."); return default_value; } @@ -417,7 +398,7 @@ auto BasePython::GetRawConfigValue(const char* name, bool default_value) try { return Python::GetPyBool(value); } catch (const std::exception&) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Expected a bool value for config value '" + std::string(name) + "'."); return default_value; } @@ -525,7 +506,7 @@ auto BasePython::GetResource(const char* key, const char* fallback_resource, try { return g_base->python->GetPyLString(results.Get()); } catch (const std::exception&) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "GetResource failed for '" + std::string(key) + "'"); // Hmm; I guess let's just return the key to help identify/fix the @@ -533,7 +514,8 @@ auto BasePython::GetResource(const char* key, const char* fallback_resource, return std::string(""; } } else { - Log(LogLevel::kError, "GetResource failed for '" + std::string(key) + "'"); + Log(LogName::kBa, LogLevel::kError, + "GetResource failed for '" + std::string(key) + "'"); } // Hmm; I guess let's just return the key to help identify/fix the issue?.. @@ -553,12 +535,12 @@ auto BasePython::GetTranslation(const char* category, const char* s) try { return g_base->python->GetPyLString(results.Get()); } catch (const std::exception&) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "GetTranslation failed for '" + std::string(category) + "'"); return ""; } } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "GetTranslation failed for category '" + std::string(category) + "'"); } return ""; @@ -574,7 +556,7 @@ void BasePython::RunDeepLink(const std::string& url) { .Get(base::BasePython::ObjID::kAppHandleDeepLinkCall) .Call(args); } else { - Log(LogLevel::kError, "Error on deep-link call"); + Log(LogName::kBa, LogLevel::kError, "Error on deep-link call"); } } @@ -595,7 +577,8 @@ auto BasePython::CanPyStringEditAdapterBeReplaced(PyObject* o) -> bool { .Get(BasePython::ObjID::kStringEditAdapterCanBeReplacedCall) .Call(args); if (!result.Exists()) { - Log(LogLevel::kError, "Error getting StringEdit valid state."); + Log(LogName::kBa, LogLevel::kError, + "Error getting StringEdit valid state."); return false; } if (result.Get() == Py_True) { @@ -604,7 +587,8 @@ auto BasePython::CanPyStringEditAdapterBeReplaced(PyObject* o) -> bool { if (result.Get() == Py_False) { return false; } - Log(LogLevel::kError, "Got unexpected value for StringEdit valid."); + Log(LogName::kBa, LogLevel::kError, + "Got unexpected value for StringEdit valid."); return false; } diff --git a/src/ballistica/base/python/base_python.h b/src/ballistica/base/python/base_python.h index 289e975e..e334e5c2 100644 --- a/src/ballistica/base/python/base_python.h +++ b/src/ballistica/base/python/base_python.h @@ -75,7 +75,6 @@ class BasePython { kEmptyCall, kPrintTraceCall, kToggleFullscreenCall, - kAppReadConfigCall, kUIRemotePressCall, kRemoveInGameAdsMessageCall, kAppOnNativeStartCall, @@ -124,7 +123,8 @@ class BasePython { void AddPythonClasses(PyObject* module); void ImportPythonObjs(); void ImportPythonAppObjs(); - void ReadConfig(); + void SetConfig(PyObject* config); + // void ReadConfig(); const auto& objs() { return objs_; } diff --git a/src/ballistica/base/python/methods/python_methods_base_1.cc b/src/ballistica/base/python/methods/python_methods_base_1.cc index 98d77428..edbb1d06 100644 --- a/src/ballistica/base/python/methods/python_methods_base_1.cc +++ b/src/ballistica/base/python/methods/python_methods_base_1.cc @@ -275,7 +275,7 @@ static auto PyPushCall(PyObject* self, PyObject* args, PyObject* keywds) // Warn the user not to use this from the logic thread since it doesnt // save/restore context. if (!suppress_warning && g_base->InLogicThread()) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "babase.pushcall() called from the logic thread with " "from_other_thread set to true (call " + Python::ObjToString(call_obj) + " at " @@ -802,13 +802,15 @@ static PyMethodDef PyEnvDef = { static auto PyEmitLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; - static const char* kwlist[] = {"name", "level", "message", nullptr}; + static const char* kwlist[] = {"name", "level", "timestamp", "message", + nullptr}; const char* name; const char* levelstr; const char* message; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss", + double timestamp; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "ssds", const_cast(kwlist), &name, &levelstr, - &message)) { + ×tamp, &message)) { return nullptr; } @@ -829,7 +831,7 @@ static auto PyEmitLog(PyObject* self, PyObject* args, PyObject* keywds) fprintf(stderr, "Invalid log level to emit_log(): %s\n", levelstr); level = LogLevel::kInfo; } - Logging::EmitLog(name, level, message); + Logging::EmitLog(name, level, timestamp, message); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -840,7 +842,8 @@ static PyMethodDef PyEmitLogDef = { (PyCFunction)PyEmitLog, // method METH_VARARGS | METH_KEYWORDS, // flags - "emit_log(name: str, level: str, message: str) -> None\n" + "emit_log(name: str, level: str, timestamp: float, message: str)" + " -> None\n" "\n" "(internal)\n" "\n" diff --git a/src/ballistica/base/python/methods/python_methods_base_2.cc b/src/ballistica/base/python/methods/python_methods_base_2.cc index 6f2d6040..0bdd48cb 100644 --- a/src/ballistica/base/python/methods/python_methods_base_2.cc +++ b/src/ballistica/base/python/methods/python_methods_base_2.cc @@ -190,7 +190,7 @@ static auto PyScreenMessage(PyObject* self, PyObject* args, PyObject* keywds) color = BasePython::GetPyVector3f(color_obj); } if (log) { - Log(LogLevel::kInfo, message_str); + Log(LogName::kBa, LogLevel::kInfo, message_str); } // This version simply displays it locally. diff --git a/src/ballistica/base/python/methods/python_methods_base_3.cc b/src/ballistica/base/python/methods/python_methods_base_3.cc index e4b6afa8..462c80d7 100644 --- a/src/ballistica/base/python/methods/python_methods_base_3.cc +++ b/src/ballistica/base/python/methods/python_methods_base_3.cc @@ -110,7 +110,7 @@ static auto PySetUIScale(PyObject* self, PyObject* args, PyObject* keywds) const char* scalestr; static const char* kwlist[] = {"scale", nullptr}; - PyObject* input_device_id_obj = Py_None; + // PyObject* input_device_id_obj = Py_None; if (!PyArg_ParseTupleAndKeywords(args, keywds, "s", const_cast(kwlist), &scalestr)) { return nullptr; @@ -328,7 +328,8 @@ static auto PySetUpSigInt(PyObject* self) -> PyObject* { if (g_base) { g_base->platform->SetupInterruptHandling(); } else { - Log(LogLevel::kError, "setup_sigint called before g_base exists."); + Log(LogName::kBa, LogLevel::kError, + "setup_sigint called before g_base exists."); } Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -1578,13 +1579,14 @@ static auto PyDevConsoleAddButton(PyObject* self, PyObject* args) -> PyObject* { float label_scale; float corner_radius; const char* style; - if (!PyArg_ParseTuple(args, "sffffOsffs", &label, &x, &y, &width, &height, - &call, &h_anchor, &label_scale, &corner_radius, - &style)) { + int disabled; + if (!PyArg_ParseTuple(args, "sffffOsffsp", &label, &x, &y, &width, &height, + &call, &h_anchor, &label_scale, &corner_radius, &style, + &disabled)) { return nullptr; } dev_console->AddButton(label, x, y, width, height, call, h_anchor, - label_scale, corner_radius, style); + label_scale, corner_radius, style, disabled); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -1605,6 +1607,7 @@ static PyMethodDef PyDevConsoleAddButtonDef = { " label_scale: float,\n" " corner_radius: float,\n" " style: str,\n" + " disabled: bool,\n" ") -> None\n" "\n" "(internal)", @@ -1970,6 +1973,26 @@ static PyMethodDef PyGetDrawUIBoundsDef = { "(internal)", }; +// -------------------------- get_initial_app_config --------------------------- + +static auto PyGetInitialAppConfig(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + + return g_core->HandOverInitialAppConfig(); + + BA_PYTHON_CATCH; +} + +static PyMethodDef PyGetInitialAppConfigDef = { + "get_initial_app_config", // name + (PyCFunction)PyGetInitialAppConfig, // method + METH_NOARGS, // flags + + "get_initial_app_config() -> dict\n" + "\n" + "(internal)", +}; + // --------------------------- set_draw_ui_bounds ----------------------------- static auto PySetDrawUIBounds(PyObject* self, PyObject* args, PyObject* keywds) @@ -2020,6 +2043,53 @@ static PyMethodDef PyPushBackPressDef = { "(internal)", }; +// ---------------------------- set_app_config --------------------------------- + +static auto PySetAppConfig(PyObject* self, PyObject* args) -> PyObject* { + BA_PYTHON_TRY; + PyObject* config_obj; + if (!PyArg_ParseTuple(args, "O", &config_obj)) { + return nullptr; + } + BA_PRECONDITION(PyDict_Check(config_obj)); + g_base->python->SetConfig(config_obj); + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PySetAppConfigDef = { + "set_app_config", // name + PySetAppConfig, // method + METH_VARARGS, // flags + + "set_app_config(config: dict) -> None\n" + "\n" + "(internal)", +}; + +// --------------------- update_internal_logger_levels ------------------------- + +static auto PyUpdateInternalLoggerLevels(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + g_core->UpdateInternalLoggerLevels(); + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyUpdateInternalLoggerLevelsDef = { + "update_internal_logger_levels", // name + (PyCFunction)PyUpdateInternalLoggerLevels, // method + METH_NOARGS, // flags + + "update_internal_logger_levels() -> None\n" + "\n" + "Update the native layer to re-cache Python logger levels.\n" + "\n" + "The native layer caches logger levels so it can efficiently\n" + "avoid making Python log calls for disabled logger levels. If any\n" + "logger levels are changed at runtime, call this method after to\n" + "instruct the native layer to regenerate its cache so the change\n" + "is properly reflected in logs originating from the native layer."}; // ----------------------------------------------------------------------------- auto PythonMoethodsBase3::GetMethods() -> std::vector { @@ -2095,6 +2165,9 @@ auto PythonMoethodsBase3::GetMethods() -> std::vector { PyPushBackPressDef, PyGetDrawUIBoundsDef, PySetDrawUIBoundsDef, + PyGetInitialAppConfigDef, + PySetAppConfigDef, + PyUpdateInternalLoggerLevelsDef, }; } diff --git a/src/ballistica/base/ui/dev_console.cc b/src/ballistica/base/ui/dev_console.cc index ad4beb7c..5066ebc7 100644 --- a/src/ballistica/base/ui/dev_console.cc +++ b/src/ballistica/base/ui/dev_console.cc @@ -24,12 +24,14 @@ #include "ballistica/base/ui/ui.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/generic/utils.h" +#include "ballistica/shared/math/vector4f.h" #include "ballistica/shared/python/python_command.h" namespace ballistica::base { // How much of the screen the console covers when it is at full size. -const float kDevConsoleSize{0.9f}; +const float kDevConsoleFullSizeCoverage{0.9f}; +const float kDevConsoleMiniSize{100.0f}; const int kDevConsoleLineLimit{80}; const int kDevConsoleStringBreakUpSize{1950}; const float kDevConsoleTabButtonCornerRadius{16.0f}; @@ -99,32 +101,32 @@ static auto IsValidHungryChar_(uint32_t this_char) -> bool { || (this_char >= 48 && this_char <= 57) || this_char == '_'); } -static void DrawRect(RenderPass* pass, Mesh* mesh, float bottom, float x, - float y, float width, float height, - const Vector3f& bgcolor) { +static void DrawRect(RenderPass* pass, Mesh* mesh, float x, float y, + float width, float height, const Vector3f& bgcolor, + float alpha = 1.0f) { SimpleComponent c(pass); c.SetTransparent(true); - c.SetColor(bgcolor.x, bgcolor.y, bgcolor.z, 1.0f); + c.SetColor(bgcolor.x, bgcolor.y, bgcolor.z, alpha); c.SetTexture(g_base->assets->SysTexture(SysTextureID::kCircle)); // Draw mesh bg. if (mesh) { auto xf = c.ScopedTransform(); - c.Translate(x, y + bottom, kDevConsoleZDepth); + c.Translate(x, y, kDevConsoleZDepth); c.DrawMesh(mesh); } } -static void DrawText(RenderPass* pass, TextGroup* tgrp, float tscale, - float bottom, float x, float y, const Vector3f& fgcolor) { +static void DrawText(RenderPass* pass, TextGroup* tgrp, float tscale, float x, + float y, const Vector3f& fgcolor, float alpha = 1.0f) { SimpleComponent c(pass); c.SetTransparent(true); // Draw text. { auto xf = c.ScopedTransform(); - c.Translate(x, y + bottom, kDevConsoleZDepth); + c.Translate(x, y, kDevConsoleZDepth); c.Scale(tscale, tscale, 1.0f); int elem_count = tgrp->GetElementCount(); - c.SetColor(fgcolor.x, fgcolor.y, fgcolor.z, 1.0f); + c.SetColor(fgcolor.x, fgcolor.y, fgcolor.z, alpha); c.SetFlatness(1.0f); for (int e = 0; e < elem_count; e++) { c.SetTexture(tgrp->GetElementTexture(e)); @@ -186,7 +188,8 @@ class DevConsole::Text_ : public DevConsole::Widget_ { void Draw(RenderPass* pass, float bottom) override { auto fgcolor = Vector3f{0.8f, 0.7f, 0.8f}; - DrawText(pass, &text_group, scale, bottom, x + XOffs(h_attach), y, fgcolor); + DrawText(pass, &text_group, scale, x + XOffs(h_attach), bottom + y, + fgcolor); } }; @@ -203,11 +206,12 @@ class DevConsole::Button_ : public DevConsole::Widget_ { TextGroup text_group; float text_scale; DevButtonStyle_ style; + bool disabled; template Button_(const std::string& label, float text_scale, DevConsoleHAnchor_ attach, float x, float y, float width, float height, float corner_radius, - DevButtonStyle_ style, const F& lambda) + DevButtonStyle_ style, bool disabled, const F& lambda) : attach{attach}, x{x}, y{y}, @@ -216,6 +220,7 @@ class DevConsole::Button_ : public DevConsole::Widget_ { call{NewLambdaRunnable(lambda)}, text_scale{text_scale}, style{style}, + disabled{disabled}, mesh(0.0f, 0.0f, 0.0f, width, height, NinePatchMesh::BorderForRadius(corner_radius, width, height), NinePatchMesh::BorderForRadius(corner_radius, height, width), @@ -232,7 +237,9 @@ class DevConsole::Button_ : public DevConsole::Widget_ { auto HandleMouseDown(float mx, float my) -> bool override { if (InUs(mx, my)) { - pressed = true; + if (!disabled) { + pressed = true; + } return true; } return false; @@ -266,9 +273,11 @@ class DevConsole::Button_ : public DevConsole::Widget_ { bgcolor = pressed ? Vector3f{0.8f, 0.7f, 0.8f} : Vector3f{0.25, 0.2f, 0.3f}; } - DrawRect(pass, &mesh, bottom, x + XOffs(attach), y, width, height, bgcolor); - DrawText(pass, &text_group, text_scale, bottom, - x + XOffs(attach) + width * 0.5f, y + height * 0.5f, fgcolor); + float alpha = disabled ? 0.3f : 1.0f; + DrawRect(pass, &mesh, x + XOffs(attach), bottom + y, width, height, bgcolor, + alpha); + DrawText(pass, &text_group, text_scale, x + XOffs(attach) + width * 0.5f, + bottom + y + height * 0.5f, fgcolor, alpha); } }; @@ -336,12 +345,12 @@ class DevConsole::ToggleButton_ : public DevConsole::Widget_ { } void Draw(RenderPass* pass, float bottom) override { - DrawRect(pass, &mesh, bottom, x + XOffs(attach), y, width, height, + DrawRect(pass, &mesh, x + XOffs(attach), bottom + y, width, height, pressed ? Vector3f{0.5f, 0.2f, 1.0f} : on ? Vector3f{0.5f, 0.4f, 0.6f} : Vector3f{0.25, 0.2f, 0.3f}); - DrawText(pass, &text_group, text_scale, bottom, - x + XOffs(attach) + width * 0.5f, y + height * 0.5f, + DrawText(pass, &text_group, text_scale, x + XOffs(attach) + width * 0.5f, + bottom + y + height * 0.5f, pressed ? Vector3f{1.0f, 1.0f, 1.0f} : on ? Vector3f{1.0f, 1.0f, 1.0f} : Vector3f{0.8f, 0.7f, 0.8f}); @@ -417,12 +426,12 @@ class DevConsole::TabButton_ : public DevConsole::Widget_ { } void Draw(RenderPass* pass, float bottom) override { - DrawRect(pass, &mesh, bottom, x + XOffs(attach), y, width, height, + DrawRect(pass, &mesh, x + XOffs(attach), bottom + y, width, height, pressed ? Vector3f{0.4f, 0.2f, 0.8f} : selected ? Vector3f{0.4f, 0.3f, 0.4f} : Vector3f{0.25, 0.2f, 0.3f}); - DrawText(pass, &text_group, text_scale, bottom, - x + XOffs(attach) + width * 0.5f, y + height * 0.5f, + DrawText(pass, &text_group, text_scale, x + XOffs(attach) + width * 0.5f, + bottom + y + height * 0.5f, pressed ? Vector3f{1.0f, 1.0f, 1.0f} : selected ? Vector3f{1.0f, 1.0f, 1.0f} : Vector3f{0.6f, 0.5f, 0.6f}); @@ -431,10 +440,16 @@ class DevConsole::TabButton_ : public DevConsole::Widget_ { class DevConsole::OutputLine_ { public: - OutputLine_(std::string s_in, double c) - : creation_time(c), s(std::move(s_in)) {} - double creation_time; + OutputLine_(std::string s_in, double creation_time, float scale, + Vector4f color) + : creation_time(creation_time), + s(std::move(s_in)), + scale(scale), + color(color) {} std::string s; + double creation_time; + float scale; + Vector4f color; auto GetText() -> TextGroup& { if (!s_mesh_.Exists()) { s_mesh_ = Object::New(); @@ -555,7 +570,8 @@ void DevConsole::AddText(const char* text, float x, float y, void DevConsole::AddButton(const char* label, float x, float y, float width, float height, PyObject* call, const char* h_anchor_str, float label_scale, - float corner_radius, const char* style_str) { + float corner_radius, const char* style_str, + bool disabled) { assert(g_base->InLogicThread()); auto style = DevButtonStyleFromStr_(style_str); @@ -563,7 +579,7 @@ void DevConsole::AddButton(const char* label, float x, float y, float width, widgets_.emplace_back(std::make_unique( label, label_scale, h_anchor, x, y, width, height, corner_radius, style, - [this, callref = PythonRef::Acquired(call)] { + disabled, [this, callref = PythonRef::Acquired(call)] { if (callref.Get() != Py_None) { callref.Call(); } @@ -574,7 +590,7 @@ void DevConsole::AddPythonTerminal() { float bs = BaseScale(); widgets_.emplace_back(std::make_unique( "Exec", 0.5f * bs, DevConsoleHAnchor_::kRight, -33.0f * bs, 15.95f * bs, - 32.0f * bs, 13.0f * bs, 2.0 * bs, DevButtonStyle_::kNormal, + 32.0f * bs, 13.0f * bs, 2.0 * bs, DevButtonStyle_::kNormal, false, [this] { Exec(); })); python_terminal_visible_ = true; } @@ -632,7 +648,11 @@ auto DevConsole::Width() -> float { } auto DevConsole::Height() -> float { - return g_base->graphics->screen_virtual_height() - Bottom_(); + if (state_ == State_::kMini) { + return kDevConsoleMiniSize; + } + return g_base->graphics->screen_virtual_height() + * kDevConsoleFullSizeCoverage; } void DevConsole::HandleMouseUp(int button, float x, float y) { @@ -678,7 +698,7 @@ void DevConsole::InvokeStringEditor_() { .Get(BasePython::ObjID::kDevConsoleStringEditAdapterClass) .Call(); if (!result.Exists()) { - Log(LogLevel::kError, "Error invoking string edit dialog."); + Log(LogName::kBa, LogLevel::kError, "Error invoking string edit dialog."); return; } @@ -1037,12 +1057,11 @@ auto DevConsole::HandleKeyRelease(const SDL_Keysym* keysym) -> bool { void DevConsole::Exec() { BA_PRECONDITION(g_base->InLogicThread()); if (!input_enabled_) { - Log(LogLevel::kWarning, "Console input is not allowed yet."); + Log(LogName::kBa, LogLevel::kWarning, "Console input is not allowed yet."); return; } input_history_position_ = 0; if (input_string_ == "clear") { - last_line_.clear(); output_lines_.clear(); } else { SubmitPythonCommand_(input_string_); @@ -1077,7 +1096,7 @@ void DevConsole::SubmitPythonCommand_(const std::string& command) { if (cmd.CanEval()) { auto obj = cmd.Eval(true, nullptr, nullptr); if (obj.Exists() && obj.Get() != Py_None) { - Print(obj.Repr() + "\n"); + Print(obj.Repr(), 1.0f, kVector4f1); } } else { // Not eval-able; just exec it. @@ -1129,23 +1148,21 @@ void DevConsole::ToggleState() { transition_start_ = g_base->logic->display_time(); } -void DevConsole::Print(const std::string& s_in) { +void DevConsole::Print(const std::string& s_in, float scale, Vector4f color) { assert(g_base->InLogicThread()); std::string s = Utils::GetValidUTF8(s_in.c_str(), "cspr"); - last_line_ += s; std::vector broken_up; g_base->text_graphics->BreakUpString( - last_line_.c_str(), kDevConsoleStringBreakUpSize, &broken_up); + s.c_str(), kDevConsoleStringBreakUpSize / scale, &broken_up); - // Spit out all completed lines and keep the last one as lastline. - for (size_t i = 0; i < broken_up.size() - 1; i++) { - output_lines_.emplace_back(broken_up[i], g_base->logic->display_time()); + // Spit out all lines. + for (size_t i = 0; i < broken_up.size(); i++) { + output_lines_.emplace_back(broken_up[i], g_base->logic->display_time(), + scale, color); if (output_lines_.size() > kDevConsoleLineLimit) { output_lines_.pop_front(); } } - last_line_ = broken_up[broken_up.size() - 1]; - last_line_mesh_dirty_ = true; } auto DevConsole::Bottom_() const -> float { @@ -1160,30 +1177,29 @@ auto DevConsole::Bottom_() const -> float { // dev-consoles are not meant to be especially pretty and I think it is // more important for them to be able to be written to a known hard-coded // mini-size. - float mini_size = 100.0f; - + // // Now that we have tabs and drop-shadows hanging down, we have to // overshoot the top of the screen when transitioning out. float top_buffer = 100.0f; if (state_ == State_::kMini) { - bottom = vh - mini_size; + bottom = vh - kDevConsoleMiniSize; } else { - bottom = vh - vh * kDevConsoleSize; + bottom = vh * (1.0f - kDevConsoleFullSizeCoverage); } if (g_base->logic->display_time() - transition_start_ < kTransitionSeconds) { float from_height; if (state_prev_ == State_::kMini) { - from_height = vh - mini_size; + from_height = vh - kDevConsoleMiniSize; } else if (state_prev_ == State_::kFull) { - from_height = vh - vh * kDevConsoleSize; + from_height = vh - vh * kDevConsoleFullSizeCoverage; } else { from_height = vh + top_buffer; } float to_height; if (state_ == State_::kMini) { - to_height = vh - mini_size; + to_height = vh - kDevConsoleMiniSize; } else if (state_ == State_::kFull) { - to_height = vh - vh * kDevConsoleSize; + to_height = vh - vh * kDevConsoleFullSizeCoverage; } else { to_height = vh + top_buffer; } @@ -1205,6 +1221,31 @@ void DevConsole::Draw(FrameDef* frame_def) { return; } + // If the virtual screen size has changed, refresh. + auto screen_virtual_width{g_base->graphics->screen_virtual_width()}; + auto screen_virtual_height{g_base->graphics->screen_virtual_height()}; + + if (last_virtual_res_x_ < 0.0f) { + // First time through, just grab current value; don't refresh. + last_virtual_res_x_ = screen_virtual_width; + last_virtual_res_y_ = screen_virtual_height; + } else { + // Otherwise if virtual res changed and its been long enough, refresh. + auto display_time{g_base->logic->display_time()}; + double update_interval{0.2}; + if (display_time > last_virtual_res_change_time_ + update_interval + && (last_virtual_res_x_ != screen_virtual_width + || last_virtual_res_y_ != screen_virtual_height)) { + last_virtual_res_x_ = screen_virtual_width; + last_virtual_res_y_ = screen_virtual_height; + last_virtual_res_change_time_ = display_time; + g_base->logic->event_loop()->PushCall([this] { + RefreshTabButtons_(); + RefreshTabContents_(); + }); + } + } + float bottom = Bottom_(); float border_height{3.0f}; @@ -1355,7 +1396,6 @@ void DevConsole::Draw(FrameDef* frame_def) { { SimpleComponent c(pass); c.SetTransparent(true); - c.SetColor(1, 1, 1, 1); c.SetFlatness(1.0f); float draw_scale = 0.64f; float v_inc = 18.0f; @@ -1363,38 +1403,19 @@ void DevConsole::Draw(FrameDef* frame_def) { * (g_base->graphics->screen_virtual_width() - (kDevConsoleStringBreakUpSize * draw_scale)); float v = bottom + 32.0f * bs; - if (!last_line_.empty()) { - if (last_line_mesh_dirty_) { - if (!last_line_mesh_group_.Exists()) { - last_line_mesh_group_ = Object::New(); - } - last_line_mesh_group_->SetText(last_line_); - last_line_mesh_dirty_ = false; - } - int elem_count = last_line_mesh_group_->GetElementCount(); - for (int e = 0; e < elem_count; e++) { - c.SetTexture(last_line_mesh_group_->GetElementTexture(e)); - { - auto xf = c.ScopedTransform(); - c.Translate(h, v + 2, kDevConsoleZDepth); - c.Scale(draw_scale, draw_scale); - c.DrawMesh(last_line_mesh_group_->GetElementMesh(e)); - } - } - v += v_inc; - } for (auto i = output_lines_.rbegin(); i != output_lines_.rend(); i++) { int elem_count = i->GetText().GetElementCount(); for (int e = 0; e < elem_count; e++) { + c.SetColor(i->color.x, i->color.y, i->color.z, i->color.a); c.SetTexture(i->GetText().GetElementTexture(e)); { auto xf = c.ScopedTransform(); c.Translate(h, v + 2, kDevConsoleZDepth); - c.Scale(draw_scale, draw_scale); + c.Scale(draw_scale * i->scale, draw_scale * i->scale); c.DrawMesh(i->GetText().GetElementMesh(e)); } } - v += v_inc; + v += v_inc * i->scale; if (v > pass->virtual_height() + v_inc) { break; } diff --git a/src/ballistica/base/ui/dev_console.h b/src/ballistica/base/ui/dev_console.h index 221cfc71..ca7b5f18 100644 --- a/src/ballistica/base/ui/dev_console.h +++ b/src/ballistica/base/ui/dev_console.h @@ -10,6 +10,7 @@ #include "ballistica/base/graphics/mesh/image_mesh.h" #include "ballistica/base/graphics/text/text_group.h" #include "ballistica/shared/foundation/object.h" +#include "ballistica/shared/math/vector4f.h" #include "ballistica/shared/python/python_ref.h" namespace ballistica::base { @@ -36,7 +37,7 @@ class DevConsole { auto PasteFromClipboard() -> bool; /// Print text to the console. - void Print(const std::string& s_in); + void Print(const std::string& s_in, float scale, Vector4f color); void Draw(FrameDef* frame_def); void StepDisplayTime(); @@ -58,7 +59,7 @@ class DevConsole { void AddButton(const char* label, float x, float y, float width, float height, PyObject* call, const char* h_anchor_str, float label_scale, - float corner_radius, const char* style_str); + float corner_radius, const char* style_str, bool disabled); void AddText(const char* text, float x, float y, const char* h_anchor_str, const char* h_align_str, const char* v_align_str, float scale); void AddPythonTerminal(); @@ -96,12 +97,14 @@ class DevConsole { State_ state_prev_{State_::kInactive}; bool input_text_dirty_{true}; bool input_enabled_{}; - bool last_line_mesh_dirty_{true}; bool python_terminal_visible_{}; bool python_terminal_pressed_{}; bool refresh_pending_{}; bool carat_dirty_{true}; float carat_x_{}; + float last_virtual_res_x_{-1.0f}; + float last_virtual_res_y_{-1.0f}; + seconds_t last_virtual_res_change_time_{}; seconds_t transition_start_{}; millisecs_t last_carat_x_change_time_{}; ImageMesh bg_mesh_; @@ -111,7 +114,6 @@ class DevConsole { TextGroup title_text_group_; TextGroup prompt_text_group_; TextGroup input_text_group_; - std::string last_line_; std::string input_string_; std::list tabs_; std::string active_tab_; @@ -120,7 +122,6 @@ class DevConsole { std::list output_lines_; std::vector > widgets_; std::vector > tab_buttons_; - Object::Ref last_line_mesh_group_; Object::Ref key_repeater_; Object::Ref carat_mesh_; Object::Ref carat_glow_mesh_; diff --git a/src/ballistica/base/ui/ui.cc b/src/ballistica/base/ui/ui.cc index b3195075..f0ce894c 100644 --- a/src/ballistica/base/ui/ui.cc +++ b/src/ballistica/base/ui/ui.cc @@ -18,6 +18,7 @@ #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/foundation/macros.h" #include "ballistica/shared/generic/utils.h" +#include "ballistica/shared/math/vector4f.h" namespace ballistica::base { @@ -165,13 +166,13 @@ void UI::OnAppStart() { if (force_scale_) { if (scale_ == UIScale::kSmall) { ScreenMessage("FORCING SMALL UI FOR TESTING", Vector3f(1, 0, 0)); - Log(LogLevel::kInfo, "FORCING SMALL UI FOR TESTING"); + Log(LogName::kBa, LogLevel::kInfo, "FORCING SMALL UI FOR TESTING"); } else if (scale_ == UIScale::kMedium) { ScreenMessage("FORCING MEDIUM UI FOR TESTING", Vector3f(1, 0, 0)); - Log(LogLevel::kInfo, "FORCING MEDIUM UI FOR TESTING"); + Log(LogName::kBa, LogLevel::kInfo, "FORCING MEDIUM UI FOR TESTING"); } else if (scale_ == UIScale::kLarge) { ScreenMessage("FORCING LARGE UI FOR TESTING", Vector3f(1, 0, 0)); - Log(LogLevel::kInfo, "FORCING LARGE UI FOR TESTING"); + Log(LogName::kBa, LogLevel::kInfo, "FORCING LARGE UI FOR TESTING"); } else { FatalError("Unhandled scale."); } @@ -576,7 +577,8 @@ void UI::ShowURL(const std::string& url) { g_base->logic->event_loop()->PushCall( [ui_delegate, url] { ui_delegate->DoShowURL(url); }); } else { - Log(LogLevel::kWarning, "UI::ShowURL called without ui_delegate present."); + Log(LogName::kBa, LogLevel::kWarning, + "UI::ShowURL called without ui_delegate present."); } } @@ -621,7 +623,8 @@ void UI::SetUIDelegate(base::UIDelegateInterface* delegate) { } } -void UI::PushDevConsolePrintCall(const std::string& msg) { +void UI::PushDevConsolePrintCall(const std::string& msg, float scale, + Vector4f color) { // Completely ignore this stuff in headless mode. if (g_core->HeadlessMode()) { return; @@ -630,12 +633,14 @@ void UI::PushDevConsolePrintCall(const std::string& msg) { // printed. Otherwise store it for the console to grab when it's ready. if (auto* event_loop = g_base->logic->event_loop()) { if (dev_console_ != nullptr) { - event_loop->PushCall([this, msg] { dev_console_->Print(msg); }); + event_loop->PushCall([this, msg, scale, color] { + dev_console_->Print(msg, scale, color); + }); return; } } // Didn't send a print; store for later. - dev_console_startup_messages_ += msg; + dev_console_startup_messages_.emplace_back(msg, scale, color); } void UI::OnAssetsAvailable() { @@ -648,7 +653,10 @@ void UI::OnAssetsAvailable() { // Print any messages that have built up. if (!dev_console_startup_messages_.empty()) { - dev_console_->Print(dev_console_startup_messages_); + for (auto&& entry : dev_console_startup_messages_) { + dev_console_->Print(std::get<0>(entry), std::get<1>(entry), + std::get<2>(entry)); + } dev_console_startup_messages_.clear(); } } diff --git a/src/ballistica/base/ui/ui.h b/src/ballistica/base/ui/ui.h index 28567f41..9453288f 100644 --- a/src/ballistica/base/ui/ui.h +++ b/src/ballistica/base/ui/ui.h @@ -3,11 +3,13 @@ #ifndef BALLISTICA_BASE_UI_UI_H_ #define BALLISTICA_BASE_UI_UI_H_ +#include #include #include #include "ballistica/base/graphics/support/frame_def.h" #include "ballistica/base/ui/widget_message.h" +#include "ballistica/shared/math/vector4f.h" // Predeclare a few things from ui_v1. namespace ballistica::ui_v1 { @@ -123,7 +125,8 @@ class UI { auto* dev_console() const { return dev_console_; } - void PushDevConsolePrintCall(const std::string& msg); + void PushDevConsolePrintCall(const std::string& msg, float scale, + Vector4f color); auto* delegate() const { return delegate_; } @@ -154,7 +157,8 @@ class UI { OperationContext* operation_context_{}; base::UIDelegateInterface* delegate_{}; DevConsole* dev_console_{}; - std::string dev_console_startup_messages_; + std::list> + dev_console_startup_messages_; millisecs_t last_input_device_use_time_{}; millisecs_t last_widget_input_reject_err_sound_time_{}; UIScale scale_{UIScale::kLarge}; diff --git a/src/ballistica/classic/classic.cc b/src/ballistica/classic/classic.cc index 31e50172..2476a6e1 100644 --- a/src/ballistica/classic/classic.cc +++ b/src/ballistica/classic/classic.cc @@ -151,9 +151,9 @@ auto ClassicFeatureSet::GetV1AccountLoginStateString() -> std::string { out = "signing_in"; break; default: - Log(LogLevel::kError, "Unknown V1LoginState '" - + std::to_string(static_cast(state)) - + "'"); + Log(LogName::kBaAccount, LogLevel::kError, + "Unknown V1LoginState '" + std::to_string(static_cast(state)) + + "'"); out = "signed_out"; break; } diff --git a/src/ballistica/classic/support/classic_app_mode.cc b/src/ballistica/classic/support/classic_app_mode.cc index 90618017..e48238d7 100644 --- a/src/ballistica/classic/support/classic_app_mode.cc +++ b/src/ballistica/classic/support/classic_app_mode.cc @@ -128,9 +128,10 @@ void ClassicAppMode::Reset_() { // If all is well our sessions should all be dead at this point. if (g_scene_v1->session_count != 0) { - Log(LogLevel::kError, "SceneV1 session count is non-zero (" - + std::to_string(g_scene_v1->session_count) - + ") on ClassicAppMode::Reset_()."); + Log(LogName::kBa, LogLevel::kError, + "SceneV1 session count is non-zero (" + + std::to_string(g_scene_v1->session_count) + + ") on ClassicAppMode::Reset_()."); } // Reset the engine itself to a default state. @@ -159,15 +160,16 @@ void ClassicAppMode::HostScanCycle() { scan_socket_ = socket(AF_INET, SOCK_DGRAM, 0); if (scan_socket_ == -1) { - Log(LogLevel::kError, "Error opening scan socket: " - + g_core->platform->GetSocketErrorString() - + "."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error opening scan socket: " + + g_core->platform->GetSocketErrorString() + "."); return; } // Since this guy lives in the logic-thread we need it to not block. if (!g_core->platform->SetSocketNonBlocking(scan_socket_)) { - Log(LogLevel::kError, "Error setting socket non-blocking."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error setting socket non-blocking."); g_core->platform->CloseSocket(scan_socket_); scan_socket_ = -1; return; @@ -182,7 +184,7 @@ void ClassicAppMode::HostScanCycle() { int result = ::bind(scan_socket_, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); if (result == 1) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Error binding socket: " + g_core->platform->GetSocketErrorString() + "."); g_core->platform->CloseSocket(scan_socket_); @@ -196,9 +198,9 @@ void ClassicAppMode::HostScanCycle() { sizeof(op_val)); if (result != 0) { - Log(LogLevel::kError, "Error enabling broadcast for scan-socket: " - + g_core->platform->GetSocketErrorString() - + "."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error enabling broadcast for scan-socket: " + + g_core->platform->GetSocketErrorString() + "."); g_core->platform->CloseSocket(scan_socket_); scan_socket_ = -1; return; @@ -230,8 +232,9 @@ void ClassicAppMode::HostScanCycle() { case ENETUNREACH: break; default: - Log(LogLevel::kError, "Error on scanSocket sendto: " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error on scan-socket sendto: " + + g_core->platform->GetSocketErrorString()); } } } @@ -253,8 +256,9 @@ void ClassicAppMode::HostScanCycle() { case EWOULDBLOCK: break; default: - Log(LogLevel::kError, "Error: recvfrom error: " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error: recvfrom error: " + + g_core->platform->GetSocketErrorString()); break; } break; @@ -311,11 +315,11 @@ void ClassicAppMode::HostScanCycle() { PruneScanResults_(); } } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Got invalid BA_PACKET_HOST_QUERY_RESPONSE packet"); } } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Got invalid BA_PACKET_HOST_QUERY_RESPONSE packet"); } } @@ -388,7 +392,7 @@ bool ClassicAppMode::HasConnectionToClients() const { auto ClassicAppMode::GetActiveOrWarn() -> ClassicAppMode* { auto* val{GetActive()}; if (val == nullptr) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "Attempting to access ClassicAppMode while it is inactive."); } return val; @@ -515,6 +519,16 @@ void ClassicAppMode::StepDisplayTime() { } legacy_display_time_millisecs_prev_ = legacy_display_time_millisecs_; + // Special case: due to things like app-mode-switches our + // prev-display-time-millisecs may be way in the past which + // can give us huge step values. So if this value is much bigger + // than the direct conversion of display_time_increment, clamp it. + auto milliseconds_inc_max = + static_cast(g_base->logic->display_time_increment() * 1000.0 * 1.5); + if (legacy_display_time_millisecs_inc > milliseconds_inc_max) { + legacy_display_time_millisecs_inc = milliseconds_inc_max; + } + UpdateKickVote_(); HandleQuitOnIdle_(); @@ -559,8 +573,9 @@ void ClassicAppMode::StepDisplayTime() { // Complain when our full update takes longer than 1/60th second. if (duration > (1000 / 60)) { - Log(LogLevel::kInfo, "Logic::StepDisplayTime update took too long (" - + std::to_string(duration) + " ms)."); + Log(LogName::kBa, LogLevel::kInfo, + "Logic::StepDisplayTime update took too long (" + + std::to_string(duration) + " ms)."); // Limit these if we want (not doing so for now). next_long_update_report_time_ = app_time; @@ -624,7 +639,7 @@ void ClassicAppMode::UpdateGameRoster() { for (auto&& p : hs->players()) { auto* delegate = p->input_device_delegate(); if (delegate == nullptr || !delegate->InputDeviceExists()) { - BA_LOG_ONCE(LogLevel::kWarning, + BA_LOG_ONCE(LogName::kBa, LogLevel::kWarning, "Found player with no/invalid input-device-delegate in " "UpdateGameRoster."); continue; @@ -1215,7 +1230,7 @@ void ClassicAppMode::PruneSessions_() { try { i.Clear(); } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Exception killing Session: " + std::string(e.what())); } have_dead_session = true; @@ -1403,7 +1418,8 @@ void ClassicAppMode::HandleQuitOnIdle_() { if (!idle_exiting_ && idle_seconds > (idle_exit_minutes_.value() * 60.0f)) { idle_exiting_ = true; - Log(LogLevel::kInfo, "Quitting due to reaching idle-exit-minutes."); + Log(LogName::kBa, LogLevel::kInfo, + "Quitting due to reaching idle-exit-minutes."); g_base->logic->event_loop()->PushCall([] { g_base->logic->Shutdown(); }); } } @@ -1462,7 +1478,8 @@ void ClassicAppMode::HandleGameQuery(const char* buffer, size_t size, BA_PRECONDITION_FATAL(!usid.empty()); if (usid.size() > 100) { - Log(LogLevel::kError, "had to truncate session-id; shouldn't happen"); + Log(LogName::kBa, LogLevel::kError, + "had to truncate session-id; shouldn't happen"); usid.resize(100); } if (usid.empty()) { @@ -1488,8 +1505,9 @@ void ClassicAppMode::HandleGameQuery(const char* buffer, size_t size, g_base->network_writer->PushSendToCall(msg_buffer, SockAddr(*from)); } else { - Log(LogLevel::kError, "Got invalid game-query packet of len " - + std::to_string(size) + "; expected 5."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Got invalid game-query packet of len " + std::to_string(size) + + "; expected 5."); } } diff --git a/src/ballistica/core/core.cc b/src/ballistica/core/core.cc index d6adf3f4..f6b759a6 100644 --- a/src/ballistica/core/core.cc +++ b/src/ballistica/core/core.cc @@ -10,6 +10,7 @@ #include "ballistica/core/platform/core_platform.h" #include "ballistica/core/python/core_python.h" +#include "ballistica/shared/foundation/macros.h" #include "ballistica/shared/foundation/types.h" #include "ballistica/shared/generic/runnable.h" @@ -29,14 +30,14 @@ auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* { "call."); } if (g_core == nullptr) { - DoImport(*config); + DoImport_(*config); } } else { // If no config is passed, use a default. If the user wants env vars // or anything else factored in, they should do so themselves in the // config they pass (CoreConfig::ForEnvVars(), etc.). if (g_core == nullptr) { - DoImport({}); + DoImport_({}); } } } else { @@ -57,23 +58,23 @@ auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* { // between monolithic and modular. std::vector argbuffer; std::vector argv = CorePython::FetchPythonArgs(&argbuffer); - DoImport(CoreConfig::ForArgsAndEnvVars(static_cast(argv.size()), - argv.data())); + DoImport_(CoreConfig::ForArgsAndEnvVars(static_cast(argv.size()), + argv.data())); } else { // Not using Python sys args but we still want to process env vars. - DoImport(CoreConfig::ForEnvVars()); + DoImport_(CoreConfig::ForEnvVars()); } } } return g_core; } -void CoreFeatureSet::DoImport(const CoreConfig& config) { +void CoreFeatureSet::DoImport_(const CoreConfig& config) { millisecs_t start_millisecs = CorePlatform::GetCurrentMillisecs(); assert(g_core == nullptr); g_core = new CoreFeatureSet(config); - g_core->PostInit(); + g_core->PostInit_(); // Slightly hacky: have to report our begin time after the fact since core // didn't exist before. Can at least add an offset to give an accurate @@ -97,16 +98,16 @@ CoreFeatureSet::CoreFeatureSet(CoreConfig config) assert(g_core == nullptr); } -void CoreFeatureSet::PostInit() { +void CoreFeatureSet::PostInit_() { // Some of this stuff might access g_core so we run most of our init // *after* assigning our singleton to be safe. // Should migrate this to classic. set_legacy_user_agent_string(platform->GetLegacyUserAgentString()); - RunSanityChecks(); + RunSanityChecks_(); - build_src_dir_ = CalcBuildSrcDir(); + build_src_dir_ = CalcBuildSrcDir_(); // On monolithic builds we need to bring up Python itself. if (g_buildconfig.monolithic_build()) { @@ -119,13 +120,9 @@ void CoreFeatureSet::PostInit() { // Grab whatever Python stuff we use. python->ImportPythonObjs(); - // Normally we wait until later to start pushing logs through to Python - // (so that our log handling system is fully bootstrapped), but - // technically we can push our log calls out to Python any time now since - // we grabbed the logging calls above. Do so immediately here if asked. - if (!core_config_.hold_early_logs) { - python->EnablePythonLoggingCalls(); - } + // We grabbed all our log handles/etc. above, so we can start piping logs + // through to Python now. + python->EnablePythonLoggingCalls(); } auto CoreFeatureSet::core_config() const -> const CoreConfig& { @@ -167,6 +164,16 @@ void CoreFeatureSet::ApplyBaEnvConfig() { ba_env_site_python_dir_ = envcfg.GetAttr("site_python_dir").ValueAsOptionalString(); + ba_env_launch_timestamp_ = envcfg.GetAttr("launch_time").ValueAsDouble(); + + auto appcfg = envcfg.GetAttr("initial_app_config"); + initial_app_config_ = appcfg.NewRef(); + + // This is also a reasonable time to grab initial logger levels that baenv + // likely mucked with. For any changes after this to make it to the native + // layer, babase.update_internal_logger_levels() must be called. + UpdateInternalLoggerLevels(); + // Consider app-python-dir to be 'custom' if baenv provided a value for it // AND that value differs from baenv's default. auto standard_app_python_dir = @@ -183,6 +190,10 @@ void CoreFeatureSet::ApplyBaEnvConfig() { } } +void CoreFeatureSet::UpdateInternalLoggerLevels() { + python->UpdateInternalLoggerLevels(log_levels_); +} + auto CoreFeatureSet::GetAppPythonDirectory() -> std::optional { BA_PRECONDITION(have_ba_env_vals_); return ba_env_app_python_dir_; @@ -209,7 +220,7 @@ auto CoreFeatureSet::GetSitePythonDirectory() -> std::optional { return ba_env_site_python_dir_; } -auto CoreFeatureSet::CalcBuildSrcDir() -> std::string { +auto CoreFeatureSet::CalcBuildSrcDir_() -> std::string { // Let's grab a string of the portion of __FILE__ before our project root. // We can use it to make error messages/etc. more pretty by stripping out // all but sub-project paths. @@ -217,14 +228,15 @@ auto CoreFeatureSet::CalcBuildSrcDir() -> std::string { auto* f_end = strstr(f, "src" BA_DIRSLASH "ballistica" BA_DIRSLASH "core" BA_DIRSLASH "core.cc"); if (!f_end) { - Log(LogLevel::kWarning, "Unable to calc build source dir from __FILE__."); + Log(LogName::kBa, LogLevel::kWarning, + "Unable to calc build source dir from __FILE__."); return ""; } else { return std::string(f).substr(0, f_end - f); } } -void CoreFeatureSet::RunSanityChecks() { +void CoreFeatureSet::RunSanityChecks_() { // Sanity check: make sure asserts are stripped out of release builds // (NDEBUG should do this). #if !BA_DEBUG_BUILD @@ -267,11 +279,11 @@ void CoreFeatureSet::RunSanityChecks() { // from. Use this to adjust the filtering as necessary so the resulting // type name matches what is expected. if (explicit_bool(false)) { - Log(LogLevel::kError, "static_type_name check; name is '" - + static_type_name() - + "' debug_full is '" - + static_type_name(true) + "'"); - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, + "static_type_name check; name is '" + + static_type_name() + "' debug_full is '" + + static_type_name(true) + "'"); + Log(LogName::kBa, LogLevel::kError, "static_type_name check; name is '" + static_type_name() + "' debug_full is '" + static_type_name(true) + "'"); @@ -293,13 +305,16 @@ auto CoreFeatureSet::SoftImportBase() -> BaseSoftInterface* { } void CoreFeatureSet::LifecycleLog(const char* msg, double offset_seconds) { - if (!core_config_.lifecycle_log) { + // Early out to avoid work if we won't show anyway. + if (!LogLevelEnabled(LogName::kBaLifecycle, LogLevel::kDebug)) { return; } + + // We include time-since-start as part of the message here. char buffer[128]; - snprintf(buffer, sizeof(buffer), "LIFE: %s @ %.3fs.", msg, + snprintf(buffer, sizeof(buffer), "%s @ %.3fs.", msg, g_core->GetAppTimeSeconds() + offset_seconds); - Log(LogLevel::kInfo, buffer); + Log(LogName::kBaLifecycle, LogLevel::kDebug, buffer); } auto CoreFeatureSet::HeadlessMode() -> bool { @@ -308,29 +323,27 @@ auto CoreFeatureSet::HeadlessMode() -> bool { return g_buildconfig.headless_build(); } -// auto CoreFeatureSet::vr_mode() -> bool { return core_config_.vr_mode; } - static void WaitThenDie(millisecs_t wait, const std::string& action) { CorePlatform::SleepMillisecs(wait); FatalError("Timed out waiting for " + action + "."); } auto CoreFeatureSet::GetAppTimeMillisecs() -> millisecs_t { - UpdateAppTime(); + UpdateAppTime_(); return app_time_microsecs_ / 1000; } auto CoreFeatureSet::GetAppTimeMicrosecs() -> microsecs_t { - UpdateAppTime(); + UpdateAppTime_(); return app_time_microsecs_; } auto CoreFeatureSet::GetAppTimeSeconds() -> seconds_t { - UpdateAppTime(); + UpdateAppTime_(); return static_cast(app_time_microsecs_) / 1000000; } -void CoreFeatureSet::UpdateAppTime() { +void CoreFeatureSet::UpdateAppTime_() { microsecs_t t = CorePlatform::GetCurrentMicrosecs(); // If we're at a different time than our last query, do our funky math. @@ -356,15 +369,6 @@ void CoreFeatureSet::UpdateAppTime() { } } -// void CoreFeatureSet::UpdateMainThreadID() { -// auto current_id = std::this_thread::get_id(); - -// // This gets called a lot and it may happen before we are spun up, so just -// // ignore it in that case. -// main_thread_id = current_id; -// main_event_loop_->set_thread_id(current_id); -// } - void CoreFeatureSet::StartSuicideTimer(const std::string& action, millisecs_t delay) { if (!started_suicide_) { @@ -433,4 +437,14 @@ auto CoreFeatureSet::CurrentThreadName() -> std::string { #endif } +auto CoreFeatureSet::HandOverInitialAppConfig() -> PyObject* { + BA_PRECONDITION(initial_app_config_); + + // Don't decrement the refcount on the pointer we're holding; just clear + // and return it, effectively handing over the ref. + auto* out{initial_app_config_}; + initial_app_config_ = nullptr; + return out; +} + } // namespace ballistica::core diff --git a/src/ballistica/core/core.h b/src/ballistica/core/core.h index 3c043779..bf8e177a 100644 --- a/src/ballistica/core/core.h +++ b/src/ballistica/core/core.h @@ -3,7 +3,6 @@ #ifndef BALLISTICA_CORE_CORE_H_ #define BALLISTICA_CORE_CORE_H_ -#include #include #include #include @@ -186,13 +185,36 @@ class CoreFeatureSet { void set_event_loops_suspended(bool val) { event_loops_suspended_ = val; } static auto CurrentThreadName() -> std::string; + auto HandOverInitialAppConfig() -> PyObject*; + + /// Grab current Python logging levels for all logs we use internally. If + /// any changes are made at runtime to Python logging levels that we use, + /// this should be called after. + void UpdateInternalLoggerLevels(); + + /// Check whether a certain log name/level combo will be shown. It is much + /// more efficient to gate log calls using this (especially frequent or + /// debug ones) rather than letting the Python layer do the gating. Be + /// aware, however, that UpdateInternalLoggerLevels() must be called after + /// making any changes to Python logger levels to keep this internal + /// system up to date. + auto LogLevelEnabled(LogName name, LogLevel level) -> bool { + return log_levels_[static_cast(name)] <= level; + } + + auto ba_env_launch_timestamp() { + // Make sure we set this before accessing it. + assert(ba_env_launch_timestamp_ > 0.0); + return ba_env_launch_timestamp_; + } + private: explicit CoreFeatureSet(CoreConfig config); - static void DoImport(const CoreConfig& config); - static auto CalcBuildSrcDir() -> std::string; - void RunSanityChecks(); - void UpdateAppTime(); - void PostInit(); + static void DoImport_(const CoreConfig& config); + static auto CalcBuildSrcDir_() -> std::string; + void RunSanityChecks_(); + void UpdateAppTime_(); + void PostInit_(); // Note to self: don't use single bits for these as they may be owned by // different threads. @@ -203,7 +225,9 @@ class CoreFeatureSet { bool vr_mode_{}; bool using_custom_app_python_dir_{}; bool engine_done_{}; + LogLevel log_levels_[static_cast(LogName::kLast)]{}; + PyObject* initial_app_config_{}; std::thread::id main_thread_id_{}; CoreConfig core_config_; std::string build_src_dir_; @@ -217,6 +241,7 @@ class CoreFeatureSet { std::optional ba_env_user_python_dir_; std::optional ba_env_site_python_dir_; std::string ba_env_data_dir_; + double ba_env_launch_timestamp_{-1.0}; std::mutex thread_info_map_mutex_; std::unordered_map thread_info_map_; }; diff --git a/src/ballistica/core/platform/core_platform.cc b/src/ballistica/core/platform/core_platform.cc index da46d639..130d1f5a 100644 --- a/src/ballistica/core/platform/core_platform.cc +++ b/src/ballistica/core/platform/core_platform.cc @@ -178,10 +178,10 @@ auto CorePlatform::GetLegacyDeviceUUID() -> const std::string& { if (FILE* f2 = FOpen(path.c_str(), "wb")) { size_t result = fwrite(val.c_str(), val.size(), 1, f2); if (result != 1) - Log(LogLevel::kError, "unable to write bsuuid file."); + Log(LogName::kBa, LogLevel::kError, "unable to write bsuuid file."); fclose(f2); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "unable to open bsuuid file for writing: '" + path + "'"); } } @@ -192,7 +192,8 @@ auto CorePlatform::GetLegacyDeviceUUID() -> const std::string& { } auto CorePlatform::GetDeviceV1AccountUUIDPrefix() -> std::string { - Log(LogLevel::kError, "GetDeviceV1AccountUUIDPrefix() unimplemented"); + Log(LogName::kBa, LogLevel::kError, + "GetDeviceV1AccountUUIDPrefix() unimplemented"); return "u"; } @@ -248,10 +249,12 @@ void CorePlatform::SetLowLevelConfigValue(const char* key, int value) { if (f) { size_t result = fwrite(out.c_str(), out.size(), 1, f); if (result != 1) - Log(LogLevel::kError, "unable to write low level config file."); + Log(LogName::kBa, LogLevel::kError, + "unable to write low level config file."); fclose(f); } else { - Log(LogLevel::kError, "unable to open low level config file for writing."); + Log(LogName::kBa, LogLevel::kError, + "unable to open low level config file for writing."); } } @@ -425,7 +428,7 @@ auto CorePlatform::GetLocale() -> std::string { return lang; } else { if (!g_buildconfig.headless_build()) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "No LANG value available; defaulting to en_US"); } return "en_US"; @@ -688,7 +691,7 @@ auto CorePlatform::ConvertIncomingLeaderboardScore( void CorePlatform::SubmitScore(const std::string& game, const std::string& version, int64_t score) { - Log(LogLevel::kError, "FIXME: SubmitScore() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "FIXME: SubmitScore() unimplemented"); } void CorePlatform::ReportAchievement(const std::string& achievement) {} @@ -701,7 +704,8 @@ auto CorePlatform::HaveLeaderboard(const std::string& game, void CorePlatform::ShowGameServiceUI(const std::string& show, const std::string& game, const std::string& game_version) { - Log(LogLevel::kError, "FIXME: ShowGameServiceUI() unimplemented"); + Log(LogName::kBa, LogLevel::kError, + "FIXME: ShowGameServiceUI() unimplemented"); } void CorePlatform::AndroidSetResString(const std::string& res) { @@ -729,7 +733,7 @@ auto CorePlatform::DemangleCXXSymbol(const std::string& s) -> std::string { abi::__cxa_demangle(s.c_str(), nullptr, nullptr, &demangle_status); if (demangled_name != nullptr) { if (demangle_status != 0) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "__cxa_demangle got buffer but non-zero status; unexpected"); } std::string retval = demangled_name; @@ -744,25 +748,28 @@ auto CorePlatform::DemangleCXXSymbol(const std::string& s) -> std::string { } void CorePlatform::ResetAchievements() { - Log(LogLevel::kError, "ResetAchievements() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "ResetAchievements() unimplemented"); } void CorePlatform::RunEvents() {} void CorePlatform::MusicPlayerPlay(PyObject* target) { - Log(LogLevel::kError, "MusicPlayerPlay() unimplemented on this platform"); + Log(LogName::kBa, LogLevel::kError, + "MusicPlayerPlay() unimplemented on this platform"); } void CorePlatform::MusicPlayerStop() { - Log(LogLevel::kError, "MusicPlayerStop() unimplemented on this platform"); + Log(LogName::kBa, LogLevel::kError, + "MusicPlayerStop() unimplemented on this platform"); } void CorePlatform::MusicPlayerShutdown() { - Log(LogLevel::kError, "MusicPlayerShutdown() unimplemented on this platform"); + Log(LogName::kBa, LogLevel::kError, + "MusicPlayerShutdown() unimplemented on this platform"); } void CorePlatform::MusicPlayerSetVolume(float volume) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "MusicPlayerSetVolume() unimplemented on this platform"); } @@ -785,7 +792,7 @@ void CorePlatform::SubmitAnalyticsCounts() {} void CorePlatform::SetPlatformMiscReadVals(const std::string& vals) {} void CorePlatform::ShowAd(const std::string& purpose) { - Log(LogLevel::kError, "ShowAd() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "ShowAd() unimplemented"); } auto CorePlatform::GetHasAds() -> bool { return false; } @@ -796,7 +803,7 @@ auto CorePlatform::GetHasVideoAds() -> bool { } void CorePlatform::SignInV1(const std::string& account_type) { - Log(LogLevel::kError, "SignInV1() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "SignInV1() unimplemented"); } void CorePlatform::V1LoginDidChange() { @@ -804,33 +811,35 @@ void CorePlatform::V1LoginDidChange() { } void CorePlatform::SignOutV1() { - Log(LogLevel::kError, "SignOutV1() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "SignOutV1() unimplemented"); } void CorePlatform::MacMusicAppInit() { - Log(LogLevel::kError, "MacMusicAppInit() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "MacMusicAppInit() unimplemented"); } auto CorePlatform::MacMusicAppGetVolume() -> int { - Log(LogLevel::kError, "MacMusicAppGetVolume() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "MacMusicAppGetVolume() unimplemented"); return 0; } void CorePlatform::MacMusicAppSetVolume(int volume) { - Log(LogLevel::kError, "MacMusicAppSetVolume() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "MacMusicAppSetVolume() unimplemented"); } void CorePlatform::MacMusicAppStop() { - Log(LogLevel::kError, "MacMusicAppStop() unimplemented"); + Log(LogName::kBa, LogLevel::kError, "MacMusicAppStop() unimplemented"); } auto CorePlatform::MacMusicAppPlayPlaylist(const std::string& playlist) -> bool { - Log(LogLevel::kError, "MacMusicAppPlayPlaylist() unimplemented"); + Log(LogName::kBa, LogLevel::kError, + "MacMusicAppPlayPlaylist() unimplemented"); return false; } auto CorePlatform::MacMusicAppGetPlaylists() -> std::list { - Log(LogLevel::kError, "MacMusicAppGetPlaylists() unimplemented"); + Log(LogName::kBa, LogLevel::kError, + "MacMusicAppGetPlaylists() unimplemented"); return {}; } @@ -935,8 +944,9 @@ auto CorePlatform::SetSocketNonBlocking(int sd) -> bool { #else int result = fcntl(sd, F_SETFL, O_NONBLOCK); if (result != 0) { - Log(LogLevel::kError, "Error setting non-blocking socket: " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBa, LogLevel::kError, + "Error setting non-blocking socket: " + + g_core->platform->GetSocketErrorString()); return false; } return true; @@ -1069,6 +1079,12 @@ auto CorePlatform::GetCurrentMicrosecs() -> millisecs_t { .count(); } +auto CorePlatform::GetSecondsSinceEpoch() -> double { + return std::chrono::duration( + std::chrono::system_clock::now().time_since_epoch()) + .count(); +} + auto CorePlatform::GetCurrentWholeSeconds() -> int64_t { return std::chrono::time_point_cast( std::chrono::steady_clock::now()) diff --git a/src/ballistica/core/platform/core_platform.h b/src/ballistica/core/platform/core_platform.h index 1a267f0f..a1b2f5f5 100644 --- a/src/ballistica/core/platform/core_platform.h +++ b/src/ballistica/core/platform/core_platform.h @@ -369,6 +369,9 @@ class CorePlatform { /// to not go backwards. static auto GetCurrentWholeSeconds() -> int64_t; + /// Return seconds since the epoch; same as Python's time.time(). + static auto GetSecondsSinceEpoch() -> double; + static void SleepSeconds(seconds_t duration); static void SleepMillisecs(millisecs_t duration); static void SleepMicrosecs(microsecs_t duration); diff --git a/src/ballistica/core/platform/windows/core_platform_windows.cc b/src/ballistica/core/platform/windows/core_platform_windows.cc index f594aac8..4a07ac45 100644 --- a/src/ballistica/core/platform/windows/core_platform_windows.cc +++ b/src/ballistica/core/platform/windows/core_platform_windows.cc @@ -919,7 +919,8 @@ auto CorePlatformWindows::GetEnv(const std::string& name) // This should always succeed at this point; make noise if not. if (result == 0 || result > big_buffer.size()) { - Log(LogLevel::kError, "GetEnv to allocated buffer failed; unexpected."); + Log(LogName::kBa, LogLevel::kError, + "GetEnv to allocated buffer failed; unexpected."); return {}; } return UTF8Encode(big_buffer.data()); @@ -997,14 +998,15 @@ std::vector CorePlatformWindows::GetBroadcastAddrs() { pIPAddrTable = static_cast(MALLOC(dwSize)); } if (pIPAddrTable == nullptr) { - Log(LogLevel::kError, "Memory allocation failed for GetIpAddrTable\n"); + Log(LogName::kBa, LogLevel::kError, + "Memory allocation failed for GetIpAddrTable\n"); err = true; } if (!err) { // Make a second call to GetIpAddrTable to get the actual data we want if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "GetIpAddrTable failed with error " + std::to_string(dwRetVal)); err = true; } @@ -1040,8 +1042,9 @@ bool CorePlatformWindows::SetSocketNonBlocking(int sd) { unsigned long dataval = 1; // NOLINT (func signature wants long) int result = ioctlsocket(sd, FIONBIO, &dataval); if (result != 0) { - Log(LogLevel::kError, "Error setting non-blocking socket: " - + g_core->platform->GetSocketErrorString()); + Log(LogName::kBa, LogLevel::kError, + "Error setting non-blocking socket: " + + g_core->platform->GetSocketErrorString()); return false; } return true; diff --git a/src/ballistica/core/python/core_python.cc b/src/ballistica/core/python/core_python.cc index ebf2ed77..0c503a48 100644 --- a/src/ballistica/core/python/core_python.cc +++ b/src/ballistica/core/python/core_python.cc @@ -4,10 +4,12 @@ #include #include +#include #include #include "ballistica/core/mgen/python_modules_monolithic.h" #include "ballistica/core/platform/core_platform.h" +#include "ballistica/shared/foundation/macros.h" #include "ballistica/shared/python/python.h" #include "ballistica/shared/python/python_command.h" @@ -174,18 +176,38 @@ void CorePython::EnablePythonLoggingCalls() { } auto gil{Python::ScopedInterpreterLock()}; - assert(objs().Exists(ObjID::kLoggingDebugCall)); - assert(objs().Exists(ObjID::kLoggingInfoCall)); - assert(objs().Exists(ObjID::kLoggingWarningCall)); - assert(objs().Exists(ObjID::kLoggingErrorCall)); - assert(objs().Exists(ObjID::kLoggingCriticalCall)); + // Make sure we've got all our logging Python bits we need. + assert(objs().Exists(ObjID::kLoggingLevelDebug)); + assert(objs().Exists(ObjID::kLoggingLevelInfo)); + assert(objs().Exists(ObjID::kLoggingLevelWarning)); + assert(objs().Exists(ObjID::kLoggingLevelError)); + assert(objs().Exists(ObjID::kLoggingLevelCritical)); + assert(objs().Exists(ObjID::kLoggerRoot)); + assert(objs().Exists(ObjID::kLoggerRootLogCall)); + assert(objs().Exists(ObjID::kLoggerBa)); + assert(objs().Exists(ObjID::kLoggerBaLogCall)); + assert(objs().Exists(ObjID::kLoggerBaAccount)); + assert(objs().Exists(ObjID::kLoggerBaAccountLogCall)); + assert(objs().Exists(ObjID::kLoggerBaAudio)); + assert(objs().Exists(ObjID::kLoggerBaAudioLogCall)); + assert(objs().Exists(ObjID::kLoggerBaGraphics)); + assert(objs().Exists(ObjID::kLoggerBaGraphicsLogCall)); + assert(objs().Exists(ObjID::kLoggerBaLifecycle)); + assert(objs().Exists(ObjID::kLoggerBaLifecycleLogCall)); + assert(objs().Exists(ObjID::kLoggerBaAssets)); + assert(objs().Exists(ObjID::kLoggerBaAssetsLogCall)); + assert(objs().Exists(ObjID::kLoggerBaInput)); + assert(objs().Exists(ObjID::kLoggerBaInputLogCall)); + assert(objs().Exists(ObjID::kLoggerBaNetworking)); + assert(objs().Exists(ObjID::kLoggerBaNetworkingLogCall)); // Push any early log calls we've been holding on to along to Python. { std::scoped_lock lock(early_log_lock_); python_logging_calls_enabled_ = true; for (auto&& entry : early_logs_) { - LoggingCall(entry.first, "[HELD] " + entry.second); + LoggingCall(std::get<0>(entry), std::get<1>(entry), + "[HELD] " + std::get<2>(entry)); } early_logs_.clear(); } @@ -214,6 +236,72 @@ void CorePython::ImportPythonObjs() { } } +void CorePython::UpdateInternalLoggerLevels(LogLevel* log_levels) { + assert(python_logging_calls_enabled_); + assert(Python::HaveGIL()); + + const int log_level_debug{10}; + const int log_level_info{20}; + const int log_level_warning{30}; + const int log_level_error{40}; + const int log_level_critical{50}; + assert(log_level_debug == objs().Get(ObjID::kLoggingLevelDebug).ValueAsInt()); + assert(log_level_info == objs().Get(ObjID::kLoggingLevelInfo).ValueAsInt()); + assert(log_level_warning + == objs().Get(ObjID::kLoggingLevelWarning).ValueAsInt()); + assert(log_level_error == objs().Get(ObjID::kLoggingLevelError).ValueAsInt()); + assert(log_level_critical + == objs().Get(ObjID::kLoggingLevelCritical).ValueAsInt()); + + std::pair pairs[] = { + {LogName::kRoot, ObjID::kLoggerRoot}, + {LogName::kBa, ObjID::kLoggerBa}, + {LogName::kBaAccount, ObjID::kLoggerBaAccount}, + {LogName::kBaAudio, ObjID::kLoggerBaAudio}, + {LogName::kBaGraphics, ObjID::kLoggerBaGraphics}, + {LogName::kBaLifecycle, ObjID::kLoggerBaLifecycle}, + {LogName::kBaAssets, ObjID::kLoggerBaAssets}, + {LogName::kBaInput, ObjID::kLoggerBaInput}, + {LogName::kBaNetworking, ObjID::kLoggerBaNetworking}, + }; + + int count{}; + for (const auto& pair : pairs) { + count++; + auto logname{pair.first}; + auto objid{pair.second}; + auto out{objs().Get(objid).GetAttr("getEffectiveLevel").Call()}; + assert(out.Exists()); + auto outval{static_cast(out.ValueAsInt())}; + + switch (outval) { + case log_level_debug: + log_levels[static_cast(logname)] = LogLevel::kDebug; + break; + case log_level_info: + log_levels[static_cast(logname)] = LogLevel::kInfo; + break; + case log_level_warning: + log_levels[static_cast(logname)] = LogLevel::kWarning; + break; + case log_level_error: + log_levels[static_cast(logname)] = LogLevel::kError; + break; + case log_level_critical: + log_levels[static_cast(logname)] = LogLevel::kCritical; + break; + default: + fprintf(stderr, "Found unexpected resolved logging level %d\n", outval); + } + } + // Sanity check: Make sure we covered our full set of LogNames. + if (count != static_cast(LogName::kLast)) { + fprintf(stderr, + "WARNING: UpdateInternalLoggerLevels does not seem to be covering " + "all log names.\n"); + } +} + void CorePython::SoftImportBase() { auto gil{Python::ScopedInterpreterLock()}; auto result = PythonRef::StolenSoft(PyImport_ImportModule("_babase")); @@ -296,23 +384,25 @@ void CorePython::MonolithicModeBaEnvConfigure() { g_core->LifecycleLog("baenv.configure() end"); } -void CorePython::LoggingCall(LogLevel loglevel, const std::string& msg) { - // If we're not yet sending logs to Python, store this one away until we are. +void CorePython::LoggingCall(LogName logname, LogLevel loglevel, + const std::string& msg) { + // If we're not yet sending logs to Python, store this one away until we + // are. if (!python_logging_calls_enabled_) { std::scoped_lock lock(early_log_lock_); - early_logs_.emplace_back(loglevel, msg); + early_logs_.emplace_back(logname, loglevel, msg); - // UPDATE - trying to disable this for now to make the concept of delayed - // logs a bit less scary. Perhaps we can update fatal-error to dump these or - // have a mode to immediate-print them as needed. + // UPDATE - trying to disable this for now to make the concept of + // delayed logs a bit less scary. Perhaps we can update fatal-error to + // dump these or have a mode to immediate-print them as needed. if (explicit_bool(false)) { - // There's a chance that we're going down in flames and this log - // might be useful to see even if we never get a chance to chip it to + // There's a chance that we're going down in flames and this log might + // be useful to see even if we never get a chance to chip it to // Python. So let's make an attempt to get it at least seen now in // whatever way we can. (platform display-log call and stderr). const char* errmsg{ - "CorePython::LoggingCall() called before Python" - " logging available."}; + "CorePython::LoggingCall() called before Python logging " + "available."}; if (g_core->platform) { g_core->platform->EmitPlatformLog("root", LogLevel::kError, errmsg); g_core->platform->EmitPlatformLog("root", loglevel, msg); @@ -322,42 +412,93 @@ void CorePython::LoggingCall(LogLevel loglevel, const std::string& msg) { return; } - // Ok; seems we've got Python calls. Run the right one for our log level. - ObjID logcallobj; - switch (loglevel) { - case LogLevel::kDebug: - logcallobj = ObjID::kLoggingDebugCall; - break; - case LogLevel::kInfo: - logcallobj = ObjID::kLoggingInfoCall; - break; - case LogLevel::kWarning: - logcallobj = ObjID::kLoggingWarningCall; - break; - case LogLevel::kError: - logcallobj = ObjID::kLoggingErrorCall; - break; - case LogLevel::kCritical: - logcallobj = ObjID::kLoggingCriticalCall; - break; - default: - logcallobj = ObjID::kLoggingInfoCall; - fprintf(stderr, "Unexpected LogLevel %d\n", static_cast(loglevel)); - break; - } - // Make sure we're good to go from any thread. Python::ScopedInterpreterLock lock; - PythonRef args(Py_BuildValue("(s)", msg.c_str()), PythonRef::kSteal); + ObjID logcallobj; + bool handled{}; + switch (logname) { + case LogName::kRoot: + logcallobj = ObjID::kLoggerRootLogCall; + handled = true; + break; + case LogName::kBa: + logcallobj = ObjID::kLoggerBaLogCall; + handled = true; + break; + case LogName::kBaAccount: + logcallobj = ObjID::kLoggerBaAccountLogCall; + handled = true; + break; + case LogName::kBaAudio: + logcallobj = ObjID::kLoggerBaAudioLogCall; + handled = true; + break; + case LogName::kBaGraphics: + logcallobj = ObjID::kLoggerBaGraphicsLogCall; + handled = true; + break; + case LogName::kBaAssets: + logcallobj = ObjID::kLoggerBaAssetsLogCall; + handled = true; + break; + case LogName::kBaInput: + logcallobj = ObjID::kLoggerBaInputLogCall; + handled = true; + break; + case LogName::kBaNetworking: + logcallobj = ObjID::kLoggerBaNetworkingLogCall; + handled = true; + break; + case LogName::kBaLifecycle: + logcallobj = ObjID::kLoggerBaLifecycleLogCall; + handled = true; + break; + case LogName::kLast: + logcallobj = ObjID::kLoggerRootLogCall; + break; + } + // Handle this here instead of via default clause so we get warnings about + // new unhandled enum values. + if (!handled) { + logcallobj = ObjID::kLoggerRootLogCall; + fprintf(stderr, "Unexpected LogName %d\n", static_cast(logname)); + } + + ObjID loglevelobjid; + switch (loglevel) { + case LogLevel::kDebug: + loglevelobjid = ObjID::kLoggingLevelDebug; + break; + case LogLevel::kInfo: + loglevelobjid = ObjID::kLoggingLevelInfo; + break; + case LogLevel::kWarning: + loglevelobjid = ObjID::kLoggingLevelWarning; + break; + case LogLevel::kError: + loglevelobjid = ObjID::kLoggingLevelError; + break; + case LogLevel::kCritical: + loglevelobjid = ObjID::kLoggingLevelCritical; + break; + default: + loglevelobjid = ObjID::kLoggingLevelInfo; + fprintf(stderr, "Unexpected LogLevel %d\n", static_cast(loglevel)); + break; + } + PythonRef args( + Py_BuildValue("(Os)", objs().Get(loglevelobjid).Get(), msg.c_str()), + PythonRef::kSteal); objs().Get(logcallobj).Call(args); } auto CorePython::WasModularMainCalled() -> bool { assert(!g_buildconfig.monolithic_build()); - // This gets called in modular builds before anything is inited, so we need to - // avoid using anything from g_core or whatnot here; only raw Python stuff. + // This gets called in modular builds before anything is inited, so we need + // to avoid using anything from g_core or whatnot here; only raw Python + // stuff. PyObject* baenv = PyImport_ImportModule("baenv"); if (!baenv) { @@ -394,8 +535,9 @@ auto CorePython::WasModularMainCalled() -> bool { auto CorePython::FetchPythonArgs(std::vector* buffer) -> std::vector { - // This gets called in modular builds before anything is inited, so we need to - // avoid using anything from g_core or whatnot here; only raw Python stuff. + // This gets called in modular builds before anything is inited, so we need + // to avoid using anything from g_core or whatnot here; only raw Python + // stuff. assert(buffer && buffer->empty()); PyObject* sys = PyImport_ImportModule("sys"); diff --git a/src/ballistica/core/python/core_python.h b/src/ballistica/core/python/core_python.h index 63900110..4a045e52 100644 --- a/src/ballistica/core/python/core_python.h +++ b/src/ballistica/core/python/core_python.h @@ -24,11 +24,29 @@ class CorePython { kJsonDumpsCall, kJsonLoadsCall, kEmptyTuple, - kLoggingDebugCall, - kLoggingInfoCall, - kLoggingWarningCall, - kLoggingErrorCall, - kLoggingCriticalCall, + kLoggingLevelDebug, + kLoggingLevelInfo, + kLoggingLevelWarning, + kLoggingLevelError, + kLoggingLevelCritical, + kLoggerRoot, + kLoggerRootLogCall, + kLoggerBa, + kLoggerBaLogCall, + kLoggerBaAccount, + kLoggerBaAccountLogCall, + kLoggerBaAudio, + kLoggerBaAudioLogCall, + kLoggerBaGraphics, + kLoggerBaGraphicsLogCall, + kLoggerBaLifecycle, + kLoggerBaLifecycleLogCall, + kLoggerBaAssets, + kLoggerBaAssetsLogCall, + kLoggerBaInput, + kLoggerBaInputLogCall, + kLoggerBaNetworking, + kLoggerBaNetworkingLogCall, kPrependSysPathCall, kBaEnvConfigureCall, kBaEnvGetConfigCall, @@ -49,10 +67,11 @@ class CorePython { /// Can be called from any thread at any time. If called before Python /// logging is available, logs locally using Logging::EmitPlatformLog() /// (with an added warning). - void LoggingCall(LogLevel loglevel, const std::string& msg); + void LoggingCall(LogName logname, LogLevel loglevel, const std::string& msg); void ImportPythonObjs(); void VerifyPythonEnvironment(); void SoftImportBase(); + void UpdateInternalLoggerLevels(LogLevel* log_levels); static auto WasModularMainCalled() -> bool; @@ -70,7 +89,7 @@ class CorePython { // go here. They all get shipped at once as soon as it is possible. bool python_logging_calls_enabled_{}; std::mutex early_log_lock_; - std::list> early_logs_; + std::list> early_logs_; }; } // namespace ballistica::core diff --git a/src/ballistica/core/support/base_soft.h b/src/ballistica/core/support/base_soft.h index bde97e10..70d6c5e1 100644 --- a/src/ballistica/core/support/base_soft.h +++ b/src/ballistica/core/support/base_soft.h @@ -33,7 +33,8 @@ class BaseSoftInterface { virtual auto FeatureSetFromData(PyObject* obj) -> FeatureSetNativeComponent* = 0; virtual void DoV1CloudLog(const std::string& msg) = 0; - virtual void PushDevConsolePrintCall(const std::string& msg) = 0; + virtual void PushDevConsolePrintCall(const std::string& msg, float scale, + Vector4f color) = 0; virtual auto GetPyExceptionType(PyExcType exctype) -> PyObject* = 0; virtual auto PrintPythonStackTrace() -> bool = 0; virtual auto GetPyLString(PyObject* obj) -> std::string = 0; diff --git a/src/ballistica/core/support/core_config.cc b/src/ballistica/core/support/core_config.cc index 4c5bb8ae..10c8376c 100644 --- a/src/ballistica/core/support/core_config.cc +++ b/src/ballistica/core/support/core_config.cc @@ -78,11 +78,15 @@ static auto ParseArgValue(int argc, char** argv, int* i, const char* arg_long, } void CoreConfig::ApplyEnvVars() { - if (auto* envval = getenv("BA_LIFECYCLE_LOG")) { - if (!strcmp(envval, "1")) { - lifecycle_log = true; - } - } + // TODO(ericf): This is now simply a log level. If we want to allow + // controlling log-levels via env-vars we should come up with a unified + // system for that. + + // if (auto* envval = getenv("BA_LIFECYCLE_LOG")) { + // if (!strcmp(envval, "1")) { + // lifecycle_log = true; + // } + // } if (auto* envval = getenv("BA_DEBUGGER_ATTACHED")) { if (!strcmp(envval, "1")) { debugger_attached = true; diff --git a/src/ballistica/core/support/core_config.h b/src/ballistica/core/support/core_config.h index a74aabf0..1ccfef09 100644 --- a/src/ballistica/core/support/core_config.h +++ b/src/ballistica/core/support/core_config.h @@ -30,14 +30,7 @@ class CoreConfig { bool vr_mode{}; /// Log various stages/times in the bootstrapping process. - bool lifecycle_log{}; - - /// Normally early C++ Log() calls are held until babase has been imported - /// so that when they are pushed out to the Python logging calls they are - /// properly routed through the full engine. If you are not using babase - /// or are trying to debug early issues you can flip this off to push - /// things to Python as soon as technically possible. - bool hold_early_logs{true}; + // bool lifecycle_log{}; /// Let the engine know there's a debugger attached so it should do things /// like abort() instead of exiting with error codes. diff --git a/src/ballistica/scene_v1/connection/connection.cc b/src/ballistica/scene_v1/connection/connection.cc index 36daa542..a38c5e5e 100644 --- a/src/ballistica/scene_v1/connection/connection.cc +++ b/src/ballistica/scene_v1/connection/connection.cc @@ -156,7 +156,7 @@ void Connection::HandleGamePacketCompressed(const std::vector& data) { try { data_decompressed = g_base->huffman->decompress(data); } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, std::string("Error in huffman decompression for packet: ") + e.what()); // Hmmm i guess lets just ignore this packet and keep on trucking?.. or @@ -176,7 +176,7 @@ void Connection::HandleGamePacket(const std::vector& data) { switch (data[0]) { case BA_SCENEPACKET_KEEPALIVE: { if (data.size() != 4) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "Error: got invalid BA_SCENEPACKET_KEEPALIVE packet."); return; } @@ -190,7 +190,8 @@ void Connection::HandleGamePacket(const std::vector& data) { // Expect 1 byte type, 2 byte num, 3 byte acks, at least 1 byte payload. if (data.size() < 7) { - Log(LogLevel::kError, "Got invalid BA_PACKET_STATE packet."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Got invalid BA_PACKET_STATE packet."); return; } uint16_t num; @@ -221,7 +222,8 @@ void Connection::HandleGamePacket(const std::vector& data) { // Expect 1 byte type, 2 byte num, 2 byte unreliable-num, 3 byte acks, // at least 1 byte payload. if (data.size() < 9) { - Log(LogLevel::kError, "Got invalid BA_PACKET_STATE_UNRELIABLE packet."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Got invalid BA_PACKET_STATE_UNRELIABLE packet."); return; } uint16_t num, num_unreliable; @@ -242,8 +244,9 @@ void Connection::HandleGamePacket(const std::vector& data) { } default: - Log(LogLevel::kError, "Connection got unknown packet type: " - + std::to_string(static_cast(data[0]))); + Log(LogName::kBaNetworking, LogLevel::kError, + "Connection got unknown packet type: " + + std::to_string(static_cast(data[0]))); break; } } @@ -325,7 +328,7 @@ void Connection::SendReliableMessage(const std::vector& data) { void Connection::SendUnreliableMessage(const std::vector& data) { // For now we just silently drop anything bigger than our max packet size. if (data.size() + 8 > kMaxPacketSize) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "Error: Dropping outgoing unreliable packet of size " + std::to_string(data.size()) + "."); return; @@ -438,11 +441,12 @@ void Connection::HandleMessagePacket(const std::vector& buffer) { multipart_buffer_.resize(old_size + (buffer.size() - 1)); memcpy(&(multipart_buffer_[old_size]), &(buffer[1]), buffer.size() - 1); } else { - Log(LogLevel::kError, "got invalid BA_MESSAGE_MULTIPART"); + Log(LogName::kBaNetworking, LogLevel::kError, + "got invalid BA_MESSAGE_MULTIPART"); } if (buffer[0] == BA_MESSAGE_MULTIPART_END) { if (multipart_buffer_[0] == BA_MESSAGE_MULTIPART) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "nested multipart message detected; kicking"); Error(""); } @@ -487,7 +491,7 @@ void Connection::SendGamePacket(const std::vector& data) { && data[0] != BA_SCENEPACKET_HANDSHAKE_RESPONSE) { if (explicit_bool(false)) { BA_LOG_ONCE( - LogLevel::kError, + LogName::kBaNetworking, LogLevel::kError, "SendGamePacket() called before can_communicate set (" + g_core->platform->DemangleCXXSymbol(typeid(*this).name()) + " ptype " + std::to_string(static_cast(data[0])) + ")"); diff --git a/src/ballistica/scene_v1/connection/connection_set.cc b/src/ballistica/scene_v1/connection/connection_set.cc index 1f318416..78e99dbf 100644 --- a/src/ballistica/scene_v1/connection/connection_set.cc +++ b/src/ballistica/scene_v1/connection/connection_set.cc @@ -31,7 +31,7 @@ void ConnectionSet::RegisterClientController(ClientControllerInterface* c) { // This shouldn't happen, but if there's already a controller registered, // detach all clients from it. if (client_controller_) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "RegisterClientController() called " "but already have a controller; bad."); for (auto&& i : connections_to_clients_) { @@ -214,7 +214,7 @@ auto ConnectionSet::GetConnectionsToClients() if (connections_to_client.second.Exists()) { connections.push_back(connections_to_client.second.Get()); } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "HAVE NONEXISTENT CONNECTION_TO_CLIENT IN LIST; UNEXPECTED"); } } @@ -274,7 +274,7 @@ void ConnectionSet::SendScreenMessageToAll(const std::string& s, float r, void ConnectionSet::PrepareForLaunchHostSession() { // If for some reason we're still attached to a host, kill the connection. if (connection_to_host_.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Had host-connection during LaunchHostSession(); shouldn't happen."); connection_to_host_->RequestDisconnect(); connection_to_host_.Clear(); @@ -320,8 +320,9 @@ auto ConnectionSet::DisconnectClient(int client_id, int ban_seconds) -> bool { return false; } if (client_id > 255) { - Log(LogLevel::kError, "DisconnectClient got client_id > 255 (" - + std::to_string(client_id) + ")"); + Log(LogName::kBaNetworking, LogLevel::kError, + "DisconnectClient got client_id > 255 (" + std::to_string(client_id) + + ")"); } else { std::vector msg_out(2); msg_out[0] = BA_MESSAGE_KICK_VOTE; @@ -409,7 +410,7 @@ void ConnectionSet::UnregisterClientController(ClientControllerInterface* c) { // This shouldn't happen. if (client_controller_ != c) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "UnregisterClientController() called with a non-registered " "controller"); return; @@ -675,7 +676,8 @@ void ConnectionSet::HandleIncomingUDPPacket(const std::vector& data_in, msg_out[0] = BA_PACKET_CLIENT_DENY; msg_out[1] = request_id; g_base->network_writer->PushSendToCall(msg_out, addr); - Log(LogLevel::kError, "All client slots full; really?.."); + Log(LogName::kBaNetworking, LogLevel::kError, + "All client slots full; really?.."); break; } connection_to_client = Object::New( @@ -717,7 +719,7 @@ auto ConnectionSet::VerifyClientAddr(uint8_t client_id, const SockAddr& addr) if (addr == connection_to_client_udp->addr()) { return true; } - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "VerifyClientAddr() found mismatch for client " + std::to_string(client_id) + "."); return false; @@ -730,7 +732,7 @@ void ConnectionSet::SetClientInfoFromMasterServer( const std::string& client_token, PyObject* info_obj) { // NOLINTNEXTLINE (python doing bitwise math on signed int) if (!PyDict_Check(info_obj)) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "got non-dict for master-server client info for token " + client_token + ": " + Python::ObjToString(info_obj)); return; diff --git a/src/ballistica/scene_v1/connection/connection_to_client.cc b/src/ballistica/scene_v1/connection/connection_to_client.cc index 458bf421..a68ed5fe 100644 --- a/src/ballistica/scene_v1/connection/connection_to_client.cc +++ b/src/ballistica/scene_v1/connection/connection_to_client.cc @@ -144,7 +144,8 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { } if (data.empty()) { - Log(LogLevel::kError, "ConnectionToClient got data size 0."); + Log(LogName::kBaNetworking, LogLevel::kError, + "ConnectionToClient got data size 0."); return; } @@ -157,7 +158,8 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { case BA_SCENEPACKET_HANDSHAKE_RESPONSE: { // We sent the client a handshake and they're responding. if (data.size() < 3) { - Log(LogLevel::kError, "got invalid BA_SCENEPACKET_HANDSHAKE_RESPONSE"); + Log(LogName::kBaNetworking, LogLevel::kError, + "got invalid BA_SCENEPACKET_HANDSHAKE_RESPONSE"); return; } @@ -350,7 +352,8 @@ void ConnectionToClient::SendScreenMessage(const std::string& s, float r, void ConnectionToClient::HandleMessagePacket( const std::vector& buffer) { if (buffer.empty()) { - Log(LogLevel::kError, "Got invalid HandleMessagePacket."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Got invalid HandleMessagePacket."); return; } @@ -404,7 +407,8 @@ void ConnectionToClient::HandleMessagePacket( if (b) { build_number_ = b->valueint; } else { - Log(LogLevel::kError, "No buildnumber in clientinfo msg."); + Log(LogName::kBaNetworking, LogLevel::kError, + "No buildnumber in clientinfo msg."); } // Grab their token (we use this to ask the @@ -413,7 +417,8 @@ void ConnectionToClient::HandleMessagePacket( if (t) { token_ = t->valuestring; } else { - Log(LogLevel::kError, "No token in clientinfo msg."); + Log(LogName::kBaNetworking, LogLevel::kError, + "No token in clientinfo msg."); } // Newer clients also pass a peer-hash, which @@ -432,7 +437,7 @@ void ConnectionToClient::HandleMessagePacket( } cJSON_Delete(info); } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Got invalid json in clientinfo message: '" + std::string(reinterpret_cast(&(buffer[1]))) + "'."); @@ -473,7 +478,7 @@ void ConnectionToClient::HandleMessagePacket( // we support for game streams vs client-connections. We could disallow // connections to/from these older peers while still allowing old replays // to play back. - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "Received old pre-json player profiles msg; ignoring."); break; } @@ -501,7 +506,7 @@ void ConnectionToClient::HandleMessagePacket( // spamming before we can verify their identities) if (appmode->require_client_authentication() && !got_info_from_master_server_) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Ignoring chat message from peer with no client info."); SendScreenMessage(R"({"r":"loadingTryAgainText"})", 1, 0, 0); } else if (last_chat_times_.size() >= 5) { @@ -599,7 +604,8 @@ void ConnectionToClient::HandleMessagePacket( GetClientInputDevice(buffer[1])) { int count = static_cast((buffer.size() - 2) / 5); if ((buffer.size() - 2) % 5 != 0) { - Log(LogLevel::kError, "Error: invalid player-input-commands packet"); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error: invalid player-input-commands packet"); break; } int index = 2; @@ -617,7 +623,8 @@ void ConnectionToClient::HandleMessagePacket( case BA_MESSAGE_REMOVE_REMOTE_PLAYER: { last_remove_player_time_ = g_core->GetAppTimeMillisecs(); if (buffer.size() != 2) { - Log(LogLevel::kError, "Error: invalid remove-remote-player packet"); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error: invalid remove-remote-player packet"); break; } if (ClientInputDevice* cid = GetClientInputDevice(buffer[1])) { @@ -632,7 +639,7 @@ void ConnectionToClient::HandleMessagePacket( host_session->RemovePlayer(player); } } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Unable to get ClientInputDevice for remove-remote-player msg."); } } @@ -641,7 +648,8 @@ void ConnectionToClient::HandleMessagePacket( case BA_MESSAGE_REQUEST_REMOTE_PLAYER: { if (buffer.size() != 2) { - Log(LogLevel::kError, "Error: invalid remote-player-request packet"); + Log(LogName::kBaNetworking, LogLevel::kError, + "Error: invalid remote-player-request packet"); break; } @@ -652,7 +660,7 @@ void ConnectionToClient::HandleMessagePacket( // It should have one of our special client delegates attached. auto* cid_d = dynamic_cast(&cid->delegate()); if (!cid_d) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Can't get client-input-device-delegate in request-remote-player " "msg."); break; @@ -676,7 +684,7 @@ void ConnectionToClient::HandleMessagePacket( } else { // Either timed out or have info; let the request go through. if (still_waiting_for_auth) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Allowing player-request without client\'s master-server " "info (build " + std::to_string(build_number_) + ")"); @@ -685,7 +693,7 @@ void ConnectionToClient::HandleMessagePacket( } } } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "ConnectionToClient got remote player" " request but have no host session"); } @@ -699,9 +707,9 @@ void ConnectionToClient::HandleMessagePacket( if (multipart_buffer_size() > 50000) { // Its not actually unknown but shhh don't tell the hackers... SendScreenMessage(R"({"r":"errorUnknownText"})", 1, 0, 0); - Log(LogLevel::kError, "Client data limit exceeded by '" - + peer_spec().GetShortName() - + "'; kicking."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Client data limit exceeded by '" + peer_spec().GetShortName() + + "'; kicking."); appmode->BanPlayer(peer_spec(), 1000 * 60); Error(""); return; @@ -792,8 +800,9 @@ void ConnectionToClient::HandleMasterServerClientInfo(PyObject* info_obj) { "{\"t\":[\"serverResponses\"," "\"Your account was rejected. Are you signed in?\"]}", 1, 0, 0); - Log(LogLevel::kError, "Master server found no valid account for '" - + peer_spec().GetShortName() + "'; kicking."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Master server found no valid account for '" + + peer_spec().GetShortName() + "'; kicking."); // Not benning anymore. People were exploiting this by impersonating // other players using their public ids to get them banned from diff --git a/src/ballistica/scene_v1/connection/connection_to_client_udp.cc b/src/ballistica/scene_v1/connection/connection_to_client_udp.cc index 805336db..f09b2ce0 100644 --- a/src/ballistica/scene_v1/connection/connection_to_client_udp.cc +++ b/src/ballistica/scene_v1/connection/connection_to_client_udp.cc @@ -72,7 +72,8 @@ void ConnectionToClientUDP::HandleGamePacket( void ConnectionToClientUDP::Die() { if (did_die_) { - Log(LogLevel::kError, "Posting multiple die messages; probably not good."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Posting multiple die messages; probably not good."); return; } // this will actually clear the object.. diff --git a/src/ballistica/scene_v1/connection/connection_to_host.cc b/src/ballistica/scene_v1/connection/connection_to_host.cc index 8da325cd..bf5de0eb 100644 --- a/src/ballistica/scene_v1/connection/connection_to_host.cc +++ b/src/ballistica/scene_v1/connection/connection_to_host.cc @@ -250,7 +250,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { g_base->python->GetRawConfigValue("Player Profiles"); PythonRef empty_dict; if (!profiles) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "No profiles found; sending empty list to host"); empty_dict.Steal(PyDict_New()); profiles = empty_dict.Get(); @@ -265,7 +265,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { .Get(core::CorePython::ObjID::kJsonDumpsCall) .Call(args, keywds); if (!results.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Error getting json dump of local profiles"); } else { try { @@ -276,14 +276,14 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { memcpy(&(msg[1]), &s[0], s.size()); SendReliableMessage(msg); } catch (const std::exception& e) { - Log(LogLevel::kError, - std::string("exc sending player profiles to host: ") + Log(LogName::kBaNetworking, LogLevel::kError, + std::string("Error sending player profiles to host: ") + e.what()); } } } } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Connected to old protocol; can't send player profiles"); } } @@ -311,7 +311,8 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { assert(g_base->InLogicThread()); if (buffer.empty()) { - Log(LogLevel::kError, "Got invalid HandleMessagePacket"); + Log(LogName::kBaNetworking, LogLevel::kError, + "Got invalid HandleMessagePacket"); return; } @@ -335,7 +336,8 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { if (b) { build_number_ = b->valueint; } else { - Log(LogLevel::kError, "no buildnumber in hostinfo msg"); + Log(LogName::kBaNetworking, LogLevel::kError, + "no buildnumber in hostinfo msg"); } // Party name. cJSON* n = cJSON_GetObjectItem(info, "n"); @@ -344,7 +346,8 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { } cJSON_Delete(info); } else { - Log(LogLevel::kError, "got invalid json in hostinfo message"); + Log(LogName::kBaNetworking, LogLevel::kError, + "got invalid json in hostinfo message"); } } got_host_info_ = true; @@ -440,7 +443,8 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { case BA_MESSAGE_ATTACH_REMOTE_PLAYER_2: { // New-style packet which includes a 32-bit player_id. if (buffer.size() != 6) { - Log(LogLevel::kError, "Invalid attach-remote-player-2 msg"); + Log(LogName::kBaNetworking, LogLevel::kError, + "Invalid attach-remote-player-2 msg"); break; } @@ -457,7 +461,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { delegate->AttachToRemotePlayer(this, static_cast_check_fit(player_id)); } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "InputDevice does not have a SceneV1 delegate as expected " "(loc1)."); } @@ -476,7 +480,8 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { // public servers. // TODO(ericf): can remove this once back-compat-protocol > 29. if (buffer.size() != 3) { - Log(LogLevel::kError, "Invalid attach-remote-player msg."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Invalid attach-remote-player msg."); break; } @@ -490,7 +495,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { &input_device->delegate())) { delegate->AttachToRemotePlayer(this, buffer[2]); } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "InputDevice does not have a SceneV1 delegate as expected " "(loc2)."); } @@ -508,7 +513,8 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { case BA_MESSAGE_DETACH_REMOTE_PLAYER: { if (buffer.size() != 2) { - Log(LogLevel::kError, "Invalid detach-remote-player msg"); + Log(LogName::kBaNetworking, LogLevel::kError, + "Invalid detach-remote-player msg"); break; } // Server is telling us that our local input device is no longer @@ -532,7 +538,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { // be cleared out at this point. Just complain if that's not // the case. if (connection_to_host != nullptr) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "InputDevice does not have a SceneV1 delegate as expected " "(loc3)."); } diff --git a/src/ballistica/scene_v1/connection/connection_to_host_udp.cc b/src/ballistica/scene_v1/connection/connection_to_host_udp.cc index ad9bcf4f..ecd4acd7 100644 --- a/src/ballistica/scene_v1/connection/connection_to_host_udp.cc +++ b/src/ballistica/scene_v1/connection/connection_to_host_udp.cc @@ -125,7 +125,8 @@ void ConnectionToHostUDP::Update() { // departure before doing this when possible. void ConnectionToHostUDP::Die() { if (did_die_) { - Log(LogLevel::kError, "Posting multiple die messages; probably not good."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Posting multiple die messages; probably not good."); return; } if (auto* appmode = classic::ClassicAppMode::GetActiveOrWarn()) { @@ -133,7 +134,7 @@ void ConnectionToHostUDP::Die() { appmode->connections()->PushDisconnectedFromHostCall(); did_die_ = true; } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "Running update for non-current host-connection; shouldn't " "happen."); } diff --git a/src/ballistica/scene_v1/dynamics/dynamics.cc b/src/ballistica/scene_v1/dynamics/dynamics.cc index 020039ad..8e6d1ee1 100644 --- a/src/ballistica/scene_v1/dynamics/dynamics.cc +++ b/src/ballistica/scene_v1/dynamics/dynamics.cc @@ -136,14 +136,14 @@ class Dynamics::Impl_ { Dynamics::Dynamics(Scene* scene_in) : scene_(scene_in), - collision_cache_(new base::CollisionCache()), + collision_cache_(std::make_unique()), impl_(std::make_unique(this)) { ResetODE_(); } Dynamics::~Dynamics() { if (in_process_) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Dynamics going down within Process() call;" " should not happen."); } diff --git a/src/ballistica/scene_v1/dynamics/material/material_component.cc b/src/ballistica/scene_v1/dynamics/material/material_component.cc index f3eec408..2942d81a 100644 --- a/src/ballistica/scene_v1/dynamics/material/material_component.cc +++ b/src/ballistica/scene_v1/dynamics/material/material_component.cc @@ -215,9 +215,9 @@ void MaterialComponent::Restore(const char** buffer, ClientSession* cs) { action = Object::New(); break; default: - Log(LogLevel::kError, "Invalid material action: '" - + std::to_string(static_cast(type)) - + "'."); + Log(LogName::kBa, LogLevel::kError, + "Invalid material action: '" + + std::to_string(static_cast(type)) + "'."); throw Exception(); } action->Restore(buffer, cs); diff --git a/src/ballistica/scene_v1/dynamics/part.cc b/src/ballistica/scene_v1/dynamics/part.cc index 81b1ecb4..8a5b1ada 100644 --- a/src/ballistica/scene_v1/dynamics/part.cc +++ b/src/ballistica/scene_v1/dynamics/part.cc @@ -99,7 +99,7 @@ void Part::SetCollidingWith(int64_t node_id, int part, bool colliding, for (auto&& i : collisions_) { if (i.node == node_id && i.part == part) { BA_PRECONDITION(node()); - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Got SetCollidingWith for part already colliding with."); return; } @@ -120,7 +120,7 @@ void Part::SetCollidingWith(int64_t node_id, int part, bool colliding, return; } } - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Got SetCollidingWith (separated) call for part we're " "not colliding with."); } diff --git a/src/ballistica/scene_v1/dynamics/rigid_body.cc b/src/ballistica/scene_v1/dynamics/rigid_body.cc index 670ebcae..66c46583 100644 --- a/src/ballistica/scene_v1/dynamics/rigid_body.cc +++ b/src/ballistica/scene_v1/dynamics/rigid_body.cc @@ -212,7 +212,7 @@ void RigidBody::Check() { if (std::isnan(q[3])) err = true; if (err) { - Log(LogLevel::kError, "Got error in rbd values!"); + Log(LogName::kBa, LogLevel::kError, "Got error in rbd values!"); } #if BA_DEBUG_BUILD for (int i = 0; i < 3; i++) { diff --git a/src/ballistica/scene_v1/node/combine_node.cc b/src/ballistica/scene_v1/node/combine_node.cc index 477e662d..54d78ffb 100644 --- a/src/ballistica/scene_v1/node/combine_node.cc +++ b/src/ballistica/scene_v1/node/combine_node.cc @@ -44,7 +44,8 @@ auto CombineNode::GetOutput() -> std::vector { if (dirty_) { if (do_size_unset_warning_) { do_size_unset_warning_ = false; - BA_LOG_ONCE(LogLevel::kError, "CombineNode size unset for " + label()); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "CombineNode size unset for " + label()); } int actual_size = std::min(4, std::max(0, size_)); output_.resize(static_cast(actual_size)); diff --git a/src/ballistica/scene_v1/node/globals_node.cc b/src/ballistica/scene_v1/node/globals_node.cc index 83b05fdb..91763abe 100644 --- a/src/ballistica/scene_v1/node/globals_node.cc +++ b/src/ballistica/scene_v1/node/globals_node.cc @@ -112,7 +112,7 @@ GlobalsNode::GlobalsNode(Scene* scene) : Node(scene, node_type) { // FIXME: Need to update this for non-host activities at some point. if (HostActivity* ha = context_ref().GetHostActivity()) { if (ha->globals_node()) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "More than one globals node created in HostActivity; this " "shouldn't happen"); } @@ -184,7 +184,7 @@ void GlobalsNode::SetAsForeground() { if (g_base->HaveClassic()) { g_base->classic()->PlayMusic(music_, music_continuous_); } else { - BA_LOG_ONCE(LogLevel::kWarning, + BA_LOG_ONCE(LogName::kBa, LogLevel::kWarning, "Classic not present; music will not play."); } } @@ -480,7 +480,7 @@ void GlobalsNode::SetMusicCount(int val) { if (g_base->HaveClassic()) { g_base->classic()->PlayMusic(music_, music_continuous_); } else { - BA_LOG_ONCE(LogLevel::kWarning, + BA_LOG_ONCE(LogName::kBa, LogLevel::kWarning, "Classic not present; music will not play (b)."); } // g_classic->python->PlayMusic(music_, music_continuous_); diff --git a/src/ballistica/scene_v1/node/node.cc b/src/ballistica/scene_v1/node/node.cc index 24fcd99d..27be238f 100644 --- a/src/ballistica/scene_v1/node/node.cc +++ b/src/ballistica/scene_v1/node/node.cc @@ -18,7 +18,7 @@ namespace ballistica::scene_v1 { NodeType::~NodeType() { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SHOULD NOT BE DESTRUCTING A TYPE type=(" + name_ + ")"); } @@ -265,7 +265,7 @@ void Node::DispatchOutOfBoundsMessage() { if (instance.Exists()) { DispatchUserMessage(instance.Get(), "Node OutOfBoundsMessage dispatch"); } else { - Log(LogLevel::kError, "Error creating OutOfBoundsMessage"); + Log(LogName::kBa, LogLevel::kError, "Error creating OutOfBoundsMessage"); } } @@ -282,7 +282,7 @@ void Node::DispatchPickUpMessage(Node* node) { if (instance.Exists()) { DispatchUserMessage(instance.Get(), "Node PickUpMessage dispatch"); } else { - Log(LogLevel::kError, "Error creating PickUpMessage"); + Log(LogName::kBa, LogLevel::kError, "Error creating PickUpMessage"); } } @@ -297,7 +297,7 @@ void Node::DispatchDropMessage() { if (instance.Exists()) { DispatchUserMessage(instance.Get(), "Node DropMessage dispatch"); } else { - Log(LogLevel::kError, "Error creating DropMessage"); + Log(LogName::kBa, LogLevel::kError, "Error creating DropMessage"); } } @@ -315,7 +315,7 @@ void Node::DispatchPickedUpMessage(Node* by_node) { if (instance.Exists()) { DispatchUserMessage(instance.Get(), "Node PickedUpMessage dispatch"); } else { - Log(LogLevel::kError, "Error creating PickedUpMessage"); + Log(LogName::kBa, LogLevel::kError, "Error creating PickedUpMessage"); } } @@ -333,7 +333,7 @@ void Node::DispatchDroppedMessage(Node* by_node) { if (instance.Exists()) { DispatchUserMessage(instance.Get(), "Node DroppedMessage dispatch"); } else { - Log(LogLevel::kError, "Error creating DroppedMessage"); + Log(LogName::kBa, LogLevel::kError, "Error creating DroppedMessage"); } } @@ -348,7 +348,7 @@ void Node::DispatchShouldShatterMessage() { if (instance.Exists()) { DispatchUserMessage(instance.Get(), "Node ShouldShatterMessage dispatch"); } else { - Log(LogLevel::kError, "Error creating ShouldShatterMessage"); + Log(LogName::kBa, LogLevel::kError, "Error creating ShouldShatterMessage"); } } @@ -364,7 +364,7 @@ void Node::DispatchImpactDamageMessage(float intensity) { if (instance.Exists()) { DispatchUserMessage(instance.Get(), "Node ImpactDamageMessage dispatch"); } else { - Log(LogLevel::kError, "Error creating ImpactDamageMessage"); + Log(LogName::kBa, LogLevel::kError, "Error creating ImpactDamageMessage"); } } @@ -392,7 +392,7 @@ void Node::DispatchUserMessage(PyObject* obj, const char* label) { c.Call(PythonRef(Py_BuildValue("(O)", obj), PythonRef::kSteal)); } } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::string("Error in handlemessage() with message ") + PythonRef(obj, PythonRef::kAcquire).Str() + ": '" + e.what() + "'"); diff --git a/src/ballistica/scene_v1/node/node_attribute.cc b/src/ballistica/scene_v1/node/node_attribute.cc index bc3e2cf5..84c912ab 100644 --- a/src/ballistica/scene_v1/node/node_attribute.cc +++ b/src/ballistica/scene_v1/node/node_attribute.cc @@ -52,7 +52,7 @@ auto NodeAttributeUnbound::GetNodeAttributeTypeName(NodeAttributeType t) case NodeAttributeType::kCollisionMeshArray: return "collision-mesh-array"; default: - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Unknown attr type name: " + std::to_string(static_cast(t))); return "unknown"; } @@ -102,7 +102,8 @@ void NodeAttributeUnbound::DisconnectIncoming(Node* node) { #if BA_DEBUG_BUILD if (test_ref.Exists()) { - Log(LogLevel::kError, "Attr connection still exists after ref releases!"); + Log(LogName::kBa, LogLevel::kError, + "Attr connection still exists after ref releases!"); } #endif } diff --git a/src/ballistica/scene_v1/node/node_attribute_connection.cc b/src/ballistica/scene_v1/node/node_attribute_connection.cc index 49c9cdcd..95d4bf5f 100644 --- a/src/ballistica/scene_v1/node/node_attribute_connection.cc +++ b/src/ballistica/scene_v1/node/node_attribute_connection.cc @@ -97,7 +97,7 @@ void NodeAttributeConnection::Update() { src_node->type()->GetAttribute(src_attr_index); NodeAttributeUnbound* dst_attr = dst_node->type()->GetAttribute(dst_attr_index); - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Attribute connection update: " + std::string(e.what()) + "; srcAttr='" + src_attr->name() + "', src_node='" + src_node->type()->name() + "', srcNodeName='" diff --git a/src/ballistica/scene_v1/node/prop_node.cc b/src/ballistica/scene_v1/node/prop_node.cc index b6cadb67..71aa0a66 100644 --- a/src/ballistica/scene_v1/node/prop_node.cc +++ b/src/ballistica/scene_v1/node/prop_node.cc @@ -274,7 +274,7 @@ void PropNode::SetBody(const std::string& val) { // we're ok with redundant sets, but complain/ignore if they try to switch.. if (body_.Exists()) { if (body_type_ != body_type || shape_ != shape) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "body attr can not be changed from its initial value"); return; } @@ -436,8 +436,9 @@ void PropNode::Step() { if (body_type_ == BodyType::UNSET) { if (!reported_unset_body_type_) { reported_unset_body_type_ = true; - Log(LogLevel::kError, "prop-node " + GetObjectDescription() - + " did not have its 'body' attr set."); + Log(LogName::kBa, LogLevel::kError, + "prop-node " + GetObjectDescription() + + " did not have its 'body' attr set."); return; } } diff --git a/src/ballistica/scene_v1/node/region_node.cc b/src/ballistica/scene_v1/node/region_node.cc index 28dd98dc..9d1cee25 100644 --- a/src/ballistica/scene_v1/node/region_node.cc +++ b/src/ballistica/scene_v1/node/region_node.cc @@ -92,7 +92,7 @@ void RegionNode::Step() { RigidBody::kCollideRegion, RigidBody::kCollideActive); } else { if (region_type_ != "box") { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Got unexpected region type: " + region_type_); } body_ = Object::New( diff --git a/src/ballistica/scene_v1/node/sound_node.cc b/src/ballistica/scene_v1/node/sound_node.cc index 4f4f4f4e..3df87d30 100644 --- a/src/ballistica/scene_v1/node/sound_node.cc +++ b/src/ballistica/scene_v1/node/sound_node.cc @@ -84,7 +84,7 @@ void SoundNode::SetLoop(bool val) { // We don't actually update looping on a playing sound. if (playing_) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Can't set 'loop' attr on already-playing sound."); } } @@ -106,7 +106,7 @@ void SoundNode::SetPositional(bool val) { } positional_ = val; if (playing_) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Can't set 'positional' attr on already-playing sound"); } } diff --git a/src/ballistica/scene_v1/node/spaz_node.cc b/src/ballistica/scene_v1/node/spaz_node.cc index 13db24b9..dca3f7e6 100644 --- a/src/ballistica/scene_v1/node/spaz_node.cc +++ b/src/ballistica/scene_v1/node/spaz_node.cc @@ -5977,7 +5977,7 @@ auto SpazNode::GetRigidBody(int id) -> RigidBody* { return hair_ponytail_bottom_body_.Get(); break; default: - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Request for unknown spaz body: " + std::to_string(id)); break; } @@ -6568,7 +6568,8 @@ void SpazNode::SetStyle(const std::string& val) { shoulder_offset_y_ = -0.05f; reflection_scale_ = 0.02f; } else { - BA_LOG_ONCE(LogLevel::kError, "Unrecognized spaz style: '" + style_ + "'"); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "Unrecognized spaz style: '" + style_ + "'"); } UpdateBodiesForStyle(); } @@ -6697,25 +6698,25 @@ void SpazNode::SetHoldNode(Node* val) { assert(dynamics); Collision* c = dynamics->active_collision(); if (c) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SRC NODE: " + ObjToString(dynamics->GetActiveCollideSrcNode())); - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "OPP NODE: " + ObjToString(dynamics->GetActiveCollideDstNode())); - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SRC BODY " + std::to_string(dynamics->GetCollideMessageReverseOrder() ? c->body_id_1 : c->body_id_2)); - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "OPP BODY " + std::to_string(dynamics->GetCollideMessageReverseOrder() ? c->body_id_2 : c->body_id_1)); - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "REVERSE " + std::to_string(dynamics->GetCollideMessageReverseOrder())); } else { - Log(LogLevel::kError, ""); + Log(LogName::kBa, LogLevel::kError, ""); } } throw Exception("specified hold_body (" + std::to_string(hold_body_) diff --git a/src/ballistica/scene_v1/node/text_node.cc b/src/ballistica/scene_v1/node/text_node.cc index 58acbf58..9c6e932c 100644 --- a/src/ballistica/scene_v1/node/text_node.cc +++ b/src/ballistica/scene_v1/node/text_node.cc @@ -120,11 +120,12 @@ void TextNode::SetText(const std::string& val) { g_base->assets->CompileResourceString(val, "setText format check", &valid); if (!valid) { - BA_LOG_ONCE(LogLevel::kError, "Invalid resource string: '" + val - + "' on node '" + label() + "'"); + BA_LOG_ONCE( + LogName::kBa, LogLevel::kError, + "Invalid resource string: '" + val + "' on node '" + label() + "'"); Python::PrintStackTrace(); } else if (print_false_positives) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Got false positive for json check on '" + val + "'"); Python::PrintStackTrace(); } diff --git a/src/ballistica/scene_v1/python/class/python_class_input_device.cc b/src/ballistica/scene_v1/python/class/python_class_input_device.cc index a73add29..82dd67d3 100644 --- a/src/ballistica/scene_v1/python/class/python_class_input_device.cc +++ b/src/ballistica/scene_v1/python/class/python_class_input_device.cc @@ -438,7 +438,7 @@ auto PythonClassInputDevice::GetButtonName(PythonClassInputDevice* self, .Get(base::BasePython::ObjID::kLstrFromJsonCall) .Call(args2); if (!results.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Error creating Lstr from raw button name: '" + bname + "'"); PythonRef args3(Py_BuildValue("(s)", "?"), PythonRef::kSteal); results = g_base->python->objs() diff --git a/src/ballistica/scene_v1/python/class/python_class_session_player.cc b/src/ballistica/scene_v1/python/class/python_class_session_player.cc index 2b8a46b8..e5d55e6c 100644 --- a/src/ballistica/scene_v1/python/class/python_class_session_player.cc +++ b/src/ballistica/scene_v1/python/class/python_class_session_player.cc @@ -271,9 +271,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" - + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "Calling getAttr for player attr '" + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyCharacter(); Py_INCREF(obj); @@ -284,9 +284,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" - + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "Calling getAttr for player attr '" + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyColor(); Py_INCREF(obj); @@ -297,9 +297,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" - + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "Calling getAttr for player attr '" + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyHighlight(); Py_INCREF(obj); @@ -310,9 +310,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" - + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "Calling getAttr for player attr '" + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyActivityPlayer(); Py_INCREF(obj); diff --git a/src/ballistica/scene_v1/python/methods/python_methods_input.cc b/src/ballistica/scene_v1/python/methods/python_methods_input.cc index b39df097..e90c72fb 100644 --- a/src/ballistica/scene_v1/python/methods/python_methods_input.cc +++ b/src/ballistica/scene_v1/python/methods/python_methods_input.cc @@ -227,7 +227,7 @@ static auto PyGetUIInputDevice(PyObject* self, PyObject* args, PyObject* keywds) // Assuming this would be due to getting called in another app-mode. // Wonder if it would be wise to error in that case... BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBa, LogLevel::kWarning, "scene_v1: Found unexpected delegate " + (delegate ? delegate->GetObjectDescription() : "(nullptr)") + " for ui-input-device " + d->GetObjectDescription() + "."); diff --git a/src/ballistica/scene_v1/python/methods/python_methods_networking.cc b/src/ballistica/scene_v1/python/methods/python_methods_networking.cc index c553f159..573ee93a 100644 --- a/src/ballistica/scene_v1/python/methods/python_methods_networking.cc +++ b/src/ballistica/scene_v1/python/methods/python_methods_networking.cc @@ -484,7 +484,7 @@ static auto PyGetConnectionToHostInfo(PyObject* self, PyObject* args, const_cast(kwlist))) { return nullptr; } - BA_LOG_ONCE(LogLevel::kWarning, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kWarning, "bascenev1.get_connection_to_host_info() is deprecated; use " "bascenev1.get_connection_to_host_info_2()."); BA_PRECONDITION(g_base->InLogicThread()); @@ -711,7 +711,7 @@ static auto PySetMasterServerSource(PyObject* self, PyObject* args) int source; if (!PyArg_ParseTuple(args, "i", &source)) return nullptr; if (source != 0 && source != 1) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "Invalid server source: " + std::to_string(source) + "."); source = 1; } diff --git a/src/ballistica/scene_v1/python/methods/python_methods_scene.cc b/src/ballistica/scene_v1/python/methods/python_methods_scene.cc index d450a6f6..b39894f5 100644 --- a/src/ballistica/scene_v1/python/methods/python_methods_scene.cc +++ b/src/ballistica/scene_v1/python/methods/python_methods_scene.cc @@ -653,7 +653,7 @@ static auto PyBroadcastMessage(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } if (log) { - Log(LogLevel::kInfo, message); + Log(LogName::kBaNetworking, LogLevel::kInfo, message); } // Transient messages get sent to clients as high-level messages instead of @@ -751,7 +751,8 @@ static auto PyBroadcastMessage(PyObject* self, PyObject* args, PyObject* keywds) tint_color.x, tint_color.y, tint_color.z, tint2_color.x, tint2_color.y, tint2_color.z); } else { - Log(LogLevel::kError, "Unhandled screenmessage output_stream case."); + Log(LogName::kBaNetworking, LogLevel::kError, + "Unhandled screenmessage output_stream case."); } } @@ -861,7 +862,7 @@ static auto PyPrintNodes(PyObject* self, PyObject* args) -> PyObject* { snprintf(buffer, sizeof(buffer), "#%d: type: %-14s desc: %s", count, i->type()->name().c_str(), i->label().c_str()); s += buffer; - Log(LogLevel::kInfo, buffer); + Log(LogName::kBa, LogLevel::kInfo, buffer); count++; } Py_RETURN_NONE; diff --git a/src/ballistica/scene_v1/python/scene_v1_python.cc b/src/ballistica/scene_v1/python/scene_v1_python.cc index bc12fdbe..a4f9750b 100644 --- a/src/ballistica/scene_v1/python/scene_v1_python.cc +++ b/src/ballistica/scene_v1/python/scene_v1_python.cc @@ -367,9 +367,10 @@ auto SceneV1Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { attr_vals.emplace_back( t->GetAttribute(std::string(PyUnicode_AsUTF8(key))), value); } catch (const std::exception&) { - Log(LogLevel::kError, "Attr not found on initial attr set: '" - + std::string(PyUnicode_AsUTF8(key)) + "' on " - + type + " node '" + name + "'"); + Log(LogName::kBa, LogLevel::kError, + "Attr not found on initial attr set: '" + + std::string(PyUnicode_AsUTF8(key)) + "' on " + type + + " node '" + name + "'"); } } @@ -379,9 +380,9 @@ auto SceneV1Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { try { SetNodeAttr(node, i.first->name().c_str(), i.second); } catch (const std::exception& e) { - Log(LogLevel::kError, "Exception in initial attr set for attr '" - + i.first->name() + "' on " + type + " node '" - + name + "':" + e.what()); + Log(LogName::kBa, LogLevel::kError, + "Exception in initial attr set for attr '" + i.first->name() + + "' on " + type + " node '" + name + "':" + e.what()); } } } @@ -394,11 +395,11 @@ auto SceneV1Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { if (PythonClassNode::Check(owner_obj)) { Node* owner_node = GetPyNode(owner_obj, true); if (owner_node == nullptr) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Empty node-ref passed for 'owner'; pass None if you want " "no owner."); } else if (owner_node->scene() != node->scene()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Owner node is from a different scene; ignoring."); } else { owner_node->AddDependentNode(node); @@ -419,9 +420,9 @@ auto SceneV1Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { } node->OnCreate(); } catch (const std::exception& e) { - Log(LogLevel::kError, "Exception in OnCreate() for node " - + ballistica::ObjToString(node) - + "':" + e.what()); + Log(LogName::kBa, LogLevel::kError, + "Exception in OnCreate() for node " + ballistica::ObjToString(node) + + "':" + e.what()); } return node; @@ -1117,7 +1118,7 @@ auto SceneV1Python::FilterChatMessage(std::string* message, int client_id) // This string data can be coming straight in off the network; need // to avoid letting malicious garbage through to Python api. if (!Utils::IsValidUTF8(*message)) { - BA_LOG_ONCE(LogLevel::kWarning, + BA_LOG_ONCE(LogName::kBa, LogLevel::kWarning, "FilterChatMessage got invalid UTF8 data; could be an attack."); return false; } @@ -1140,7 +1141,7 @@ auto SceneV1Python::FilterChatMessage(std::string* message, int client_id) try { *message = g_base->python->GetPyLString(result.Get()); } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Error getting string from chat filter: " + std::string(e.what())); } return true; @@ -1400,7 +1401,7 @@ auto SceneV1Python::HandleCapturedKeyPress(const SDL_Keysym& keysym) -> bool { keyboard_capture_call_.Call(args); } else { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBa, LogLevel::kWarning, "Python key-press callbacks do not work with this input-device class."); } return true; @@ -1425,7 +1426,7 @@ auto SceneV1Python::HandleCapturedKeyRelease(const SDL_Keysym& keysym) -> bool { keyboard_capture_call_.Call(args); } else { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBa, LogLevel::kWarning, "Python key-press callbacks do not work with this input-device class."); } return true; @@ -1497,7 +1498,7 @@ auto SceneV1Python::HandleCapturedJoystickEvent(const SDL_Event& event, } } else { BA_LOG_ONCE( - LogLevel::kWarning, + LogName::kBa, LogLevel::kWarning, "Python key-press callbacks do not work with this input-device class."); } return true; diff --git a/src/ballistica/scene_v1/support/client_input_device_delegate.cc b/src/ballistica/scene_v1/support/client_input_device_delegate.cc index 6d3cd3b9..8fbcbafe 100644 --- a/src/ballistica/scene_v1/support/client_input_device_delegate.cc +++ b/src/ballistica/scene_v1/support/client_input_device_delegate.cc @@ -65,7 +65,7 @@ auto ClientInputDeviceDelegate::GetClientID() const -> int { if (ConnectionToClient* c = connection_to_client_.Get()) { return c->id(); } else { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "ClientInputDevice::get_client_id(): connection_to_client no longer " "exists; returning -1.."); return -1; diff --git a/src/ballistica/scene_v1/support/client_session.cc b/src/ballistica/scene_v1/support/client_session.cc index 0c052a0c..bd60117b 100644 --- a/src/ballistica/scene_v1/support/client_session.cc +++ b/src/ballistica/scene_v1/support/client_session.cc @@ -200,7 +200,7 @@ void ClientSession::Update(int time_advance_millisecs, double time_advance) { if (g_buildconfig.debug_build()) { if (current_cmd_ptr_ != nullptr) { if (current_cmd_ptr_ != &(current_cmd_[0]) + current_cmd_.size()) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "SIZE ERROR FOR CMD " + std::to_string(static_cast(current_cmd_[0])) + " expected " + std::to_string(current_cmd_.size()) @@ -969,7 +969,8 @@ auto ClientSession::GetCollisionMesh(int id) const -> SceneCollisionMesh* { } void ClientSession::Error(const std::string& description) { - Log(LogLevel::kError, "Client session error: " + description); + Log(LogName::kBaNetworking, LogLevel::kError, + "Client session error: " + description); End(); } diff --git a/src/ballistica/scene_v1/support/client_session_net.cc b/src/ballistica/scene_v1/support/client_session_net.cc index 1269e1e0..c7953b1f 100644 --- a/src/ballistica/scene_v1/support/client_session_net.cc +++ b/src/ballistica/scene_v1/support/client_session_net.cc @@ -17,7 +17,7 @@ namespace ballistica::scene_v1 { ClientSessionNet::ClientSessionNet() { // Sanity check: we should only ever be writing one replay at once. if (g_scene_v1->replay_open) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "g_scene_v1->replay_open true at netclient start;" " shouldn't happen."); } @@ -33,7 +33,7 @@ ClientSessionNet::~ClientSessionNet() { if (writing_replay_) { // Sanity check: we should only ever be writing one replay at once. if (!g_scene_v1->replay_open) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "g_scene_v1->replay_open false at net-client close;" " shouldn't happen."); } diff --git a/src/ballistica/scene_v1/support/client_session_replay.cc b/src/ballistica/scene_v1/support/client_session_replay.cc index 50dc9583..890fbea9 100644 --- a/src/ballistica/scene_v1/support/client_session_replay.cc +++ b/src/ballistica/scene_v1/support/client_session_replay.cc @@ -69,7 +69,7 @@ void ClientSessionReplay::OnClientConnected(ConnectionToClient* c) { // sanity check - abort if its on either of our lists already for (ConnectionToClient* i : connections_to_clients_) { if (i == c) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "ReplayClientSession::OnClientConnected()" " got duplicate connection"); return; @@ -77,7 +77,7 @@ void ClientSessionReplay::OnClientConnected(ConnectionToClient* c) { } for (ConnectionToClient* i : connections_to_clients_ignored_) { if (i == c) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "ReplayClientSession::OnClientConnected()" " got duplicate connection"); return; @@ -139,7 +139,7 @@ void ClientSessionReplay::OnClientDisconnected(ConnectionToClient* c) { return; } } - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "ReplayClientSession::OnClientDisconnected()" " called for connection not on lists"); } diff --git a/src/ballistica/scene_v1/support/host_activity.cc b/src/ballistica/scene_v1/support/host_activity.cc index e0dd1582..611b688e 100644 --- a/src/ballistica/scene_v1/support/host_activity.cc +++ b/src/ballistica/scene_v1/support/host_activity.cc @@ -111,7 +111,7 @@ HostActivity::~HostActivity() { for (auto& python_call : context_calls_) s += "\n " + std::to_string(count++) + ": " + (*python_call).GetObjectDescription(); - Log(LogLevel::kWarning, s); + Log(LogName::kBa, LogLevel::kWarning, s); } } } @@ -157,7 +157,7 @@ void HostActivity::RegisterContextCall(base::PythonContextCall* call) { // If we're shutting down, just kill the call immediately. // (we turn all of our calls to no-ops as we shut down) if (shutting_down_) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "Adding call to expired activity; call will not function: " + call->GetObjectDescription()); call->MarkDead(); @@ -166,18 +166,19 @@ void HostActivity::RegisterContextCall(base::PythonContextCall* call) { void HostActivity::Start() { if (started_) { - Log(LogLevel::kError, "HostActivity::Start() called twice."); + Log(LogName::kBa, LogLevel::kError, "HostActivity::Start() called twice."); return; } started_ = true; if (shutting_down_) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "HostActivity::Start() called for shutting-down activity."); return; } auto* host_session = host_session_.Get(); if (!host_session) { - Log(LogLevel::kError, "HostActivity::Start() called with dead session."); + Log(LogName::kBa, LogLevel::kError, + "HostActivity::Start() called with dead session."); return; } // Create our step timer - gets called whenever scene should step. @@ -289,7 +290,7 @@ void HostActivity::HandleOutOfBoundsNodes() { // Make sure someone's handling our out-of-bounds messages. out_of_bounds_in_a_row_++; if (out_of_bounds_in_a_row_ > 100) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "100 consecutive out-of-bounds messages sent." " They are probably not being handled properly"); int j = 0; @@ -302,7 +303,7 @@ void HostActivity::HandleOutOfBoundsNodes() { if (delegate) { dstr = PythonRef(delegate, PythonRef::kAcquire).Str(); } - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, " node #" + std::to_string(j) + ": type='" + n->type()->name() + "' addr=" + Utils::PtrToString(i.Get()) + " name='" + n->label() + "' delegate=" + dstr); diff --git a/src/ballistica/scene_v1/support/host_session.cc b/src/ballistica/scene_v1/support/host_session.cc index 82310276..bb61ae4c 100644 --- a/src/ballistica/scene_v1/support/host_session.cc +++ b/src/ballistica/scene_v1/support/host_session.cc @@ -218,7 +218,7 @@ void HostSession::RequestPlayer(SceneV1InputDeviceDelegate* device) { // Ignore if we have no Python session Obj. if (!GetSessionPyObj()) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "HostSession::RequestPlayer() called w/no session_py_obj_."); return; } @@ -312,12 +312,12 @@ void HostSession::IssuePlayerLeft(Player* player) { BA_LOG_PYTHON_TRACE_ONCE("missing player on IssuePlayerLeft"); } } else { - Log(LogLevel::kWarning, + Log(LogName::kBaNetworking, LogLevel::kWarning, "HostSession: IssuePlayerLeft caled with no " "session_py_obj_"); } } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, std::string("Error calling on_player_leave(): ") + e.what()); } } @@ -338,7 +338,7 @@ void HostSession::SetForegroundHostActivity(HostActivity* a) { auto* appmode = classic::ClassicAppMode::GetActiveOrFatal(); if (shutting_down_) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "SetForegroundHostActivity called during session shutdown; " "ignoring."); return; @@ -509,6 +509,16 @@ void HostSession::Update(int time_advance_millisecs, double time_advance) { } } + // Shouldn't be getting huge steps through here; watch out for that. + if (time_advance_millisecs > 500 || time_advance > 0.5) { + printf( + "WARNING: HostSession::Update() got excessive time_advance " + "(%d ms, %f s); should not happen.\n", + time_advance_millisecs, time_advance); + } + + // printf("ADV %d %f\n", time_advance_millisecs, time_advance); + // We can be killed at any time, so let's keep an eye out for that. WeakRef test_ref(this); assert(test_ref.Exists()); @@ -518,8 +528,8 @@ void HostSession::Update(int time_advance_millisecs, double time_advance) { SessionStream* output_stream = GetSceneStream(); auto too_slow{false}; - // Try to advance our base time by the provided amount, - // firing all timers along the way. + // Try to advance our base time by the provided amount, firing all timers + // along the way. millisecs_t target_base_time_millisecs = base_time_millisecs_ + time_advance_millisecs; while (!base_timers_.Empty() @@ -656,11 +666,11 @@ HostSession::~HostSession() { s += ("\n " + std::to_string(count++) + ": " + i->GetObjectDescription()); } - Log(LogLevel::kWarning, s); + Log(LogName::kBa, LogLevel::kWarning, s); } } } catch (const std::exception& e) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Exception in HostSession destructor: " + std::string(e.what())); } } @@ -678,7 +688,7 @@ void HostSession::RegisterContextCall(base::PythonContextCall* call) { // If we're shutting down, just kill the call immediately. // (we turn all of our calls to no-ops as we shut down). if (shutting_down_) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "Adding call to expired session; call will not function: " + call->GetObjectDescription()); call->MarkDead(); diff --git a/src/ballistica/scene_v1/support/player_spec.cc b/src/ballistica/scene_v1/support/player_spec.cc index 3a4fc27f..0b7140a7 100644 --- a/src/ballistica/scene_v1/support/player_spec.cc +++ b/src/ballistica/scene_v1/support/player_spec.cc @@ -39,7 +39,8 @@ PlayerSpec::PlayerSpec(const std::string& s) { cJSON_Delete(root_obj); } if (!success) { - Log(LogLevel::kError, "Error creating PlayerSpec from string: '" + s + "'"); + Log(LogName::kBa, LogLevel::kError, + "Error creating PlayerSpec from string: '" + s + "'"); name_ = ""; short_name_ = ""; // account_type_ = classic::V1AccountType::kInvalid; @@ -113,7 +114,8 @@ auto PlayerSpec::GetAccountPlayerSpec() -> PlayerSpec { } if (spec.name_.size() > 100) { // FIXME should perhaps clamp this in unicode space - Log(LogLevel::kError, "account name size too long: '" + spec.name_ + "'"); + Log(LogName::kBa, LogLevel::kError, + "account name size too long: '" + spec.name_ + "'"); spec.name_.resize(100); spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgaps3"); } @@ -125,7 +127,7 @@ auto PlayerSpec::GetDummyPlayerSpec(const std::string& name) -> PlayerSpec { spec.name_ = Utils::GetValidUTF8(name.c_str(), "bsgdps1"); if (spec.name_.size() > 100) { // FIXME should perhaps clamp this in unicode space - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "dummy player spec name too long: '" + spec.name_ + "'"); spec.name_.resize(100); spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgdps2"); diff --git a/src/ballistica/scene_v1/support/scene.cc b/src/ballistica/scene_v1/support/scene.cc index 3b5c610a..1e441e93 100644 --- a/src/ballistica/scene_v1/support/scene.cc +++ b/src/ballistica/scene_v1/support/scene.cc @@ -87,7 +87,8 @@ void Scene::PlaySound(SceneSound* sound, float volume, bool host_only) { auto Scene::IsOutOfBounds(float x, float y, float z) -> bool { if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isinf(x) || std::isinf(y) || std::isinf(z)) - BA_LOG_ONCE(LogLevel::kError, "Got INF/NAN value on IsOutOfBounds() check"); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "Got INF/NAN value on IsOutOfBounds() check"); return ((x < bounds_min_[0]) || (x > bounds_max_[0]) || (y < bounds_min_[1]) || (y > bounds_max_[1]) || (z < bounds_min_[2]) @@ -215,7 +216,8 @@ void Scene::DeleteNode(Node* node) { // Sanity test: at this point the node should be dead. #if BA_DEBUG_BUILD if (temp_weak_ref.Exists()) { - Log(LogLevel::kError, "Node still exists after ref release!!"); + Log(LogName::kBa, LogLevel::kError, + "Node still exists after ref release!!"); } #endif // BA_DEBUG_BUILD @@ -399,7 +401,7 @@ void Scene::DumpNodes(SessionStream* out) { break; } default: - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Invalid attr type for Scene::DumpNodes() attr set: " + std::to_string(static_cast(attr.type()))); break; diff --git a/src/ballistica/scene_v1/support/scene_v1_context.cc b/src/ballistica/scene_v1/support/scene_v1_context.cc index 7739a9c5..e9d2a853 100644 --- a/src/ballistica/scene_v1/support/scene_v1_context.cc +++ b/src/ballistica/scene_v1/support/scene_v1_context.cc @@ -77,7 +77,8 @@ auto SceneV1Context::NewTimer(TimeType timetype, TimerMedium length, void SceneV1Context::DeleteTimer(TimeType timetype, int timer_id) { // We throw on NewTimer; lets just ignore anything that comes // through here to avoid messing up destructors. - Log(LogLevel::kError, "ContextTarget::DeleteTimer() called; unexpected."); + Log(LogName::kBa, LogLevel::kError, + "ContextTarget::DeleteTimer() called; unexpected."); } auto SceneV1Context::GetTime(TimeType timetype) -> millisecs_t { diff --git a/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc b/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc index 2694058b..a318201c 100644 --- a/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc +++ b/src/ballistica/scene_v1/support/scene_v1_input_device_delegate.cc @@ -65,13 +65,13 @@ void SceneV1InputDeviceDelegate::RequestPlayer() { BA_PRECONDITION_FATAL(appmode); if (player_.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "InputDevice::RequestPlayer()" " called with already-existing player"); return; } if (remote_player_.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "InputDevice::RequestPlayer() called with already-existing " "remote-player"); return; @@ -101,14 +101,14 @@ void SceneV1InputDeviceDelegate::RequestPlayer() { // When the host-session tells us to attach to a player void SceneV1InputDeviceDelegate::AttachToLocalPlayer(Player* player) { if (player_.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "InputDevice::AttachToLocalPlayer() called with already " "existing " "player"); return; } if (remote_player_.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "InputDevice::AttachToLocalPlayer() called with already " "existing " "remote-player"); @@ -122,14 +122,14 @@ void SceneV1InputDeviceDelegate::AttachToRemotePlayer( ConnectionToHost* connection_to_host, int remote_player_id) { assert(connection_to_host); if (player_.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "InputDevice::AttachToRemotePlayer()" " called with already existing " "player"); return; } if (remote_player_.Exists()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "InputDevice::AttachToRemotePlayer()" " called with already existing " "remote-player"); diff --git a/src/ballistica/scene_v1/support/session.cc b/src/ballistica/scene_v1/support/session.cc index 0371fda1..75957529 100644 --- a/src/ballistica/scene_v1/support/session.cc +++ b/src/ballistica/scene_v1/support/session.cc @@ -20,7 +20,7 @@ Session::~Session() { g_scene_v1->session_count--; } void Session::Update(int time_advance_millisecs, double time_advance) {} auto Session::TimeToNextEvent() -> std::optional { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Session::TimeToNextEvent() being called; should not happen."); return 5000000; } @@ -36,7 +36,7 @@ void Session::LanguageChanged() {} void Session::DebugSpeedMultChanged() {} void Session::DumpFullState(SessionStream* out) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "Session::DumpFullState() being called; shouldn't happen."); } diff --git a/src/ballistica/scene_v1/support/session_stream.cc b/src/ballistica/scene_v1/support/session_stream.cc index 4db81709..ca9f9b5e 100644 --- a/src/ballistica/scene_v1/support/session_stream.cc +++ b/src/ballistica/scene_v1/support/session_stream.cc @@ -31,7 +31,7 @@ SessionStream::SessionStream(HostSession* host_session, bool save_replay) if (save_replay) { // Sanity check - we should only ever be writing one replay at once. if (g_scene_v1->replay_open) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "g_scene_v1->replay_open true at replay start;" " shouldn't happen."); } @@ -57,7 +57,7 @@ SessionStream::~SessionStream() { if (writing_replay_) { // Sanity check: We should only ever be writing one replay at once. if (!g_scene_v1->replay_open) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "g_scene_v1->replay_open false at replay close;" " shouldn't happen."); } @@ -78,38 +78,38 @@ SessionStream::~SessionStream() { size_t count; count = GetPointerCount(scenes_); if (count != 0) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::to_string(count) + " scene graphs in output stream at shutdown"); } count = GetPointerCount(nodes_); if (count != 0) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::to_string(count) + " nodes in output stream at shutdown"); } count = GetPointerCount(materials_); if (count != 0) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::to_string(count) + " materials in output stream at shutdown"); } count = GetPointerCount(textures_); if (count != 0) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::to_string(count) + " textures in output stream at shutdown"); } count = GetPointerCount(meshes_); if (count != 0) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::to_string(count) + " meshes in output stream at shutdown"); } count = GetPointerCount(sounds_); if (count != 0) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::to_string(count) + " sounds in output stream at shutdown"); } count = GetPointerCount(collision_meshes_); if (count != 0) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::to_string(count) + " collision_meshes in output stream at shutdown"); } @@ -122,7 +122,7 @@ auto SessionStream::GetOutMessage() const -> std::vector { assert(!host_session_); // this should only be getting used for // standalone temp ones.. if (!out_command_.empty()) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SceneStream shutting down with non-empty outCommand"); } return out_message_; @@ -188,11 +188,11 @@ void SessionStream::Remove(T* val, std::vector* vec, } void SessionStream::Fail() { - Log(LogLevel::kError, "Error writing replay file"); + Log(LogName::kBa, LogLevel::kError, "Error writing replay file"); if (writing_replay_) { // Sanity check: We should only ever be writing one replay at once. if (!g_scene_v1->replay_open) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "g_scene_v1->replay_open false at replay close;" " shouldn't happen."); } @@ -205,7 +205,7 @@ void SessionStream::Fail() { void SessionStream::Flush() { if (!out_command_.empty()) - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SceneStream flushing down with non-empty outCommand"); if (!out_message_.empty()) { ShipSessionCommandsMessage(); @@ -535,7 +535,8 @@ void SessionStream::SetTime(millisecs_t t) { } millisecs_t diff = t - time_; if (diff > 255) { - Log(LogLevel::kError, "SceneStream got time diff > 255; not expected."); + Log(LogName::kBa, LogLevel::kError, + "SceneStream got time diff > 255; not expected."); diff = 255; } WriteCommandInt64(SessionCommand::kBaseTimeStep, diff); @@ -1194,14 +1195,14 @@ void SessionStream::OnClientConnected(ConnectionToClient* c) { // Sanity check - abort if its on either of our lists already. for (auto& connections_to_client : connections_to_clients_) { if (connections_to_client == c) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SceneStream::OnClientConnected() got duplicate connection."); return; } } for (auto& i : connections_to_clients_ignored_) { if (i == c) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SceneStream::OnClientConnected() got duplicate connection."); return; } @@ -1255,7 +1256,7 @@ void SessionStream::OnClientDisconnected(ConnectionToClient* c) { return; } } - Log(LogLevel::kError, + Log(LogName::kBaNetworking, LogLevel::kError, "SceneStream::OnClientDisconnected() called for connection not on " "lists"); } diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index 466beb3c..63d9f092 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -40,7 +40,7 @@ auto main(int argc, char** argv) -> int { namespace ballistica { // These are set automatically via script; don't modify them here. -const int kEngineBuildNumber = 22025; +const int kEngineBuildNumber = 22040; const char* kEngineVersion = "1.7.37"; const int kEngineApiVersion = 9; @@ -134,7 +134,7 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int { auto env_config_duration = time3 - time2; auto base_import_duration = time4 - time3; auto start_app_duration = time5 - time4; - Log(LogLevel::kWarning, + Log(LogName::kBa, 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) @@ -284,13 +284,15 @@ void FatalError(const std::string& message) { FatalError::DoFatalError(message); } -void Log(LogLevel level, const std::string& msg) { Logging::Log(level, msg); } +void Log(LogName name, LogLevel level, const std::string& msg) { + Logging::Log(name, level, msg); +} void ScreenMessage(const std::string& s, const Vector3f& color) { if (core::g_base_soft) { core::g_base_soft->ScreenMessage(s, color); } else { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "ScreenMessage called without base feature-set loaded (will be lost): '" + s + "'"); } diff --git a/src/ballistica/shared/ballistica.h b/src/ballistica/shared/ballistica.h index 083ae9d7..755975c9 100644 --- a/src/ballistica/shared/ballistica.h +++ b/src/ballistica/shared/ballistica.h @@ -13,9 +13,9 @@ #endif // Minimum functionality we want available everywhere we are included. -#include "ballistica/shared/foundation/exception.h" -#include "ballistica/shared/foundation/inline.h" -#include "ballistica/shared/foundation/macros.h" +#include "ballistica/shared/foundation/exception.h" // IWYU pragma: keep. +#include "ballistica/shared/foundation/inline.h" // IWYU pragma: keep. +#include "ballistica/shared/foundation/macros.h" // IWYU pragma: keep. #include "ballistica/shared/foundation/types.h" // There are one or two places where we include this from regular C or @@ -89,7 +89,7 @@ void ScreenMessage(const std::string& msg, const Vector3f& color); auto CurrentThreadName() -> std::string; /// Convenient access to Logging::Log. -void Log(LogLevel level, const std::string& msg); +void Log(LogName name, LogLevel level, const std::string& msg); /// Log a fatal error and kill the app. Can be called from any thread at any /// time. Provided message will be shown to the user if possible. This will diff --git a/src/ballistica/shared/foundation/event_loop.cc b/src/ballistica/shared/foundation/event_loop.cc index 207a64c1..73616a8a 100644 --- a/src/ballistica/shared/foundation/event_loop.cc +++ b/src/ballistica/shared/foundation/event_loop.cc @@ -545,7 +545,7 @@ void EventLoop::PushThreadMessage_(const ThreadMessage_& t) { // Now log anything we accumulated safely outside of the locked section. for (auto&& log_entry : log_entries) { - Log(log_entry.first, log_entry.second); + Log(LogName::kBa, log_entry.first, log_entry.second); } } @@ -728,8 +728,9 @@ void EventLoop::AcquireGIL_() { if (debug_timing) { auto duration{core::CorePlatform::GetCurrentMillisecs() - startmillisecs}; if (duration > (1000 / 120)) { - Log(LogLevel::kInfo, "GIL acquire took too long (" - + std::to_string(duration) + " millisecs)."); + Log(LogName::kBa, LogLevel::kInfo, + "GIL acquire took too long (" + std::to_string(duration) + + " millisecs)."); } } } diff --git a/src/ballistica/shared/foundation/exception.h b/src/ballistica/shared/foundation/exception.h index e4447ad0..8d533287 100644 --- a/src/ballistica/shared/foundation/exception.h +++ b/src/ballistica/shared/foundation/exception.h @@ -4,7 +4,7 @@ #define BALLISTICA_SHARED_FOUNDATION_EXCEPTION_H_ #ifdef __cplusplus -#include +#include // IWYU pragma: keep. #include #include "ballistica/shared/foundation/types.h" diff --git a/src/ballistica/shared/foundation/fatal_error.cc b/src/ballistica/shared/foundation/fatal_error.cc index 46a433ab..8cfe5d4a 100644 --- a/src/ballistica/shared/foundation/fatal_error.cc +++ b/src/ballistica/shared/foundation/fatal_error.cc @@ -120,7 +120,9 @@ void FatalError::ReportFatalError(const std::string& message, // momentarily, and also go to platform-specific logging and good ol' // stderr. Logging::V1CloudLog(logmsg); - Logging::EmitLog("root", LogLevel::kCritical, logmsg); + + Logging::EmitLog("root", LogLevel::kCritical, + core::CorePlatform::GetSecondsSinceEpoch(), logmsg); fprintf(stderr, "%s\n", logmsg.c_str()); std::string prefix = "FATAL-ERROR-LOG:"; @@ -208,7 +210,9 @@ auto FatalError::HandleFatalError(bool exit_cleanly, // bring the app down ourself. if (!in_top_level_exception_handler) { if (exit_cleanly) { - Logging::EmitLog("root", LogLevel::kCritical, "Calling exit(1)..."); + Logging::EmitLog("root", LogLevel::kCritical, + core::CorePlatform::GetSecondsSinceEpoch(), + "Calling exit(1)..."); // Inform anyone who cares that the engine is going down NOW. // This value can be polled by threads that may otherwise block us @@ -218,7 +222,9 @@ auto FatalError::HandleFatalError(bool exit_cleanly, exit(1); } else { - Logging::EmitLog("root", LogLevel::kCritical, "Calling abort()..."); + Logging::EmitLog("root", LogLevel::kCritical, + core::CorePlatform::GetSecondsSinceEpoch(), + "Calling abort()..."); abort(); } } diff --git a/src/ballistica/shared/foundation/logging.cc b/src/ballistica/shared/foundation/logging.cc index 8601c5ef..c1f24393 100644 --- a/src/ballistica/shared/foundation/logging.cc +++ b/src/ballistica/shared/foundation/logging.cc @@ -2,11 +2,13 @@ #include "ballistica/shared/foundation/logging.h" +#include #include #include "ballistica/core/platform/core_platform.h" #include "ballistica/core/python/core_python.h" #include "ballistica/core/support/base_soft.h" +#include "ballistica/shared/math/vector4f.h" namespace ballistica { @@ -17,16 +19,62 @@ using core::g_core; int g_early_v1_cloud_log_writes{10}; -void Logging::Log(LogLevel level, const std::string& msg) { +void Logging::Log(LogName name, LogLevel level, const std::string& msg) { BA_PRECONDITION(g_core); - g_core->python->LoggingCall(level, msg); + + // Optimization: avoid touching Python at all if this log level isn't + // enabled. + if (!g_core->LogLevelEnabled(name, level)) { + return; + } + + g_core->python->LoggingCall(name, level, msg); } -void Logging::EmitLog(const std::string& name, LogLevel level, +void Logging::EmitLog(const std::string& name, LogLevel level, double timestamp, const std::string& msg) { // Print to the dev console. - if (g_base_soft) { - g_base_soft->PushDevConsolePrintCall(msg + "\n"); + if (name == "stdout" || name == "stderr") { + // Print stdout/stderr entries with no extra info. + g_base_soft->PushDevConsolePrintCall(msg, 1.0f, kVector4f1); + } else { + auto rel_time{timestamp - g_core->ba_env_launch_timestamp()}; + + if (g_base_soft) { + // const char* loglevelname{"?"}; + Vector4f logcolor; + switch (level) { + case LogLevel::kDebug: + // loglevelname = "D"; + logcolor = Vector4f(0.0f, 0.5f, 1.0f, 1.0f); + break; + case LogLevel::kInfo: + // loglevelname = "I"; + logcolor = Vector4f(1.0f, 1.0f, 1.0f, 1.0f); + break; + case LogLevel::kWarning: + // loglevelname = "W"; + logcolor = Vector4f(1.0f, 0.7f, 0.0f, 1.0f); + break; + case LogLevel::kError: + // loglevelname = "E"; + logcolor = Vector4f(1.0f, 0.0, 0.0f, 1.0f); + break; + case LogLevel::kCritical: + // loglevelname = "C"; + logcolor = Vector4f(0.6f, 0.0, 0.25f, 1.0f); + break; + } + char prestr[256]; + snprintf(prestr, sizeof(prestr), "%.3f %s", rel_time, name.c_str()); + // std::string msgfull = prestr + msg; + g_base_soft->PushDevConsolePrintCall("", 0.3f, kVector4f1); + g_base_soft->PushDevConsolePrintCall( + prestr, 0.75f, + Vector4f(logcolor.x * 0.4f + 0.6f, logcolor.y * 0.4f + 0.6f, + logcolor.z * 0.4f + 0.6f, 0.75)); + g_base_soft->PushDevConsolePrintCall(msg, 1.0f, logcolor); + } } // Ship to platform-specific display mechanisms (android log, etc). diff --git a/src/ballistica/shared/foundation/logging.h b/src/ballistica/shared/foundation/logging.h index d08b56e0..a111f82d 100644 --- a/src/ballistica/shared/foundation/logging.h +++ b/src/ballistica/shared/foundation/logging.h @@ -27,12 +27,12 @@ class Logging { /// ever made will be routed through the app, visible in in-app consoles, /// etc. Note that direct Python logging calls or prints occurring before /// babase is imported may not be visible in the app for that same reason. - static void Log(LogLevel level, const std::string& msg); + static void Log(LogName name, LogLevel level, const std::string& msg); /// Send a log message to the in-app console, platform-specific logs, etc. /// This generally should not be called directly but instead wired up to /// log messages coming through the Python logging system. - static void EmitLog(const std::string& name, LogLevel level, + static void EmitLog(const std::string& name, LogLevel level, double timestamp, const std::string& msg); /// Write a message to the v1 cloud log. This is considered legacy and diff --git a/src/ballistica/shared/foundation/macros.cc b/src/ballistica/shared/foundation/macros.cc index 7813dbf0..c77b5120 100644 --- a/src/ballistica/shared/foundation/macros.cc +++ b/src/ballistica/shared/foundation/macros.cc @@ -25,8 +25,9 @@ void MacroFunctionTimerEnd(core::CoreFeatureSet* corefs, millisecs_t starttime, assert(corefs); millisecs_t endtime = corefs->platform->GetTicks(); if (endtime - starttime > time) { - Log(LogLevel::kWarning, std::to_string(endtime - starttime) - + " milliseconds spent in " + funcname); + Log(LogName::kBa, LogLevel::kWarning, + std::to_string(endtime - starttime) + " milliseconds spent in " + + funcname); } } @@ -41,7 +42,7 @@ void MacroFunctionTimerEndThread(core::CoreFeatureSet* corefs, assert(corefs); millisecs_t endtime = corefs->platform->GetTicks(); if (endtime - starttime > time) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, std::to_string(endtime - starttime) + " milliseconds spent by " + ballistica::CurrentThreadName() + " thread in " + funcname); } @@ -58,9 +59,9 @@ void MacroFunctionTimerEndEx(core::CoreFeatureSet* corefs, assert(corefs); millisecs_t endtime = corefs->platform->GetTicks(); if (endtime - starttime > time) { - Log(LogLevel::kWarning, std::to_string(endtime - starttime) - + " milliseconds spent in " + funcname + " for " - + what); + Log(LogName::kBa, LogLevel::kWarning, + std::to_string(endtime - starttime) + " milliseconds spent in " + + funcname + " for " + what); } } @@ -76,10 +77,10 @@ void MacroFunctionTimerEndThreadEx(core::CoreFeatureSet* corefs, assert(corefs); millisecs_t endtime = corefs->platform->GetTicks(); if (endtime - starttime > time) { - Log(LogLevel::kWarning, std::to_string(endtime - starttime) - + " milliseconds spent by " - + ballistica::CurrentThreadName() - + " thread in " + funcname + " for " + what); + Log(LogName::kBa, LogLevel::kWarning, + std::to_string(endtime - starttime) + " milliseconds spent by " + + ballistica::CurrentThreadName() + " thread in " + funcname + + " for " + what); } } @@ -94,7 +95,7 @@ void MacroTimeCheckEnd(core::CoreFeatureSet* corefs, millisecs_t starttime, assert(corefs); millisecs_t e = corefs->platform->GetTicks(); if (e - starttime > time) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, std::string(name) + " took " + std::to_string(e - starttime) + " milliseconds; " + MacroPathFilter(corefs, file) + " line " + std::to_string(line)); @@ -110,7 +111,7 @@ void MacroLogErrorNativeTrace(core::CoreFeatureSet* corefs, auto trace = corefs->platform->GetNativeStackTrace(); auto trace_s = trace ? trace->FormatForDisplay() : ""; - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::string(buffer) + " error: " + msg + "\n" + trace_s); } @@ -124,7 +125,7 @@ void MacroLogErrorPythonTrace(core::CoreFeatureSet* corefs, // Since our logging goes through Python anyway, we should just ask // Python do include the trace in our log call. Python::PrintStackTrace(); - Log(LogLevel::kError, std::string(buffer) + " error: " + msg); + Log(LogName::kBa, LogLevel::kError, std::string(buffer) + " error: " + msg); } void MacroLogError(core::CoreFeatureSet* corefs, const std::string& msg, @@ -132,12 +133,12 @@ void MacroLogError(core::CoreFeatureSet* corefs, const std::string& msg, char e_buffer[2048]; snprintf(e_buffer, sizeof(e_buffer), "%s:%d:", MacroPathFilter(corefs, fname), line); - Log(LogLevel::kError, std::string(e_buffer) + " error: " + msg); + Log(LogName::kBa, LogLevel::kError, std::string(e_buffer) + " error: " + msg); } void MacroLogPythonTrace(core::CoreFeatureSet* corefs, const std::string& msg) { Python::PrintStackTrace(); - Log(LogLevel::kError, msg); + Log(LogName::kBa, LogLevel::kError, msg); } auto MacroPathFilter(core::CoreFeatureSet* corefs, const char* filename) diff --git a/src/ballistica/shared/foundation/macros.h b/src/ballistica/shared/foundation/macros.h index 60ad3b07..a9193c34 100644 --- a/src/ballistica/shared/foundation/macros.h +++ b/src/ballistica/shared/foundation/macros.h @@ -104,14 +104,14 @@ } \ ((void)0) // (see 'Trailing-semicolon note' at top) -#define BA_LOG_ONCE(lvl, msg) \ - { \ - static bool did_log_here{}; \ - if (!did_log_here) { \ - ::ballistica::Log(lvl, msg); \ - did_log_here = true; \ - } \ - } \ +#define BA_LOG_ONCE(nm, lvl, msg) \ + { \ + static bool did_log_here{}; \ + if (!did_log_here) { \ + ::ballistica::Log(nm, lvl, msg); \ + did_log_here = true; \ + } \ + } \ ((void)0) // (see 'Trailing-semicolon note' at top) #define BA_LOG_PYTHON_TRACE(msg) ::ballistica::MacroLogPythonTrace(g_core, msg) diff --git a/src/ballistica/shared/foundation/object.cc b/src/ballistica/shared/foundation/object.cc index f045cf9c..99502cae 100644 --- a/src/ballistica/shared/foundation/object.cc +++ b/src/ballistica/shared/foundation/object.cc @@ -155,9 +155,10 @@ void Object::LsObjects() { assert(count == g_core->object_count); } } - Log(LogLevel::kInfo, s); + Log(LogName::kBa, LogLevel::kInfo, s); #else - Log(LogLevel::kInfo, "LsObjects() only functions in debug builds."); + Log(LogName::kBa, LogLevel::kInfo, + "LsObjects() only functions in debug builds."); #endif // BA_DEBUG_BUILD } diff --git a/src/ballistica/shared/foundation/types.h b/src/ballistica/shared/foundation/types.h index b2cb6f24..c6a201a6 100644 --- a/src/ballistica/shared/foundation/types.h +++ b/src/ballistica/shared/foundation/types.h @@ -284,6 +284,19 @@ enum class PyExcType : uint8_t { kWidgetNotFound }; +enum class LogName : uint8_t { + kRoot, + kBa, + kBaAccount, + kBaLifecycle, + kBaAudio, + kBaGraphics, + kBaAssets, + kBaInput, + kBaNetworking, + kLast // Sentinel +}; + enum class LogLevel : uint8_t { kDebug, kInfo, diff --git a/src/ballistica/shared/generic/runnable.cc b/src/ballistica/shared/generic/runnable.cc index 04711629..809e7c9b 100644 --- a/src/ballistica/shared/generic/runnable.cc +++ b/src/ballistica/shared/generic/runnable.cc @@ -25,7 +25,7 @@ void Runnable::RunAndLogErrors() { } else { type_name = ""; } - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, std::string("Error in Runnable: " + type_name + ": ") + exc.what()); } } diff --git a/src/ballistica/shared/generic/timer_list.cc b/src/ballistica/shared/generic/timer_list.cc index 7bcf8b27..a255feeb 100644 --- a/src/ballistica/shared/generic/timer_list.cc +++ b/src/ballistica/shared/generic/timer_list.cc @@ -19,14 +19,17 @@ TimerList::~TimerList() { if (g_buildconfig.debug_build()) { if (timer_count_active_ != 0) { - Log(LogLevel::kError, "Invalid timerlist state on teardown."); + Log(LogName::kBa, LogLevel::kError, + "Invalid timerlist state on teardown."); } if (timer_count_inactive_ != 0) { - Log(LogLevel::kError, "Invalid timerlist state on teardown."); + Log(LogName::kBa, LogLevel::kError, + "Invalid timerlist state on teardown."); } if (!((timer_count_total_ == 0) || (client_timer_ != nullptr && timer_count_total_ == 1))) { - Log(LogLevel::kError, "Invalid timerlist state on teardown."); + Log(LogName::kBa, LogLevel::kError, + "Invalid timerlist state on teardown."); } } } diff --git a/src/ballistica/shared/generic/utils.cc b/src/ballistica/shared/generic/utils.cc index e0da681d..02306fe0 100644 --- a/src/ballistica/shared/generic/utils.cc +++ b/src/ballistica/shared/generic/utils.cc @@ -223,9 +223,9 @@ auto Utils::GetValidUTF8(const char* str, const char* loc) -> std::string { } } logged_count++; - Log(LogLevel::kError, "GOT INVALID UTF8 SEQUENCE: (" + log_str - + "); RETURNING '" + to + "'; LOC '" + loc - + "'"); + Log(LogName::kBa, LogLevel::kError, + "GOT INVALID UTF8 SEQUENCE: (" + log_str + "); RETURNING '" + to + + "'; LOC '" + loc + "'"); } } else { diff --git a/src/ballistica/shared/math/vector4f.h b/src/ballistica/shared/math/vector4f.h index 8a36dcd2..fc1eed2a 100644 --- a/src/ballistica/shared/math/vector4f.h +++ b/src/ballistica/shared/math/vector4f.h @@ -11,7 +11,7 @@ class Vector4f { public: Vector4f() = default; // NOLINTNEXTLINE saying we don't init v but in effect we do. - Vector4f(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} + Vector4f(float x, float y, float z, float a) : x(x), y(y), z(z), a(a) {} auto xyz() const -> Vector3f { return {x, y, z}; } @@ -20,13 +20,14 @@ class Vector4f { float x; float y; float z; - float w; + float a; }; float v[4]; }; }; -const Vector4f kVector4f0{0.0f, 0.0f, 0.0f, 0.0f}; // NOLINT(cert-err58-cpp) +const Vector4f kVector4f0{0.0f, 0.0f, 0.0f, 0.0f}; +const Vector4f kVector4f1{1.0f, 1.0f, 1.0f, 1.0f}; } // namespace ballistica diff --git a/src/ballistica/shared/python/python.cc b/src/ballistica/shared/python/python.cc index 47471e13..af941812 100644 --- a/src/ballistica/shared/python/python.cc +++ b/src/ballistica/shared/python/python.cc @@ -96,7 +96,7 @@ void Python::PrintStackTrace() { available = g_base_soft->PrintPythonStackTrace(); } if (!available) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "Python::PrintStackTrace() called before _babase set up; " "not printing."); } diff --git a/src/ballistica/shared/python/python_object_set.cc b/src/ballistica/shared/python/python_object_set.cc index 7f7ee884..c9afb6c4 100644 --- a/src/ballistica/shared/python/python_object_set.cc +++ b/src/ballistica/shared/python/python_object_set.cc @@ -39,7 +39,7 @@ void PythonObjectSetBase::StoreObj(int id, PyObject* pyobj) { // Also make sure we're not storing an object that's already been stored. for (auto&& i : objs_) { if (i.Get() != nullptr && i.Get() == pyobj) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "Python::StoreObj() called twice for same ptr; id=" + std::to_string(id) + "."); } diff --git a/src/ballistica/shared/python/python_ref.cc b/src/ballistica/shared/python/python_ref.cc index ab237c51..e1c337bf 100644 --- a/src/ballistica/shared/python/python_ref.cc +++ b/src/ballistica/shared/python/python_ref.cc @@ -29,7 +29,7 @@ static void ClearPythonExceptionAndWarnIfUnset() { // avoid that situation because it opens up the possibility of us clearing // exceptions that aren't related to our nullptr. if (!PyErr_Occurred()) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "A PythonRef acquire/steal call was passed nullptr but no Python " "exception is set. This situation should be avoided; only pass " "acquire/steal if it is directly due to a Python exception."); @@ -228,6 +228,12 @@ auto PythonRef::ValueAsInt() const -> int64_t { return Python::GetPyInt64(obj_); } +auto PythonRef::ValueAsDouble() const -> double { + assert(Python::HaveGIL()); + ThrowIfUnset(); + return Python::GetPyDouble(obj_); +} + auto PythonRef::GetAttr(const char* name) const -> PythonRef { assert(Python::HaveGIL()); ThrowIfUnset(); diff --git a/src/ballistica/shared/python/python_ref.h b/src/ballistica/shared/python/python_ref.h index 9a7ae3b4..9d195427 100644 --- a/src/ballistica/shared/python/python_ref.h +++ b/src/ballistica/shared/python/python_ref.h @@ -179,6 +179,7 @@ class PythonRef { -> std::optional>; auto ValueAsInt() const -> int64_t; + auto ValueAsDouble() const -> double; auto ValueAsOptionalInt() const -> std::optional; /// Returns whether the underlying PyObject is callable. diff --git a/src/ballistica/shared/python/python_sys.h b/src/ballistica/shared/python/python_sys.h index 30796fce..ba8b0d56 100644 --- a/src/ballistica/shared/python/python_sys.h +++ b/src/ballistica/shared/python/python_sys.h @@ -67,12 +67,13 @@ ((void)0) // For use in tp_dealloc; simply prints the error. -#define BA_PYTHON_DEALLOC_CATCH \ - } \ - catch (const std::exception& e) { \ - Log(LogLevel::kError, std::string("tp_dealloc exception: ") \ - + GetShortExceptionDescription(e)); \ - } \ +#define BA_PYTHON_DEALLOC_CATCH \ + } \ + catch (const std::exception& e) { \ + Log(LogName::kBa, LogLevel::kError, \ + std::string("tp_dealloc exception: ") \ + + GetShortExceptionDescription(e)); \ + } \ ((void)0) // Sets Python error and returns -1. diff --git a/src/ballistica/template_fs/python/class/python_class_hello.cc b/src/ballistica/template_fs/python/class/python_class_hello.cc index ec93b6a2..39d13682 100644 --- a/src/ballistica/template_fs/python/class/python_class_hello.cc +++ b/src/ballistica/template_fs/python/class/python_class_hello.cc @@ -50,11 +50,13 @@ void PythonClassHello::tp_dealloc(PythonClassHello* self) { } PythonClassHello::PythonClassHello() { - Log(LogLevel::kInfo, "Hello from PythonClassHello constructor!!!"); + Log(LogName::kBa, LogLevel::kInfo, + "Hello from PythonClassHello constructor!!!"); } PythonClassHello::~PythonClassHello() { - Log(LogLevel::kInfo, "Goodbye from PythonClassHello destructor!!!"); + Log(LogName::kBa, LogLevel::kInfo, + "Goodbye from PythonClassHello destructor!!!"); } auto PythonClassHello::TestMethod(PythonClassHello* self, PyObject* args, @@ -66,8 +68,9 @@ auto PythonClassHello::TestMethod(PythonClassHello* self, PyObject* args, const_cast(kwlist), &val)) { return nullptr; } - Log(LogLevel::kInfo, "Hello from PythonClassHello.test_method!!! (val=" - + std::to_string(val) + ")"); + Log(LogName::kBa, LogLevel::kInfo, + "Hello from PythonClassHello.test_method!!! (val=" + std::to_string(val) + + ")"); Py_RETURN_NONE; BA_PYTHON_CATCH; } diff --git a/src/ballistica/template_fs/python/methods/python_methods_template_fs.cc b/src/ballistica/template_fs/python/methods/python_methods_template_fs.cc index 4cd4f76a..b9d1b9d7 100644 --- a/src/ballistica/template_fs/python/methods/python_methods_template_fs.cc +++ b/src/ballistica/template_fs/python/methods/python_methods_template_fs.cc @@ -19,7 +19,7 @@ static auto PyHelloAgainWorld(PyObject* self, PyObject* args, PyObject* keywds) &name)) { return nullptr; } - Log(LogLevel::kInfo, "HELLO AGAIN WORLD!"); + Log(LogName::kBa, LogLevel::kInfo, "HELLO AGAIN WORLD!"); Py_RETURN_NONE; BA_PYTHON_CATCH; } diff --git a/src/ballistica/ui_v1/python/class/python_class_widget.cc b/src/ballistica/ui_v1/python/class/python_class_widget.cc index 2623a8a5..dadf5e74 100644 --- a/src/ballistica/ui_v1/python/class/python_class_widget.cc +++ b/src/ballistica/ui_v1/python/class/python_class_widget.cc @@ -127,7 +127,6 @@ auto PythonClassWidget::tp_setattro(PythonClassWidget* self, PyObject* attr, // Assuming this will always be a str? assert(PyUnicode_Check(attr)); - const char* s = PyUnicode_AsUTF8(attr); throw Exception("Attr '" + std::string(PyUnicode_AsUTF8(attr)) + "' is not settable on SessionPlayer objects.", @@ -302,7 +301,7 @@ auto PythonClassWidget::Delete(PythonClassWidget* self, PyObject* args, if (p) { p->DeleteWidget(w); } else { - Log(LogLevel::kError, "Can't delete widget: no parent."); + Log(LogName::kBa, LogLevel::kError, "Can't delete widget: no parent."); } } diff --git a/src/ballistica/ui_v1/python/ui_v1_python.cc b/src/ballistica/ui_v1/python/ui_v1_python.cc index 7a6592c0..f5976a75 100644 --- a/src/ballistica/ui_v1/python/ui_v1_python.cc +++ b/src/ballistica/ui_v1/python/ui_v1_python.cc @@ -78,7 +78,7 @@ void UIV1Python::ShowURL(const std::string& url) { PythonRef args(Py_BuildValue("(s)", url.c_str()), PythonRef::kSteal); objs().Get(ObjID::kShowURLWindowCall).Call(args); } else { - Log(LogLevel::kError, "ShowURLWindowCall nonexistent."); + Log(LogName::kBa, LogLevel::kError, "ShowURLWindowCall nonexistent."); } } @@ -119,7 +119,7 @@ void UIV1Python::InvokeStringEditor(PyObject* string_edit_adapter_instance) { context_call->ScheduleInUIOperation(args); } else { // Otherwise just run immediately. - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "UIV1Python::InvokeStringEditor running outside of UIInteraction; " "unexpected."); context_call->Run(args); diff --git a/src/ballistica/ui_v1/ui_v1.cc b/src/ballistica/ui_v1/ui_v1.cc index f51a2aba..ed3bff89 100644 --- a/src/ballistica/ui_v1/ui_v1.cc +++ b/src/ballistica/ui_v1/ui_v1.cc @@ -241,7 +241,9 @@ void UIV1FeatureSet::OnScreenChange() { // We allow OnScreenSizeChange() to handle size changes but *do* handle // UIScale changes here. - root_widget_->OnUIScaleChange(); + if (auto* root_widget = root_widget_.Get()) { + root_widget->OnUIScaleChange(); + } } void UIV1FeatureSet::OnLanguageChange() { diff --git a/src/ballistica/ui_v1/widget/button_widget.cc b/src/ballistica/ui_v1/widget/button_widget.cc index 00584833..f70d54ce 100644 --- a/src/ballistica/ui_v1/widget/button_widget.cc +++ b/src/ballistica/ui_v1/widget/button_widget.cc @@ -543,7 +543,7 @@ void ButtonWidget::Activate() { DoActivate(); } void ButtonWidget::DoActivate(bool is_repeat) { if (!enabled_) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "ButtonWidget::DoActivate() called on disabled button"); return; } diff --git a/src/ballistica/ui_v1/widget/container_widget.cc b/src/ballistica/ui_v1/widget/container_widget.cc index fa50aec4..41ed06e8 100644 --- a/src/ballistica/ui_v1/widget/container_widget.cc +++ b/src/ballistica/ui_v1/widget/container_widget.cc @@ -1402,7 +1402,7 @@ void ContainerWidget::SelectWidget(Widget* w, SelectionCause c) { } } else { if (root_selectable_) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "SelectWidget() called on a ContainerWidget which is itself " "selectable. Ignoring."); return; @@ -1429,7 +1429,7 @@ void ContainerWidget::SelectWidget(Widget* w, SelectionCause c) { } else { static bool printed = false; if (!printed) { - Log(LogLevel::kWarning, + Log(LogName::kBa, LogLevel::kWarning, "SelectWidget called on unselectable widget: " + w->GetWidgetTypeName()); Python::PrintStackTrace(); @@ -1600,7 +1600,8 @@ void ContainerWidget::SelectDownWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui_v1 || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { - BA_LOG_ONCE(LogLevel::kError, "SelectDownWidget called before UI init."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "SelectDownWidget called before UI init."); return; } @@ -1633,7 +1634,7 @@ void ContainerWidget::SelectDownWidget() { } if (w) { if (!w->IsSelectable()) { - Log(LogLevel::kError, "Down_widget is not selectable."); + Log(LogName::kBa, LogLevel::kError, "Down_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. @@ -1664,7 +1665,8 @@ void ContainerWidget::SelectUpWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui_v1 || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { - BA_LOG_ONCE(LogLevel::kError, "SelectUpWidget called before UI init."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "SelectUpWidget called before UI init."); return; } @@ -1697,7 +1699,7 @@ void ContainerWidget::SelectUpWidget() { } if (w) { if (!w->IsSelectable()) { - Log(LogLevel::kError, "up_widget is not selectable."); + Log(LogName::kBa, LogLevel::kError, "up_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. @@ -1728,7 +1730,8 @@ void ContainerWidget::SelectLeftWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui_v1 || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { - BA_LOG_ONCE(LogLevel::kError, "SelectLeftWidget called before UI init."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "SelectLeftWidget called before UI init."); return; } @@ -1749,7 +1752,7 @@ void ContainerWidget::SelectLeftWidget() { } if (w) { if (!w->IsSelectable()) { - Log(LogLevel::kError, "left_widget is not selectable."); + Log(LogName::kBa, LogLevel::kError, "left_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. @@ -1780,7 +1783,8 @@ void ContainerWidget::SelectRightWidget() { if (!g_base->ui || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { - BA_LOG_ONCE(LogLevel::kError, "SelectRightWidget called before UI init."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "SelectRightWidget called before UI init."); return; } @@ -1801,7 +1805,7 @@ void ContainerWidget::SelectRightWidget() { } if (w) { if (!w->IsSelectable()) { - Log(LogLevel::kError, "right_widget is not selectable."); + Log(LogName::kBa, LogLevel::kError, "right_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. @@ -1833,7 +1837,8 @@ void ContainerWidget::SelectNextWidget() { if (!g_base->ui || !g_ui_v1->root_widget() || !g_ui_v1->screen_root_widget()) { - BA_LOG_ONCE(LogLevel::kError, "SelectNextWidget called before UI init."); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "SelectNextWidget called before UI init."); return; } diff --git a/src/ballistica/ui_v1/widget/root_widget.cc b/src/ballistica/ui_v1/widget/root_widget.cc index fc32be3b..8e960a33 100644 --- a/src/ballistica/ui_v1/widget/root_widget.cc +++ b/src/ballistica/ui_v1/widget/root_widget.cc @@ -629,8 +629,8 @@ void RootWidget::Setup() { auto centerx = -1.5f; auto centery = 8.0f; - auto offsx = 5.5f; - auto offsy = 5.5f; + // auto offsx = 5.5f; + // auto offsy = 5.5f; { TextDef td; td.button = achievements_button_; diff --git a/src/ballistica/ui_v1/widget/text_widget.cc b/src/ballistica/ui_v1/widget/text_widget.cc index 435d7e8c..55a4ffd3 100644 --- a/src/ballistica/ui_v1/widget/text_widget.cc +++ b/src/ballistica/ui_v1/widget/text_widget.cc @@ -549,11 +549,11 @@ void TextWidget::SetText(const std::string& text_in_raw) { g_base->assets->CompileResourceString( text_in_raw, "TextWidget::set_text format check", &valid); if (!valid) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Invalid resource string: '" + text_in_raw + "'"); Python::PrintStackTrace(); } else if (explicit_bool(print_false_positives)) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Got false positive for json check on '" + text_in_raw + "'"); Python::PrintStackTrace(); } @@ -643,7 +643,7 @@ void TextWidget::InvokeStringEditor_() { .Get(UIV1Python::ObjID::kTextWidgetStringEditAdapterClass) .Call(args); if (!result.Exists()) { - Log(LogLevel::kError, "Error invoking string edit dialog."); + Log(LogName::kBa, LogLevel::kError, "Error invoking string edit dialog."); return; } diff --git a/src/ballistica/ui_v1/widget/widget.cc b/src/ballistica/ui_v1/widget/widget.cc index f704be9d..c073aa52 100644 --- a/src/ballistica/ui_v1/widget/widget.cc +++ b/src/ballistica/ui_v1/widget/widget.cc @@ -38,7 +38,7 @@ void Widget::SetToolbarVisibility(ToolbarVisibility v) { auto Widget::IsInMainStack() const -> bool { if (!g_base->ui) { - BA_LOG_ONCE(LogLevel::kError, + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, "Widget::IsInMainStack() called before ui creation."); return false; } @@ -193,7 +193,7 @@ void Widget::ScreenPointToWidget(float* x, float* y) const { float y_test = *y; WidgetPointToScreen(&x_test, &y_test); if (std::abs(x_test - x_old) > 0.01f || std::abs(y_test - y_old) > 0.01f) { - Log(LogLevel::kError, + Log(LogName::kBa, LogLevel::kError, "ScreenPointToWidget sanity check error: expected (" + std::to_string(x_old) + "," + std::to_string(y_old) + ") got (" + std::to_string(x_test) + "," + std::to_string(y_test) + ")"); diff --git a/src/meta/babasemeta/pyembed/binding_base_app.py b/src/meta/babasemeta/pyembed/binding_base_app.py index da1cd286..a29fafdf 100644 --- a/src/meta/babasemeta/pyembed/binding_base_app.py +++ b/src/meta/babasemeta/pyembed/binding_base_app.py @@ -19,6 +19,5 @@ values = [ app.on_native_shutdown, # kAppOnNativeShutdownCall app.on_native_shutdown_complete, # kAppOnNativeShutdownCompleteCall app.on_native_active_changed, # kAppOnNativeActiveChangedCall - app.read_config, # kAppReadConfigCall app.devconsole.do_refresh_tab, # kAppDevConsoleDoRefreshTabCall ] diff --git a/src/meta/bacoremeta/pyembed/binding_core.py b/src/meta/bacoremeta/pyembed/binding_core.py index e2acf9b9..45c9b08d 100644 --- a/src/meta/bacoremeta/pyembed/binding_core.py +++ b/src/meta/bacoremeta/pyembed/binding_core.py @@ -17,9 +17,27 @@ values = [ copy.copy, # kShallowCopyCall json.dumps, # kJsonDumpsCall json.loads, # kJsonLoadsCall - logging.debug, # kLoggingDebugCall - logging.info, # kLoggingInfoCall - logging.warning, # kLoggingWarningCall - logging.error, # kLoggingErrorCall - logging.critical, # kLoggingCriticalCall + logging.DEBUG, # kLoggingLevelDebug + logging.INFO, # kLoggingLevelInfo + logging.WARNING, # kLoggingLevelWarning + logging.ERROR, # kLoggingLevelError + logging.CRITICAL, # kLoggingLevelCritical + logging.getLogger('root'), # kLoggerRoot + logging.getLogger('root').log, # kLoggerRootLogCall + logging.getLogger('ba'), # kLoggerBa + logging.getLogger('ba').log, # kLoggerBaLogCall + logging.getLogger('ba.account'), # kLoggerBaAccount + logging.getLogger('ba.account').log, # kLoggerBaAccountLogCall + logging.getLogger('ba.audio'), # kLoggerBaAudio + logging.getLogger('ba.audio').log, # kLoggerBaAudioLogCall + logging.getLogger('ba.graphics'), # kLoggerBaGraphics + logging.getLogger('ba.graphics').log, # kLoggerBaGraphicsLogCall + logging.getLogger('ba.lifecycle'), # kLoggerBaLifecycle + logging.getLogger('ba.lifecycle').log, # kLoggerBaLifecycleLogCall + logging.getLogger('ba.assets'), # kLoggerBaAssets + logging.getLogger('ba.assets').log, # kLoggerBaAssetsLogCall + logging.getLogger('ba.input'), # kLoggerBaInput + logging.getLogger('ba.input').log, # kLoggerBaInputLogCall + logging.getLogger('ba.networking'), # kLoggerBaNetworking + logging.getLogger('ba.networking').log, # kLoggerBaNetworkingLogCall ] diff --git a/tools/bacommon/loggercontrol.py b/tools/bacommon/loggercontrol.py new file mode 100644 index 00000000..e34fd143 --- /dev/null +++ b/tools/bacommon/loggercontrol.py @@ -0,0 +1,118 @@ +# Released under the MIT License. See LICENSE for details. +# +"""System for managing loggers.""" + +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING, Annotated +from dataclasses import dataclass, field + +from efro.dataclassio import ioprepped, IOAttrs + +if TYPE_CHECKING: + from typing import Self + + +@ioprepped +@dataclass +class LoggerControlConfig: + """A logging level configuration that applies to all loggers. + + Any loggers not explicitly contained in the configuration will be + set to NOTSET. + """ + + # Logger names mapped to log-level values (from system logging module). + levels: Annotated[dict[str, int], IOAttrs('l', store_default=False)] = ( + field(default_factory=dict) + ) + + def apply(self) -> None: + """Apply the config to all Python loggers.""" + existinglognames = ( + set(['root']) | logging.root.manager.loggerDict.keys() + ) + + # First, update levels for all existing loggers. + for logname in existinglognames: + logger = logging.getLogger(logname) + level = self.levels.get(logname) + if level is None: + level = logging.NOTSET + logger.setLevel(level) + + # Next, assign levels to any loggers that don't exist. + for logname, level in self.levels.items(): + if logname not in existinglognames: + logging.getLogger(logname).setLevel(level) + + def would_make_changes(self) -> bool: + """Return whether calling apply would change anything.""" + + existinglognames = ( + set(['root']) | logging.root.manager.loggerDict.keys() + ) + + # Return True if we contain any nonexistent loggers. Even if + # we wouldn't change their level, the fact that we'd create + # them still counts as a difference. + if any( + logname not in existinglognames for logname in self.levels.keys() + ): + return True + + # Now go through all existing loggers and return True if we + # would change their level. + for logname in existinglognames: + logger = logging.getLogger(logname) + level = self.levels.get(logname) + if level is None: + level = logging.NOTSET + if logger.level != level: + return True + + return False + + def diff(self, baseconfig: LoggerControlConfig) -> LoggerControlConfig: + """Return a config containing only changes compared to a base config. + + Note that this omits all NOTSET values that resolve to NOTSET in + the base config. + + This diffed config can later be used with apply_diff() against the + base config to recreate the state represented by self. + """ + cls = type(self) + config = cls() + for loggername, level in self.levels.items(): + baselevel = baseconfig.levels.get(loggername, logging.NOTSET) + if level != baselevel: + config.levels[loggername] = level + return config + + def apply_diff( + self, diffconfig: LoggerControlConfig + ) -> LoggerControlConfig: + """Apply a diff config to ourself.""" + cls = type(self) + + # Create a new config (with an indepenent levels dict copy). + config = cls(levels=dict(self.levels)) + + # Overlay the diff levels dict onto our new one. + config.levels.update(diffconfig.levels) + + # Note: we do NOT prune NOTSET values here. This is so all + # loggers mentioned in the base config get created if we are + # applied, even if they are assigned a default level. + return config + + @classmethod + def from_current_loggers(cls) -> Self: + """Build a config from the current set of loggers.""" + lognames = ['root'] + sorted(logging.root.manager.loggerDict) + config = cls() + for logname in lognames: + config.levels[logname] = logging.getLogger(logname).level + return config diff --git a/tools/bacommon/logging.py b/tools/bacommon/logging.py new file mode 100644 index 00000000..4ee7fd02 --- /dev/null +++ b/tools/bacommon/logging.py @@ -0,0 +1,19 @@ +# Released under the MIT License. See LICENSE for details. +# +"""Logging functionality.""" + +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING +from bacommon.loggercontrol import LoggerControlConfig + +if TYPE_CHECKING: + pass + + +def get_base_logger_control_config_client() -> LoggerControlConfig: + """Return the logger-control-config used by the ballistica client.""" + + # Just info for everything by default. + return LoggerControlConfig(levels={'root': logging.INFO}) diff --git a/tools/batools/project/_checks.py b/tools/batools/project/_checks.py index ecf3abaa..5a85fddb 100755 --- a/tools/batools/project/_checks.py +++ b/tools/batools/project/_checks.py @@ -601,7 +601,11 @@ def check_misc(self: ProjectUpdater) -> None: ) as infile: msconfig = infile.read() if ( - '// V2 Master Server:\n' '\n' '// PROD\n' '#if 1\n' + '// V2 Master Server ------------------------' + '------------------------------------\n' + '\n' + '// PROD\n' + '#if 1\n' ) not in msconfig: if ( os.environ.get('BA_ALLOW_NON_PROD_V2_MASTER_SERVER', '0') diff --git a/tools/efro/call.py b/tools/efro/call.py index cd594b25..22eda0de 100644 --- a/tools/efro/call.py +++ b/tools/efro/call.py @@ -4,7 +4,8 @@ from __future__ import annotations -# import functools +import threading +import weakref from typing import TYPE_CHECKING, TypeVar, Generic T = TypeVar('T') @@ -13,23 +14,81 @@ if TYPE_CHECKING: pass -class SimpleCallbackSet(Generic[T]): - """A simple way to manage a set of callbacks.""" +class CallbackSet(Generic[T]): + """A simple way to manage a set of callbacks. + + Any number of calls can be added to a callback set. Each add results + in an entry that can be used to remove the call from the set later. + Callbacks are also implicitly removed when an entry is deallocated, + so make sure to hold on to the return value when adding. + + CallbackSet instances should be used from a single thread only + (this will be checked in debug mode). + """ def __init__(self) -> None: - self._entries: list[SimpleCallbackSetEntry[T]] = [] + self._entries: list[weakref.ref[CallbackRegistration[T]]] = [] + self.thread: threading.Thread + if __debug__: + self.thread = threading.current_thread() - def add(self, call: T) -> None: + def add(self, call: T) -> CallbackRegistration[T]: """Add a callback.""" - self._entries.append(SimpleCallbackSetEntry(call)) + assert threading.current_thread() == self.thread + + self._prune() + + entry = CallbackRegistration(call, self) + self._entries.append(weakref.ref(entry)) + return entry def getcalls(self) -> list[T]: - """Return the current set of registered calls.""" - return [e.call for e in self._entries] + """Return the current set of registered calls. + + Note that this returns a flattened list of calls; generally this + should protect against calls which themselves add or remove + callbacks. + """ + assert threading.current_thread() == self.thread + + self._prune() + + # Ignore calls that have been deallocated or explicitly cleared. + entries = [e() for e in self._entries] + return [e.call for e in entries if e is not None and e.call is not None] + + def _prune(self) -> None: + + # Quick-out if all our entries are intact. + needs_prune = False + for entry in self._entries: + entrytarget = entry() + if entrytarget is None or entrytarget.call is None: + needs_prune = True + break + if not needs_prune: + return + + newentries: list[weakref.ref[CallbackRegistration[T]]] = [] + for entry in self._entries: + entrytarget = entry() + if entrytarget is not None and entrytarget.call is not None: + newentries.append(entry) + self._entries = newentries -class SimpleCallbackSetEntry(Generic[T]): +class CallbackRegistration(Generic[T]): """An entry for a callback set.""" - def __init__(self, call: T) -> None: - self.call = call + def __init__(self, call: T, callbackset: CallbackSet[T]) -> None: + self.call: T | None = call + self.callbackset: CallbackSet[T] | None = callbackset + + def clear(self) -> None: + """Explicitly remove a callback from a CallbackSet.""" + assert ( + self.callbackset is None + or threading.current_thread() == self.callbackset.thread + ) + # Simply clear the call to mark us as dead. + self.call = None diff --git a/tools/efro/dataclassio/_prep.py b/tools/efro/dataclassio/_prep.py index 68bcaa37..fd80bb4b 100644 --- a/tools/efro/dataclassio/_prep.py +++ b/tools/efro/dataclassio/_prep.py @@ -185,7 +185,7 @@ class PrepSession: # which allows us to pick up nested classes, etc. resolved_annotations = get_type_hints( cls, - localns=vars(cls), # type: ignore[arg-type] + localns=vars(cls), globalns=self.globalns, include_extras=True, ) diff --git a/tools/efro/log.py b/tools/efro/logging.py similarity index 81% rename from tools/efro/log.py rename to tools/efro/logging.py index f681fab9..93df2100 100644 --- a/tools/efro/log.py +++ b/tools/efro/logging.py @@ -22,7 +22,7 @@ from efro.dataclassio import ioprepped, IOAttrs, dataclass_to_json if TYPE_CHECKING: from pathlib import Path - from typing import Any, Callable, TextIO + from typing import Any, Callable, TextIO, Literal class LogLevel(Enum): @@ -129,21 +129,23 @@ class LogHandler(logging.Handler): *, path: str | Path | None, echofile: TextIO | None, - suppress_non_root_debug: bool, cache_size_limit: int, cache_time_limit: datetime.timedelta | None, + echofile_timestamp_format: Literal['default', 'relative'] = 'default', + launch_time: float | None = None, ): super().__init__() # pylint: disable=consider-using-with self._file = None if path is None else open(path, 'w', encoding='utf-8') self._echofile = echofile + self._echofile_timestamp_format = echofile_timestamp_format self._callbacks: list[Callable[[LogEntry], None]] = [] - self._suppress_non_root_debug = suppress_non_root_debug self._file_chunks: dict[str, list[str]] = {'stdout': [], 'stderr': []} self._file_chunk_ship_task: dict[str, asyncio.Task | None] = { 'stdout': None, 'stderr': None, } + self._launch_time = time.time() if launch_time is None else launch_time self._cache_size = 0 assert cache_size_limit >= 0 self._cache_size_limit = cache_size_limit @@ -159,8 +161,8 @@ class LogHandler(logging.Handler): self._thread.start() # Spin until our thread is up and running; otherwise we could - # wind up trying to push stuff to our event loop before the - # loop exists. + # wind up trying to push stuff to our event loop before the loop + # exists. while not self._thread_bootstrapped: time.sleep(0.001) @@ -235,8 +237,8 @@ class LogHandler(logging.Handler): await asyncio.sleep(61.27) now = utc_now() with self._cache_lock: - # Prune the oldest entry as long as there is a first one that - # is too old. + # Prune the oldest entry as long as there is a first one + # that is too old. while ( self._cache and (now - self._cache[0][1].time) >= self._cache_time_limit @@ -252,9 +254,9 @@ class LogHandler(logging.Handler): This will only include entries that have been processed by the background thread, so may not include just-submitted logs or - entries for partially written stdout/stderr lines. - Entries from the range [start_index:start_index+max_entries] - which are still present in the cache will be returned. + entries for partially written stdout/stderr lines. Entries from + the range [start_index:start_index+max_entries] which are still + present in the cache will be returned. """ assert start_index >= 0 @@ -283,11 +285,11 @@ class LogHandler(logging.Handler): def _cache_slice( self, start: int, end: int, step: int = 1 ) -> list[LogEntry]: - # Deque doesn't natively support slicing but we can do it manually. - # It sounds like rotating the deque and pulling from the beginning - # is the most efficient way to do this. The downside is the deque - # gets temporarily modified in the process so we need to make sure - # we're holding the lock. + # Deque doesn't natively support slicing but we can do it + # manually. It sounds like rotating the deque and pulling from + # the beginning is the most efficient way to do this. The + # downside is the deque gets temporarily modified in the process + # so we need to make sure we're holding the lock. assert self._cache_lock.locked() cache = self._cache cache.rotate(-start) @@ -310,25 +312,18 @@ class LogHandler(logging.Handler): @override def emit(self, record: logging.LogRecord) -> None: # pylint: disable=too-many-branches + if __debug__: starttime = time.monotonic() # Called by logging to send us records. - # TODO - kill this. - if ( - self._suppress_non_root_debug - and record.name != 'root' - and record.levelname == 'DEBUG' - ): - return - # Optimization: if our log args are all simple immutable values, - # we can just kick the whole thing over to our background thread to - # be formatted there at our leisure. If anything is mutable and - # thus could possibly change between now and then or if we want - # to do immediate file echoing then we need to bite the bullet - # and do that stuff here at the call site. + # we can just kick the whole thing over to our background thread + # to be formatted there at our leisure. If anything is mutable + # and thus could possibly change between now and then or if we + # want to do immediate file echoing then we need to bite the + # bullet and do that stuff here at the call site. fast_path = self._echofile is None and self._is_immutable_log_data( record.args ) @@ -360,37 +355,27 @@ class LogHandler(logging.Handler): if __debug__: formattime = time.monotonic() - # Also immediately print pretty colored output to our echo file - # (generally stderr). We do this part here instead of in our bg - # thread because the delay can throw off command line prompts or - # make tight debugging harder. + # Also immediately print pretty colored output to our echo + # file (generally stderr). We do this part here instead of + # in our bg thread because the delay can throw off command + # line prompts or make tight debugging harder. if self._echofile is not None: - # try: - # if self._report_blocking_io_on_echo_error: - # premsg = ( - # 'WARNING: BlockingIOError ON LOG ECHO OUTPUT;' - # ' YOU ARE PROBABLY MISSING LOGS\n' - # ) - # self._report_blocking_io_on_echo_error = False - # else: - # premsg = '' - ends = LEVELNO_COLOR_CODES.get(record.levelno) - namepre = f'{Clr.WHT}{record.name}:{Clr.RST} ' - if ends is not None: - self._echofile.write( - f'{namepre}{ends[0]}' - f'{msg}{ends[1]}\n' - # f'{namepre}{ends[0]}' f'{premsg}{msg}{ends[1]}\n' - ) + if self._echofile_timestamp_format == 'relative': + timestamp = f'{record.created - self._launch_time:.3f}' else: - self._echofile.write(f'{namepre}{msg}\n') + timestamp = ( + datetime.datetime.fromtimestamp( + record.created, tz=datetime.UTC + ).strftime('%H:%M:%S') + + f'.{int(record.msecs):03d}' + ) + preinfo = f'{Clr.WHT}{timestamp} {record.name}:{Clr.RST} ' + ends = LEVELNO_COLOR_CODES.get(record.levelno) + if ends is not None: + self._echofile.write(f'{preinfo}{ends[0]}{msg}{ends[1]}\n') + else: + self._echofile.write(f'{preinfo}{msg}\n') self._echofile.flush() - # except BlockingIOError: - # # Ran into this when doing a bunch of logging; assuming - # # this is asyncio's doing?.. For now trying to survive - # # the error but telling the user something is probably - # # missing in their output. - # self._report_blocking_io_on_echo_error = True if __debug__: echotime = time.monotonic() @@ -408,29 +393,28 @@ class LogHandler(logging.Handler): if __debug__: # pylint: disable=used-before-assignment - # Make noise if we're taking a significant amount of time here. - # Limit the noise to once every so often though; otherwise we - # could get a feedback loop where every log emit results in a - # warning log which results in another, etc. + # + # Make noise if we're taking a significant amount of time + # here. Limit the noise to once every so often though; + # otherwise we could get a feedback loop where every log + # emit results in a warning log which results in another, + # etc. now = time.monotonic() - # noinspection PyUnboundLocalVariable - duration = now - starttime # pyright: ignore - # noinspection PyUnboundLocalVariable - format_duration = formattime - starttime # pyright: ignore - # noinspection PyUnboundLocalVariable - echo_duration = echotime - formattime # pyright: ignore + duration = now - starttime + format_duration = formattime - starttime + echo_duration = echotime - formattime if duration > 0.05 and ( self._last_slow_emit_warning_time is None or now > self._last_slow_emit_warning_time + 10.0 ): - # Logging calls from *within* a logging handler - # sounds sketchy, so let's just kick this over to - # the bg event loop thread we've already got. + # Logging calls from *within* a logging handler sounds + # sketchy, so let's just kick this over to the bg event + # loop thread we've already got. self._last_slow_emit_warning_time = now self._event_loop.call_soon_threadsafe( partial( logging.warning, - 'efro.log.LogHandler emit took too long' + 'efro.logging.LogHandler emit took too long' ' (%.2fs total; %.2fs format, %.2fs echo,' ' fast_path=%s).', duration, @@ -491,17 +475,17 @@ class LogHandler(logging.Handler): self._file_chunks[name].append(output) - # Individual parts of a print come across as separate writes, - # and the end of a print will be a standalone '\n' by default. - # Let's use that as a hint that we're likely at the end of - # a full print statement and ship what we've got. + # Individual parts of a print come across as separate + # writes, and the end of a print will be a standalone '\n' + # by default. Let's use that as a hint that we're likely at + # the end of a full print statement and ship what we've got. if output == '\n': self._ship_file_chunks(name, cancel_ship_task=True) else: - # By default just keep adding chunks. - # However we keep a timer running anytime we've got - # unshipped chunks so that we can ship what we've got - # after a short bit if we never get a newline. + # By default just keep adding chunks. However we keep a + # timer running anytime we've got unshipped chunks so + # that we can ship what we've got after a short bit if + # we never get a newline. ship_task = self._file_chunk_ship_task[name] if ship_task is None: self._file_chunk_ship_task[name] = ( @@ -612,6 +596,7 @@ class LogHandler(logging.Handler): self._run_callback_on_entry(call, entry) # Dump to our structured log file. + # # TODO: should set a timer for flushing; don't flush every line. if self._file is not None: entry_s = dataclass_to_json(entry) @@ -644,23 +629,9 @@ class FileLogEcho: self._name = name self._handler = handler - # Think this was a result of setting non-blocking stdin somehow; - # probably not needed. - # self._report_blocking_io_error = False - def write(self, output: Any) -> None: """Override standard write call.""" - # try: - # if self._report_blocking_io_error: - # self._report_blocking_io_error = False - # self._original.write( - # 'WARNING: BlockingIOError ENCOUNTERED;' - # ' OUTPUT IS PROBABLY MISSING' - # ) - self._original.write(output) - # except BlockingIOError: - # self._report_blocking_io_error = True self._handler.file_write(self._name, output) def flush(self) -> None: @@ -681,11 +652,11 @@ def setup_logging( log_path: str | Path | None, level: LogLevel, *, - suppress_non_root_debug: bool = False, log_stdout_stderr: bool = False, echo_to_stderr: bool = True, cache_size_limit: int = 0, cache_time_limit: datetime.timedelta | None = None, + launch_time: float | None = None, ) -> LogHandler: """Set up our logging environment. @@ -701,34 +672,35 @@ def setup_logging( LogLevel.CRITICAL: logging.CRITICAL, } - # Wire logger output to go to a structured log file. - # Also echo it to stderr IF we're running in a terminal. - # UPDATE: Actually gonna always go to stderr. Is there a - # reason we shouldn't? This makes debugging possible if all - # we have is access to a non-interactive terminal or file dump. - # We could add a '--quiet' arg or whatnot to change this behavior. + # Wire logger output to go to a structured log file. Also echo it to + # stderr IF we're running in a terminal. + # + # UPDATE: Actually gonna always go to stderr. Is there a reason we + # shouldn't? This makes debugging possible if all we have is access + # to a non-interactive terminal or file dump. We could add a + # '--quiet' arg or whatnot to change this behavior. # Note: by passing in the *original* stderr here before we - # (potentially) replace it, we ensure that our log echos - # won't themselves be intercepted and sent to the logger - # which would create an infinite loop. + # (potentially) replace it, we ensure that our log echos won't + # themselves be intercepted and sent to the logger which would + # create an infinite loop. loghandler = LogHandler( path=log_path, echofile=sys.stderr if echo_to_stderr else None, - suppress_non_root_debug=suppress_non_root_debug, + echofile_timestamp_format='relative', cache_size_limit=cache_size_limit, cache_time_limit=cache_time_limit, + launch_time=launch_time, ) # Note: going ahead with force=True here so that we replace any - # existing logger. Though we warn if it looks like we are doing - # that so we can try to avoid creating the first one. + # existing logger. Though we warn if it looks like we are doing that + # so we can try to avoid creating the first one. had_previous_handlers = bool(logging.root.handlers) logging.basicConfig( level=lmap[level], - # format='%(name)s: %(message)s', - # We dump *only* the message here. We pass various log record bits - # around and format things fancier where they end up. + # We dump *only* the message here. We pass various log record + # bits around so we can write rich logs or format things later. format='%(message)s', handlers=[loghandler], force=True,