From 402dfc66c58e2359b268bf33fee8bca04f97098f Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 1 Nov 2024 18:37:17 -0700 Subject: [PATCH] lots of work on logging, langs, and cloud subscriptions --- .efrocachemap | 137 ++++++++-------- CHANGELOG.md | 9 +- config/requirements.txt | 4 +- src/assets/.asset_manifest_private.json | 9 +- src/assets/.asset_manifest_public.json | 2 + src/assets/Makefile | 11 +- src/assets/ba_data/python/babase/__init__.py | 2 + src/assets/ba_data/python/babase/_cloud.py | 26 +++ .../ba_data/python/babase/_devconsole.py | 17 +- .../ba_data/python/babase/_devconsoletabs.py | 101 ++++++++---- src/assets/ba_data/python/babase/_hooks.py | 37 +++++ src/assets/ba_data/python/babase/_language.py | 2 + .../ba_data/python/baclassic/_appmode.py | 22 ++- src/assets/ba_data/python/baenv.py | 2 +- src/assets/ba_data/python/baplus/_cloud.py | 30 +++- .../bascenev1lib/game/capturetheflag.py | 1 + src/ballistica/base/assets/assets_server.cc | 13 +- src/ballistica/base/assets/sound_asset.h | 2 +- src/ballistica/base/audio/al_sys.h | 4 - src/ballistica/base/logic/logic.cc | 14 +- .../base/networking/network_reader.cc | 6 +- src/ballistica/base/python/base_python.h | 1 + src/ballistica/base/ui/dev_console.cc | 149 +++++++++++++++++- src/ballistica/base/ui/dev_console.h | 1 + .../classic/support/classic_app_mode.cc | 6 +- .../scene_v1/support/host_session.cc | 11 +- src/ballistica/shared/ballistica.cc | 2 +- src/meta/babasemeta/pyembed/binding_base.py | 1 + tools/bacommon/cloud.py | 50 +++++- tools/efro/message/_protocol.py | 28 ++-- tools/efro/message/_sender.py | 14 ++ 31 files changed, 556 insertions(+), 158 deletions(-) create mode 100644 src/assets/ba_data/python/babase/_cloud.py diff --git a/.efrocachemap b/.efrocachemap index f7724b84..fe534723 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -421,43 +421,44 @@ "build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26", "build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55", - "build/assets/ba_data/data/langdata.json": "84353324570add5ea6698d8a7ddd99be", + "build/assets/ba_data/data/langdata.json": "1f4ee8b8b810e7b644291f775cb06bf0", "build/assets/ba_data/data/languages/arabic.json": "609f5d698a488e40e61787b62ee8ea5e", "build/assets/ba_data/data/languages/belarussian.json": "3d5523d0004293aa2df02f3f6f3b84f8", "build/assets/ba_data/data/languages/chinese.json": "57d199cfd2a5cf493e777dd96cc12f7c", "build/assets/ba_data/data/languages/chinesetraditional.json": "d85c58cc1e0e4bd0b09b2bc768cb1971", "build/assets/ba_data/data/languages/croatian.json": "b23619cb396ac16640c47458f884b16a", - "build/assets/ba_data/data/languages/czech.json": "be5d536a1160607dfa158e59ecc1c324", + "build/assets/ba_data/data/languages/czech.json": "7f123919752ed1e9de5bd06b46fa811e", "build/assets/ba_data/data/languages/danish.json": "8e57db30c5250df2abff14a822f83ea7", - "build/assets/ba_data/data/languages/dutch.json": "b0900d572c9141897d53d6574c471343", - "build/assets/ba_data/data/languages/english.json": "5a73dea22df1117d58a79459def62ff5", + "build/assets/ba_data/data/languages/dutch.json": "734357560f53b4820221f6d60a0b79e8", + "build/assets/ba_data/data/languages/english.json": "a13015c5fea152cf84f5988e5cab9298", "build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880", "build/assets/ba_data/data/languages/filipino.json": "30f9622136067fe866bebf7e81ee8546", - "build/assets/ba_data/data/languages/french.json": "69578cc38349367912659a62ace5a42d", + "build/assets/ba_data/data/languages/french.json": "e0bf81cff0497dd84044442606cc878e", "build/assets/ba_data/data/languages/german.json": "c4b8c4d3c078b7902155af3221cf9cf4", - "build/assets/ba_data/data/languages/gibberish.json": "d6810f99fc9055b5203c382a83bc5128", + "build/assets/ba_data/data/languages/gibberish.json": "48bd2b7f70bdb76ac217c27c06f7a294", "build/assets/ba_data/data/languages/greek.json": "d28d1092fbb00ed857cbd53124c0dc78", - "build/assets/ba_data/data/languages/hindi.json": "54cd56bade6922b40989a8ac5e0c17f6", + "build/assets/ba_data/data/languages/hindi.json": "5f60453c7dd3853c95c6f822e92d5300", "build/assets/ba_data/data/languages/hungarian.json": "9d88004a98f0fbe2ea72edd5e0b3002e", "build/assets/ba_data/data/languages/indonesian.json": "2ccb3fe081ead7706dbebb1008a8bc4e", "build/assets/ba_data/data/languages/italian.json": "d9eb41f6eafb19040f8d5c0608790b62", "build/assets/ba_data/data/languages/korean.json": "4e3524327a0174250aff5e1ef4c0c597", "build/assets/ba_data/data/languages/malay.json": "f6ce0426d03a62612e3e436ed5d1be1f", "build/assets/ba_data/data/languages/persian.json": "fc25780341e0dfebc393e4c1c846b7a9", + "build/assets/ba_data/data/languages/piratespeak.json": "da60ddd83706b0e9e61c7ef9b6e7c975", "build/assets/ba_data/data/languages/polish.json": "89333fb207f9eb2f22fff5a95b022c35", - "build/assets/ba_data/data/languages/portuguese.json": "eb2563e245e1ea00b870264dced3ebd7", - "build/assets/ba_data/data/languages/romanian.json": "55a8744e466801013ea131266a856924", + "build/assets/ba_data/data/languages/portuguese.json": "ac198f63b4af6e1f25044e92b953b96e", + "build/assets/ba_data/data/languages/romanian.json": "5ae206fe0b71c4015b02b86da8931c8f", "build/assets/ba_data/data/languages/russian.json": "0fcc60bf1e8e19a74f02b0798728ec68", "build/assets/ba_data/data/languages/serbian.json": "623fa4129a1154c2f32ed7867e56ff6a", "build/assets/ba_data/data/languages/slovak.json": "3c08c748c96c71bd9e1d7291fb8817b6", - "build/assets/ba_data/data/languages/spanish.json": "0a37387183a6634fc8e9ac225fcf20b1", + "build/assets/ba_data/data/languages/spanish.json": "e7f59cd89c0ebe2b7cd4c116f4d0e639", "build/assets/ba_data/data/languages/swedish.json": "3b179e7333183c70adb0811246b09959", "build/assets/ba_data/data/languages/tamil.json": "ead39b864228696a9b0d19344bc4b5ec", "build/assets/ba_data/data/languages/thai.json": "1d665629361f302693dead39de8fa945", "build/assets/ba_data/data/languages/turkish.json": "6153ca5248b8e4743e9501ac72378493", "build/assets/ba_data/data/languages/ukrainian.json": "3a5b8132690fcd583d280879876c85b7", "build/assets/ba_data/data/languages/venetian.json": "a1315f5233ebbee1464683ac55d5d9d5", - "build/assets/ba_data/data/languages/vietnamese.json": "5ae84265600b6cfda45c9bed18724e1d", + "build/assets/ba_data/data/languages/vietnamese.json": "34a8b75acba2c0234e0b00fb4ef7d011", "build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054", "build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422", "build/assets/ba_data/data/maps/courtyard.json": "4b836554c8949bcd2ae382f5e3c1a9cc", @@ -945,17 +946,17 @@ "build/assets/ba_data/meshes/zoeTorso.bob": "26a1007e98902178d8c62fd7069d5da5", "build/assets/ba_data/meshes/zoeUpperArm.bob": "a8a881010ac1ee9ec5ca872d5c5e853a", "build/assets/ba_data/meshes/zoeUpperLeg.bob": "95b2502f74c70f934927f67cd505c3ad", - "build/assets/ba_data/python-site-packages/certifi/__init__.py": "8133ab2f7828219b186d4cecb2d5443b", + "build/assets/ba_data/python-site-packages/certifi/__init__.py": "611f53e8096b704efa4e0caa4c5dcd7c", "build/assets/ba_data/python-site-packages/certifi/__main__.py": "ef02e73f8581609df189a9f61aca365b", - "build/assets/ba_data/python-site-packages/certifi/cacert.pem": "072b2f0da8254144432a91d068fa6fb1", + "build/assets/ba_data/python-site-packages/certifi/cacert.pem": "131628a15d2001d10cac7c73d001c3c2", "build/assets/ba_data/python-site-packages/certifi/core.py": "c1b8c102093ea26587619677c7ec6016", "build/assets/ba_data/python-site-packages/typing_extensions.py": "1f6b4db70adb7ca05dff2b74f6cde8aa", "build/assets/ba_data/python-site-packages/urllib3/__init__.py": "06cffcf88c8024218069f628c9938c50", "build/assets/ba_data/python-site-packages/urllib3/_base_connection.py": "4404e529dbd3db128d9a2e60c6ed9243", "build/assets/ba_data/python-site-packages/urllib3/_collections.py": "14963d7d43c0e51e777851c0e2868928", - "build/assets/ba_data/python-site-packages/urllib3/_request_methods.py": "87d6a7a77db1324e81d02f919c134f1a", - "build/assets/ba_data/python-site-packages/urllib3/_version.py": "46e3fbaaf7d02c2e55fcecc98deb5af8", - "build/assets/ba_data/python-site-packages/urllib3/connection.py": "3b49efceb61dfab0587286686e0e5ada", + "build/assets/ba_data/python-site-packages/urllib3/_request_methods.py": "ee22bc685aec965a9cbc8fd51097758b", + "build/assets/ba_data/python-site-packages/urllib3/_version.py": "eb50943fd218eb58da9cc08cdc3d97d9", + "build/assets/ba_data/python-site-packages/urllib3/connection.py": "4afbb583ae2aff33f1da0d0a27a5eb21", "build/assets/ba_data/python-site-packages/urllib3/connectionpool.py": "e891fe6cdff0c03a3b670e225c0c4d88", "build/assets/ba_data/python-site-packages/urllib3/contrib/__init__.py": "340c83beff7dcff8f5c7b87cd43cedaf", "build/assets/ba_data/python-site-packages/urllib3/contrib/emscripten/__init__.py": "33531b60d1ff8b9073567240f2a1745f", @@ -963,23 +964,25 @@ "build/assets/ba_data/python-site-packages/urllib3/contrib/emscripten/fetch.py": "be8618e1c9a004108bf9f82f706503d7", "build/assets/ba_data/python-site-packages/urllib3/contrib/emscripten/request.py": "da01c66ef924f2a2875d46adb87d7507", "build/assets/ba_data/python-site-packages/urllib3/contrib/emscripten/response.py": "11208e765d5e9a1d481ded597e58c7d3", - "build/assets/ba_data/python-site-packages/urllib3/contrib/pyopenssl.py": "7a14ef1c58c55511581043ed25aebba3", + "build/assets/ba_data/python-site-packages/urllib3/contrib/pyopenssl.py": "fc0e3d0b92a7866fdd3dd242eee78713", "build/assets/ba_data/python-site-packages/urllib3/contrib/socks.py": "bd5fed33c678f750ad9d1c754a26faf0", "build/assets/ba_data/python-site-packages/urllib3/exceptions.py": "c346a13d762dcdff2f9cccc1d06c92d6", "build/assets/ba_data/python-site-packages/urllib3/fields.py": "e24ef9e64962ca25322c0216cc4bc923", "build/assets/ba_data/python-site-packages/urllib3/filepost.py": "d0a96fd7614fcaa368b26d8a769ff848", - "build/assets/ba_data/python-site-packages/urllib3/http2.py": "c307bd4c486d10070770c320145eb33c", + "build/assets/ba_data/python-site-packages/urllib3/http2/__init__.py": "e2b631c71eba41cadcbd71097df87d0b", + "build/assets/ba_data/python-site-packages/urllib3/http2/connection.py": "98ec5901ae0a7ab8d54cb298f3797ea7", + "build/assets/ba_data/python-site-packages/urllib3/http2/probe.py": "24a017e7e59ca2060a4b7af53c15723a", "build/assets/ba_data/python-site-packages/urllib3/poolmanager.py": "6dd72a45419efb9a2ab4e58c527d84d5", "build/assets/ba_data/python-site-packages/urllib3/response.py": "f9f74417b5c2b4e5812b473435e3f455", "build/assets/ba_data/python-site-packages/urllib3/util/__init__.py": "3ef2c56aa3ccfc25f466dc767f191b2c", - "build/assets/ba_data/python-site-packages/urllib3/util/connection.py": "888aa8c2f0a4a76749e6c3fd0df597e2", + "build/assets/ba_data/python-site-packages/urllib3/util/connection.py": "373bae67383b8abf80d88c0d4e26d2fe", "build/assets/ba_data/python-site-packages/urllib3/util/proxy.py": "2de685261596886eb8e2655e1af9ce7b", - "build/assets/ba_data/python-site-packages/urllib3/util/request.py": "a1fb665c16fa443eef15db73c5e29917", + "build/assets/ba_data/python-site-packages/urllib3/util/request.py": "72389227066178a97711ead0d25a0868", "build/assets/ba_data/python-site-packages/urllib3/util/response.py": "e6a942417aa97215e358ae97b53bafe6", "build/assets/ba_data/python-site-packages/urllib3/util/retry.py": "4eeffe54c7d400c4af731f02ef3fb074", - "build/assets/ba_data/python-site-packages/urllib3/util/ssl_.py": "c1b929bdfba831479f4b2d1538954536", + "build/assets/ba_data/python-site-packages/urllib3/util/ssl_.py": "6105f661daa3e837e9ff8fdf06b2037a", "build/assets/ba_data/python-site-packages/urllib3/util/ssl_match_hostname.py": "cbb28520c901282629d47375af2dd717", - "build/assets/ba_data/python-site-packages/urllib3/util/ssltransport.py": "0a51cd95ce75673b6c1117cb22da18b1", + "build/assets/ba_data/python-site-packages/urllib3/util/ssltransport.py": "77fa0b9225a7d6033f1413687f575a96", "build/assets/ba_data/python-site-packages/urllib3/util/timeout.py": "e3bc037067fd6aaed823955be6f1792d", "build/assets/ba_data/python-site-packages/urllib3/util/url.py": "6a37bafe6598aecba9e034be2085f583", "build/assets/ba_data/python-site-packages/urllib3/util/util.py": "95e6d2fa1b0f310a991612421f1a29a1", @@ -4096,53 +4099,53 @@ "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": "16c5eddc745dc4a601cb2ee364b71e31", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "51a65b326094cf210be9d7d085e57c2b", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "96e368ec6da34e00222328f3e2821320", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "b7a33145dfe3f18adf42133a0843be2d", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "e7642737ca3a0ff3f034a073e657ef42", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "376c2361f94219726a082b745f500db7", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "339b5cc93d9db484800f488cf1b2f4b9", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "cf9527c43d75f7a51f483e17a59eab82", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "758b91a1374bfbd8489b2cc68ec18bd2", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "33d824f0b6f30446167636ad68e9a4ae", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "7203b89f81d3315822156e0041429aef", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "0d4048eccd45dbf7a6269722def6accd", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "06481d98907f976a3f9ee0273ceaff38", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "db76906a502c58fedb79ebe9237cd8ec", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "91ce9cf92332763823df13fec765da78", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "a8e9aadab42b687abd5c3c299f3373e4", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "a354c943a42e737044029f746df3e74b", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "7936c43cf14fa0912c9261d03d478a91", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "0d82db33672ec85d7e720e9233b22e84", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "d041406d19983269b8ba5e576186521d", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "47cd40a453b5e7c8031c36afe51cfc74", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "61b8d712130f831f049179bf25c96b86", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "47cd40a453b5e7c8031c36afe51cfc74", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "61b8d712130f831f049179bf25c96b86", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "a26331b58f4511138f90106ce28a595e", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "3292f6eaed1c1715754b15cf9dac3108", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "a26331b58f4511138f90106ce28a595e", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "3292f6eaed1c1715754b15cf9dac3108", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "b89d6a533de222a7cd988d575ffb0b65", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d4a9e505da7243d9418d99805ccd0550", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "b89d6a533de222a7cd988d575ffb0b65", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d4a9e505da7243d9418d99805ccd0550", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "c551a02dc318a7f665a727f88cf2b1c1", - "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "8911877eeeabd36033f4552c84d18f96", - "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "7cc7fd84cf143a1e4e1429ef7cb8bf18", - "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "8911877eeeabd36033f4552c84d18f96", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "c48af47c5fbbb085a65d00598050d30d", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "03fd14ef8f7baf3d25612a9d31792f4b", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "5aea1501b99692a6675f7566944f4523", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "a3198455822b3576a17e0e57bf79e9ee", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "d18789c1c44f7bc9d9c808385df18555", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "8de2be3f0402065db2a75708d1ba08af", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "591e71334f4da572cfdae7c603e25f55", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "87461e54837ca42f9e45dc0926746133", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "ab2a763986c41b05e108609e2245bc88", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "22354f5977eecb7898e8fed6a15a3471", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "5f42e62b736e12db3418740a66eaaaa6", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "3985a5b97d65f3069e78e2b864270973", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "31c224d17503059828870a65e8bc6983", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "ff368944b4df967e9ae9adca0ba2ecb6", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "2eb53cf0c1469f2de823fc37ed66e947", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "b1e608f6a95595309fcb3db40763fdc3", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "be64c41aecd2f18efc6b34c98e3c340f", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "701213f2480fcc86aabdf954d6499f42", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "c909d72602b5296d1d10d292547cf4f2", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "6ee608e0c722de648614a6d9453fa8ef", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "4564cff8a6abc34c046726c4a2f83fa1", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "0bc4cd868fd813d6a9f442f2dd38242a", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "b4ef832c06699a5b505d005057c15a18", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "d1be84a36ac1de882eeaf704e5dd4490", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "9daa986905e50270f19135279d040d90", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "a51416a1fc183a310012472a717fbb96", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "a23c0971f8b9549c7a709d72447fd47c", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "e2891d1c10f73a875e1c5efcf3006121", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "8c667cd77a82124b48c50630b2dd4aed", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "7e6824b45b74266c09823109b5a7069f", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "8c667cd77a82124b48c50630b2dd4aed", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "7e6824b45b74266c09823109b5a7069f", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "ee9800f3b72862f087ced2ef382d39f1", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "1e121312835e14cfecc7ee3d26f57439", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "ee9800f3b72862f087ced2ef382d39f1", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "1e121312835e14cfecc7ee3d26f57439", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "7b8ba069d9ca2edc257f6868fc9e9df9", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "a237f9e945d1a416be9aa913b5d60705", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "7b8ba069d9ca2edc257f6868fc9e9df9", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "a237f9e945d1a416be9aa913b5d60705", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "920f39fc5155d9c41b96ff01a88a5020", + "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c39bcd7db4fd85f1b59855aeaf966070", + "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "a0abd47869e753575f06a4336659ca79", + "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c39bcd7db4fd85f1b59855aeaf966070", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "e77911c9d4e0dd92bfff36a506abb54a", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "558708ed8550ff325e3c40445e92fbf0", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "01222e9a53456ba1765779e2d43df195", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "0e9441acd45a6e923963477c6efcc3bd", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "37e5cf12b015c65126ebe16ff55ac70f", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "570acb3ca5c9e199e61ddaef41737aec", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "1c6f9950c4426c881ddc80d9be42a3c3", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "3fb42beee8520d8bab03535989dafa5c", "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.inc": "3a583e7e03bd4907b21adc3bf5729d15", "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": "02bf828e423d0fced4d87aa74f25cbdc", diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f9a8f1d..50b83247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -### 1.7.37 (build 22062, api 9, 2024-10-22) +### 1.7.37 (build 22068, api 9, 2024-11-01) +- I am pleased to announce that after years of hard work from many members of + the community, PirateSpeak is now complete and available as a language choice. + This changes everything. - 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. @@ -134,6 +137,10 @@ bg-dynamics message overflows. - Added a close button to the dev-console as an alternate to using key presses to close it. +- (build 22063) Added a 'Copy History' button in the Python tab in the + dev-console. Note that this will copy all cached log history; not just what is + displayed in the dev-console. This should be handy for diagnosing problems in + the future. ### 1.7.36 (build 21944, api 8, 2024-07-26) - Wired up Tokens, BombSquad's new purchasable currency. The first thing these diff --git a/config/requirements.txt b/config/requirements.txt index 38dcc654..1822d122 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -2,14 +2,14 @@ cpplint==2.0.0 dmgbuild==1.6.2 filelock==3.16.1 furo==2024.8.6 -mypy==1.12.1 +mypy==1.13.0 pbxproj==4.2.1 pdoc==15.0.0 pur==7.3.2 pylint==3.3.1 pylsp-mypy==0.6.9 pytest==8.3.3 -python-daemon==3.0.1 +python-daemon==3.1.0 python-lsp-black==2.0.0 python-lsp-server==1.12.0 requests==2.32.3 diff --git a/src/assets/.asset_manifest_private.json b/src/assets/.asset_manifest_private.json index cb9f5cf8..7495f8c7 100644 --- a/src/assets/.asset_manifest_private.json +++ b/src/assets/.asset_manifest_private.json @@ -443,6 +443,7 @@ "ba_data/data/languages/korean.json", "ba_data/data/languages/malay.json", "ba_data/data/languages/persian.json", + "ba_data/data/languages/piratespeak.json", "ba_data/data/languages/polish.json", "ba_data/data/languages/portuguese.json", "ba_data/data/languages/romanian.json", @@ -964,7 +965,6 @@ "ba_data/python-site-packages/urllib3/__pycache__/exceptions.cpython-312.opt-1.pyc", "ba_data/python-site-packages/urllib3/__pycache__/fields.cpython-312.opt-1.pyc", "ba_data/python-site-packages/urllib3/__pycache__/filepost.cpython-312.opt-1.pyc", - "ba_data/python-site-packages/urllib3/__pycache__/http2.cpython-312.opt-1.pyc", "ba_data/python-site-packages/urllib3/__pycache__/poolmanager.cpython-312.opt-1.pyc", "ba_data/python-site-packages/urllib3/__pycache__/response.cpython-312.opt-1.pyc", "ba_data/python-site-packages/urllib3/_base_connection.py", @@ -992,7 +992,12 @@ "ba_data/python-site-packages/urllib3/exceptions.py", "ba_data/python-site-packages/urllib3/fields.py", "ba_data/python-site-packages/urllib3/filepost.py", - "ba_data/python-site-packages/urllib3/http2.py", + "ba_data/python-site-packages/urllib3/http2/__init__.py", + "ba_data/python-site-packages/urllib3/http2/__pycache__/__init__.cpython-312.opt-1.pyc", + "ba_data/python-site-packages/urllib3/http2/__pycache__/connection.cpython-312.opt-1.pyc", + "ba_data/python-site-packages/urllib3/http2/__pycache__/probe.cpython-312.opt-1.pyc", + "ba_data/python-site-packages/urllib3/http2/connection.py", + "ba_data/python-site-packages/urllib3/http2/probe.py", "ba_data/python-site-packages/urllib3/poolmanager.py", "ba_data/python-site-packages/urllib3/response.py", "ba_data/python-site-packages/urllib3/util/__init__.py", diff --git a/src/assets/.asset_manifest_public.json b/src/assets/.asset_manifest_public.json index 1545d690..062e1a90 100644 --- a/src/assets/.asset_manifest_public.json +++ b/src/assets/.asset_manifest_public.json @@ -13,6 +13,7 @@ "ba_data/python/babase/__pycache__/_apputils.cpython-312.opt-1.pyc", "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__/_cloud.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", @@ -42,6 +43,7 @@ "ba_data/python/babase/_apputils.py", "ba_data/python/babase/_assetmanager.py", "ba_data/python/babase/_asyncio.py", + "ba_data/python/babase/_cloud.py", "ba_data/python/babase/_devconsole.py", "ba_data/python/babase/_devconsoletabs.py", "ba_data/python/babase/_emptyappmode.py", diff --git a/src/assets/Makefile b/src/assets/Makefile index e693a1ca..33f3709e 100644 --- a/src/assets/Makefile +++ b/src/assets/Makefile @@ -174,6 +174,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \ $(BUILD_DIR)/ba_data/python/babase/_apputils.py \ $(BUILD_DIR)/ba_data/python/babase/_assetmanager.py \ $(BUILD_DIR)/ba_data/python/babase/_asyncio.py \ + $(BUILD_DIR)/ba_data/python/babase/_cloud.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 \ @@ -452,6 +453,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_apputils.cpython-312.opt-1.pyc \ $(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__/_cloud.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 \ @@ -2728,7 +2730,9 @@ SCRIPT_TARGETS_PY_PRIVATE_COMMON = \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/exceptions.py \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/fields.py \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/filepost.py \ - $(BUILD_DIR)/ba_data/python-site-packages/urllib3/http2.py \ + $(BUILD_DIR)/ba_data/python-site-packages/urllib3/http2/__init__.py \ + $(BUILD_DIR)/ba_data/python-site-packages/urllib3/http2/connection.py \ + $(BUILD_DIR)/ba_data/python-site-packages/urllib3/http2/probe.py \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/poolmanager.py \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/response.py \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/util/__init__.py \ @@ -2769,7 +2773,9 @@ SCRIPT_TARGETS_PYC_PRIVATE_COMMON = \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/__pycache__/exceptions.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/__pycache__/fields.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/__pycache__/filepost.cpython-312.opt-1.pyc \ - $(BUILD_DIR)/ba_data/python-site-packages/urllib3/__pycache__/http2.cpython-312.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python-site-packages/urllib3/http2/__pycache__/__init__.cpython-312.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python-site-packages/urllib3/http2/__pycache__/connection.cpython-312.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python-site-packages/urllib3/http2/__pycache__/probe.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/__pycache__/poolmanager.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/__pycache__/response.cpython-312.opt-1.pyc \ $(BUILD_DIR)/ba_data/python-site-packages/urllib3/util/__pycache__/__init__.cpython-312.opt-1.pyc \ @@ -5160,6 +5166,7 @@ DATA_TARGETS = \ $(BUILD_DIR)/ba_data/data/languages/korean.json \ $(BUILD_DIR)/ba_data/data/languages/malay.json \ $(BUILD_DIR)/ba_data/data/languages/persian.json \ + $(BUILD_DIR)/ba_data/data/languages/piratespeak.json \ $(BUILD_DIR)/ba_data/data/languages/polish.json \ $(BUILD_DIR)/ba_data/data/languages/portuguese.json \ $(BUILD_DIR)/ba_data/data/languages/romanian.json \ diff --git a/src/assets/ba_data/python/babase/__init__.py b/src/assets/ba_data/python/babase/__init__.py index a32fb729..aae9a1b2 100644 --- a/src/assets/ba_data/python/babase/__init__.py +++ b/src/assets/ba_data/python/babase/__init__.py @@ -131,6 +131,7 @@ from babase._apputils import ( get_remote_app_name, AppHealthMonitor, ) +from babase._cloud import CloudSubscription from babase._devconsole import ( DevConsoleTab, DevConsoleTabEntry, @@ -230,6 +231,7 @@ __all__ = [ 'clipboard_get_text', 'clipboard_has_text', 'clipboard_is_supported', + 'CloudSubscription', 'clipboard_set_text', 'commit_app_config', 'ContextCall', diff --git a/src/assets/ba_data/python/babase/_cloud.py b/src/assets/ba_data/python/babase/_cloud.py new file mode 100644 index 00000000..bb966627 --- /dev/null +++ b/src/assets/ba_data/python/babase/_cloud.py @@ -0,0 +1,26 @@ +# Released under the MIT License. See LICENSE for details. +# +"""Cloud related functionality.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import _babase + +if TYPE_CHECKING: + pass + + +class CloudSubscription: + """User handle to a subscription to some cloud data. + + Do not instantiate these directly; use the subscribe methods + in *.app.plus.cloud to create them. + """ + + def __init__(self, subscription_id: int) -> None: + self._subscription_id = subscription_id + + def __del__(self) -> None: + if _babase.app.plus is not None: + _babase.app.plus.cloud.unsubscribe(self._subscription_id) diff --git a/src/assets/ba_data/python/babase/_devconsole.py b/src/assets/ba_data/python/babase/_devconsole.py index 92dadf2a..66632ba6 100644 --- a/src/assets/ba_data/python/babase/_devconsole.py +++ b/src/assets/ba_data/python/babase/_devconsole.py @@ -34,7 +34,22 @@ class DevConsoleTab: h_anchor: Literal['left', 'center', 'right'] = 'center', label_scale: float = 1.0, corner_radius: float = 8.0, - style: Literal['normal', 'light'] = 'normal', + style: Literal[ + 'normal', + 'bright', + 'red', + 'red_bright', + 'purple', + 'purple_bright', + 'yellow', + 'yellow_bright', + 'blue', + 'blue_bright', + 'white', + 'white_bright', + 'black', + 'black_bright', + ] = 'normal', disabled: bool = False, ) -> None: """Add a button to the tab being refreshed.""" diff --git a/src/assets/ba_data/python/babase/_devconsoletabs.py b/src/assets/ba_data/python/babase/_devconsoletabs.py index bffab22c..279a5317 100644 --- a/src/assets/ba_data/python/babase/_devconsoletabs.py +++ b/src/assets/ba_data/python/babase/_devconsoletabs.py @@ -57,7 +57,9 @@ class DevConsoleTabAppModes(DevConsoleTab): label_scale=0.6, call=partial(self._set_app_mode, mode), style=( - 'light' if isinstance(_babase.app._mode, mode) else 'normal' + 'bright' + if isinstance(_babase.app._mode, mode) + else 'normal' ), ) @@ -110,7 +112,7 @@ class DevConsoleTabUI(DevConsoleTab): # h_anchor='left', label_scale=0.6, call=self.toggle_ui_overlay, - style='light' if ui_overlay else 'normal', + style='bright' if ui_overlay else 'normal', ) x = 300 self.text( @@ -132,7 +134,7 @@ class DevConsoleTabUI(DevConsoleTab): label_scale=0.6, call=partial(_babase.app.set_ui_scale, scale), style=( - 'light' + 'bright' if scale.name.lower() == _babase.get_ui_scale() else 'normal' ), @@ -232,14 +234,20 @@ class Table(Generic[T]): ) # 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 + # + # UPDATE: Nevermind; top feels better. Keeping this code around + # in case we ever want to make it an option though. + if bool(False): + yoffs = -1.0 * ( + tab.height + - ( + rows_on_this_page * self._entry_height + + margin_top + + margin_bottom + ) ) - ) + else: + yoffs = 0 # Keep our corners up to date for user use. self.top_left = (center_to_left + xoffs, tab.height + yoffs) @@ -253,12 +261,14 @@ class Table(Generic[T]): # Page left/right buttons. tab.button( '<', - pos=(center_to_left + xoffs, margin_bottom), + pos=( + center_to_left + xoffs, + yoffs + tab.height - margin_top - rows * self._entry_height, + ), size=( self._margin_left_right, - rows_on_this_page * self._entry_height, + rows * self._entry_height, ), - # h_anchor='left', call=partial(self._page_left, tab), disabled=entry_offset == 0, ) @@ -269,13 +279,12 @@ class Table(Generic[T]): + xoffs + self._margin_left_right + columns_on_this_page * self._entry_width, - margin_bottom, + yoffs + tab.height - margin_top - rows * self._entry_height, ), size=( self._margin_left_right, - rows_on_this_page * self._entry_height, + rows * self._entry_height, ), - # h_anchor='left', call=partial(self._page_right, tab), disabled=( entry_offset + entries_on_this_page >= len(self._entries) @@ -353,6 +362,7 @@ class DevConsoleTabLogging(DevConsoleTab): self._table = Table( title='Logging Levels', entry_width=800, + entry_height=42, debug_bounds=False, entries=list[str](), draw_entry_call=self._draw_entry, @@ -469,21 +479,22 @@ class DevConsoleTabLogging(DevConsoleTab): 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' + effectivelevel = logger.getEffectiveLevel() + # 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), + size=(bwidth * 1.0 - 2.0, height - 10), label_scale=btextscale, - style='light' if level == logging.NOTSET else 'normal', + style='white_bright' if level == logging.NOTSET else 'black', call=partial( self._set_entry_val, entry_index, entry, logging.NOTSET ), @@ -494,7 +505,12 @@ class DevConsoleTabLogging(DevConsoleTab): 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', + style=( + 'blue_bright' + if level == logging.DEBUG + else 'blue' if effectivelevel <= logging.DEBUG else 'black' + ), + # style='bright' if level == logging.DEBUG else 'normal', call=partial( self._set_entry_val, entry_index, entry, logging.DEBUG ), @@ -505,7 +521,12 @@ class DevConsoleTabLogging(DevConsoleTab): 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', + style=( + 'white_bright' + if level == logging.INFO + else 'white' if effectivelevel <= logging.INFO else 'black' + ), + # style='bright' if level == logging.INFO else 'normal', call=partial(self._set_entry_val, entry_index, entry, logging.INFO), ) index += 1 @@ -514,7 +535,11 @@ class DevConsoleTabLogging(DevConsoleTab): 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', + style=( + 'yellow_bright' + if level == logging.WARNING + else 'yellow' if effectivelevel <= logging.WARNING else 'black' + ), call=partial( self._set_entry_val, entry_index, entry, logging.WARNING ), @@ -525,7 +550,11 @@ class DevConsoleTabLogging(DevConsoleTab): 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', + style=( + 'red_bright' + if level == logging.ERROR + else 'red' if effectivelevel <= logging.ERROR else 'black' + ), call=partial( self._set_entry_val, entry_index, entry, logging.ERROR ), @@ -536,7 +565,13 @@ class DevConsoleTabLogging(DevConsoleTab): 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', + style=( + 'purple_bright' + if level == logging.CRITICAL + else ( + 'purple' if effectivelevel <= logging.CRITICAL else 'black' + ) + ), call=partial( self._set_entry_val, entry_index, entry, logging.CRITICAL ), @@ -563,7 +598,7 @@ class DevConsoleTabTest(DevConsoleTab): size=(100, 30), h_anchor='left', label_scale=0.6, - style='light', + style='bright', ) self.text( 'TestText', diff --git a/src/assets/ba_data/python/babase/_hooks.py b/src/assets/ba_data/python/babase/_hooks.py index c344d269..d2f51c78 100644 --- a/src/assets/ba_data/python/babase/_hooks.py +++ b/src/assets/ba_data/python/babase/_hooks.py @@ -430,3 +430,40 @@ def unsupported_controller_message(name: str) -> None: Lstr(resource='unsupportedControllerText', subs=[('${NAME}', name)]), color=(1, 0, 0), ) + + +def copy_dev_console_history() -> None: + """Copy log history from the dev console.""" + import baenv + from babase._language import Lstr + + if not _babase.clipboard_is_supported(): + _babase.getsimplesound('error').play() + _babase.screenmessage( + 'Clipboard not supported on this build.', + color=(1, 0, 0), + ) + return + + # This requires us to be running with a log-handler set up. + envconfig = baenv.get_config() + if envconfig.log_handler is None: + _babase.getsimplesound('error').play() + _babase.screenmessage( + 'Not available; standard engine logging is not enabled.', + color=(1, 0, 0), + ) + return + + # Just dump everything in the log-handler's cache. + archive = envconfig.log_handler.get_cached() + lines: list[str] = [] + stdnames = ('stdout', 'stderr') + for entry in archive.entries: + reltime = entry.time.timestamp() - envconfig.launch_time + level_ex = '' if entry.name in stdnames else f' {entry.level.name}' + lines.append(f'{reltime:.3f} {entry.name}{level_ex}: {entry.message}') + + _babase.clipboard_set_text('\n'.join(lines)) + _babase.screenmessage(Lstr(resource='copyConfirmText'), color=(0, 1, 0)) + _babase.getsimplesound('gunCocking').play() diff --git a/src/assets/ba_data/python/babase/_language.py b/src/assets/ba_data/python/babase/_language.py index 4e118b58..5f393557 100644 --- a/src/assets/ba_data/python/babase/_language.py +++ b/src/assets/ba_data/python/babase/_language.py @@ -83,6 +83,8 @@ class LanguageSubsystem(AppSubsystem): for i, name in enumerate(names): if name == 'Chinesetraditional': names[i] = 'ChineseTraditional' + elif name == 'Piratespeak': + names[i] = 'PirateSpeak' except Exception: from babase import _error diff --git a/src/assets/ba_data/python/baclassic/_appmode.py b/src/assets/ba_data/python/baclassic/_appmode.py index 42cdb70f..fe192d32 100644 --- a/src/assets/ba_data/python/baclassic/_appmode.py +++ b/src/assets/ba_data/python/baclassic/_appmode.py @@ -21,10 +21,10 @@ from babase import ( import _baclassic if TYPE_CHECKING: - from typing import Callable + from typing import Callable, Any from efro.call import CallbackRegistration - from babase import AppIntent, AccountV2Handle + from babase import AppIntent, AccountV2Handle, CloudSubscription from bauiv1 import UIV1AppSubsystem, MainWindow, MainWindowState @@ -35,6 +35,7 @@ class ClassicAppMode(AppMode): self._on_primary_account_changed_callback: ( CallbackRegistration | None ) = None + self._test_sub: CloudSubscription | None = None @override @classmethod @@ -154,8 +155,21 @@ class ClassicAppMode(AppMode): ) -> 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) + + if bool(False): + assert app.plus is not None + if account is None: + self._test_sub = None + else: + with account: + self._test_sub = app.plus.cloud.subscribe( + self._on_sub_update + ) + else: + self._test_sub = None + + def _on_sub_update(self, val: Any) -> None: + print(f'GOT SUB UPDATE: {val}') 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 99921968..f12be131 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -53,7 +53,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 22062 +TARGET_BALLISTICA_BUILD = 22068 TARGET_BALLISTICA_VERSION = '1.7.37' diff --git a/src/assets/ba_data/python/baplus/_cloud.py b/src/assets/ba_data/python/baplus/_cloud.py index 9d24d3f5..285f9b26 100644 --- a/src/assets/ba_data/python/baplus/_cloud.py +++ b/src/assets/ba_data/python/baplus/_cloud.py @@ -174,10 +174,32 @@ class CloudSubsystem(babase.AppSubsystem): 'Cloud functionality is not present in this build.' ) - # def subscribe( - # self, - # on_response: Callable[[Any], None], - # ) -> CallbackRegistration: + def subscribe( + self, updatecall: Callable[[Any], None] + ) -> babase.CloudSubscription: + """Subscribe to some data.""" + from bacommon.cloud import TestCloudSubscriptionRequest + + return self._subscribe(TestCloudSubscriptionRequest(), updatecall) + + def _subscribe( + self, + request: bacommon.cloud.CloudSubscriptionRequest, + updatecall: Callable[[Any], None], + ) -> babase.CloudSubscription: + """Subscribe to some cloud data.""" + raise NotImplementedError( + 'Cloud functionality is not present in this build.' + ) + + def unsubscribe(self, subscription_id: int) -> None: + """Unsubscribe from some subscription. + + Do not call this manually; it is called by CloudSubscription. + """ + raise NotImplementedError( + 'Cloud functionality is not present in this build.' + ) def cloud_console_exec(code: str) -> None: diff --git a/src/assets/ba_data/python/bascenev1lib/game/capturetheflag.py b/src/assets/ba_data/python/bascenev1lib/game/capturetheflag.py index e86af03e..de2ea8a7 100644 --- a/src/assets/ba_data/python/bascenev1lib/game/capturetheflag.py +++ b/src/assets/ba_data/python/bascenev1lib/game/capturetheflag.py @@ -32,6 +32,7 @@ class CTFFlag(Flag): activity: CaptureTheFlagGame def __init__(self, team: Team): + assert team.flagmaterial is not None super().__init__( materials=[team.flagmaterial], diff --git a/src/ballistica/base/assets/assets_server.cc b/src/ballistica/base/assets/assets_server.cc index 88c03a9b..d541335c 100644 --- a/src/ballistica/base/assets/assets_server.cc +++ b/src/ballistica/base/assets/assets_server.cc @@ -184,7 +184,7 @@ void AssetsServer::WriteReplayMessages() { fclose(replay_out_file_); replay_out_file_ = nullptr; g_core->Log(LogName::kBaAudio, LogLevel::kError, - "error writing replay file: " + "Error writing replay file: " + g_core->platform->GetErrnoString()); return; } @@ -197,7 +197,7 @@ void AssetsServer::WriteReplayMessages() { fclose(replay_out_file_); replay_out_file_ = nullptr; g_core->Log(LogName::kBaAudio, LogLevel::kError, - "error writing replay file: " + "Error writing replay file: " + g_core->platform->GetErrnoString()); return; } @@ -206,7 +206,7 @@ void AssetsServer::WriteReplayMessages() { fclose(replay_out_file_); replay_out_file_ = nullptr; g_core->Log(LogName::kBaAudio, LogLevel::kError, - "error writing replay file: " + "Error writing replay file: " + g_core->platform->GetErrnoString()); return; } @@ -220,7 +220,7 @@ void AssetsServer::WriteReplayMessages() { replay_out_file_ = nullptr; g_core->Log( LogName::kBaAudio, LogLevel::kError, - "error writing replay file: " + g_core->platform->GetErrnoString()); + "Error writing replay file: " + g_core->platform->GetErrnoString()); return; } replay_bytes_written_ += data_compressed.size() + 2; @@ -240,11 +240,6 @@ void AssetsServer::Process() { if (!g_base->graphics->has_client_context()) { return; } - // if (!g_base->assets || - // || !g_base->graphics->texture_compression_types_are_set() // NOLINT - // || !g_base->graphics_server->texture_quality_set()) { - // return; - // } // Process exactly 1 preload item. Empty out our non-audio list first // (audio is less likely to cause noticeable hitches if it needs to be loaded diff --git a/src/ballistica/base/assets/sound_asset.h b/src/ballistica/base/assets/sound_asset.h index 43b9333a..df272d43 100644 --- a/src/ballistica/base/assets/sound_asset.h +++ b/src/ballistica/base/assets/sound_asset.h @@ -7,7 +7,7 @@ #include #include "ballistica/base/assets/asset.h" -#include "ballistica/base/audio/al_sys.h" +#include "ballistica/base/audio/al_sys.h" // IWYU pragma: keep. namespace ballistica::base { diff --git a/src/ballistica/base/audio/al_sys.h b/src/ballistica/base/audio/al_sys.h index 80427913..a8ccd28a 100644 --- a/src/ballistica/base/audio/al_sys.h +++ b/src/ballistica/base/audio/al_sys.h @@ -3,10 +3,6 @@ #ifndef BALLISTICA_BASE_AUDIO_AL_SYS_H_ #define BALLISTICA_BASE_AUDIO_AL_SYS_H_ -#include - -#include "ballistica/shared/ballistica.h" - #if BA_ENABLE_AUDIO #if BA_HAVE_FRAMEWORK_OPENAL diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index 861e90e4..9181363e 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -394,7 +394,7 @@ void Logic::UpdateDisplayTimeForHeadlessMode_() { // don't care about keeping the increments smooth or consistent. // The one thing we *do* try to do, however, is keep our timer length - // updated so that we fire exactly when the next app-mode event is + // updated so that we'll fire exactly when the next app-mode event is // scheduled (or at least close enough so we can fudge it and tell them // its that exact time). @@ -554,6 +554,18 @@ void Logic::UpdateDisplayTimeForFrameDraw_() { display_time_increment_ = display_time_increment_ + offs; } + // If our final increment is bigger than some sane threshold, clamp it + // and try to report the conditions that caused it. We should finesse + // our math so that this never happens naturally. + const float max_increment{0.25f}; + if (display_time_increment_ > max_increment) { + BA_LOG_ONCE(LogName::kBaDisplayTime, LogLevel::kWarning, + "Calced excessively large display_time_increment_ (" + + std::to_string(display_time_increment_) + + "); should not happen."); + display_time_increment_ = max_increment; + } + g_core->Log(LogName::kBaDisplayTime, LogLevel::kDebug, [this, use_avg, this_increment, chaos, used] { char buffer[256]; diff --git a/src/ballistica/base/networking/network_reader.cc b/src/ballistica/base/networking/network_reader.cc index 9892f3b0..8b3ec21b 100644 --- a/src/ballistica/base/networking/network_reader.cc +++ b/src/ballistica/base/networking/network_reader.cc @@ -241,7 +241,11 @@ auto NetworkReader::RunThread_() -> int { recvfrom(sd, buffer, sizeof(buffer), 0, reinterpret_cast(&from), &from_size); if (rresult == 0) { - g_core->Log(LogName::kBaNetworking, LogLevel::kError, + // Note: have gotten reports of server attacks with this log + // message repeating. So now only logging this once to eliminate + // repeated log overhead and hopefully make the attack less + // effective. + BA_LOG_ONCE(LogName::kBaNetworking, LogLevel::kError, "NetworkReader Recv got length 0; this shouldn't " "happen"); } else if (rresult == -1) { diff --git a/src/ballistica/base/python/base_python.h b/src/ballistica/base/python/base_python.h index 81e9083e..cbcd252e 100644 --- a/src/ballistica/base/python/base_python.h +++ b/src/ballistica/base/python/base_python.h @@ -117,6 +117,7 @@ class BasePython { kUnsupportedControllerMessageCall, kGetV2AccountIdCall, kAppOnNativeActiveChangedCall, + kCopyDevConsoleHistoryCall, kLast // Sentinel; must be at end. }; diff --git a/src/ballistica/base/ui/dev_console.cc b/src/ballistica/base/ui/dev_console.cc index a7f8d28b..03fbe5e6 100644 --- a/src/ballistica/base/ui/dev_console.cc +++ b/src/ballistica/base/ui/dev_console.cc @@ -39,13 +39,69 @@ const float kDevConsoleTabButtonCornerRadius{16.0f}; const double kTransitionSeconds{0.15}; enum class DevConsoleHAnchor_ { kLeft, kCenter, kRight }; -enum class DevButtonStyle_ { kNormal, kLight }; +enum class DevButtonStyle_ { + kNormal, + kBright, + kRed, + kRedBright, + kPurple, + kPurpleBright, + kYellow, + kYellowBright, + kBlue, + kBlueBright, + kWhite, + kWhiteBright, + kBlack, + kBlackBright +}; static auto DevButtonStyleFromStr_(const char* strval) { - if (!strcmp(strval, "light")) { - return DevButtonStyle_::kLight; + if (!strcmp(strval, "normal")) { + return DevButtonStyle_::kNormal; } - assert(!strcmp(strval, "normal")); + if (!strcmp(strval, "bright")) { + return DevButtonStyle_::kBright; + } + if (!strcmp(strval, "red")) { + return DevButtonStyle_::kRed; + } + if (!strcmp(strval, "red_bright")) { + return DevButtonStyle_::kRedBright; + } + if (!strcmp(strval, "blue")) { + return DevButtonStyle_::kBlue; + } + if (!strcmp(strval, "blue_bright")) { + return DevButtonStyle_::kBlueBright; + } + if (!strcmp(strval, "purple")) { + return DevButtonStyle_::kPurple; + } + if (!strcmp(strval, "purple_bright")) { + return DevButtonStyle_::kPurpleBright; + } + if (!strcmp(strval, "yellow")) { + return DevButtonStyle_::kYellow; + } + if (!strcmp(strval, "yellow_bright")) { + return DevButtonStyle_::kYellowBright; + } + if (!strcmp(strval, "white")) { + return DevButtonStyle_::kWhite; + } + if (!strcmp(strval, "white_bright")) { + return DevButtonStyle_::kWhiteBright; + } + if (!strcmp(strval, "black")) { + return DevButtonStyle_::kBlack; + } + if (!strcmp(strval, "black_bright")) { + return DevButtonStyle_::kBlackBright; + } + + g_core->Log(LogName::kBa, LogLevel::kError, + std::string("Invalid button-style: ") + strval); return DevButtonStyle_::kNormal; } @@ -260,7 +316,79 @@ class DevConsole::Button_ : public DevConsole::Widget_ { Vector3f fgcolor; Vector3f bgcolor; switch (style) { - case DevButtonStyle_::kLight: + case DevButtonStyle_::kYellow: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; + bgcolor = + pressed ? Vector3f{0.8f, 0.5f, 0.0f} : Vector3f{0.45, 0.4f, 0.35f}; + break; + case DevButtonStyle_::kYellowBright: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.0f, 0.0f, 0.0f}; + bgcolor = + pressed ? Vector3f{1.0f, 0.5f, 0.0f} : Vector3f{0.9, 0.7f, 0.0f}; + break; + case DevButtonStyle_::kRed: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; + bgcolor = + pressed ? Vector3f{1.0f, 0.2f, 0.2f} : Vector3f{0.45, 0.3f, 0.35f}; + break; + case DevButtonStyle_::kRedBright: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; + bgcolor = + pressed ? Vector3f{1.0f, 0.0f, 0.0f} : Vector3f{0.8, 0.05f, 0.1f}; + break; + case DevButtonStyle_::kPurple: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; + bgcolor = + pressed ? Vector3f{0.8f, 0.0f, 1.0f} : Vector3f{0.35, 0.2f, 0.4f}; + break; + case DevButtonStyle_::kPurpleBright: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; + bgcolor = + pressed ? Vector3f{1.0f, 0.5f, 1.0f} : Vector3f{0.6, 0.2f, 0.8f}; + break; + case DevButtonStyle_::kBlue: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; + bgcolor = + pressed ? Vector3f{0.0f, 0.5f, 0.7f} : Vector3f{0.35, 0.4f, 0.55f}; + break; + case DevButtonStyle_::kBlueBright: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.0f, 0.0f, 0.0f}; + bgcolor = + pressed ? Vector3f{0.2f, 0.2f, 1.0f} : Vector3f{0.5, 0.7f, 1.0f}; + break; + case DevButtonStyle_::kWhite: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; + bgcolor = + pressed ? Vector3f{0.3f, 0.3f, 0.3f} : Vector3f{0.38, 0.33f, 0.4f}; + break; + case DevButtonStyle_::kWhiteBright: + fgcolor = + pressed ? Vector3f{1.0f, 1.0f, 1.0f} : Vector3f{0.0f, 0.0f, 0.0f}; + bgcolor = + pressed ? Vector3f{0.4f, 0.4f, 0.4f} : Vector3f{0.8, 0.7f, 0.8f}; + break; + case DevButtonStyle_::kBlack: + fgcolor = + pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.8f, 0.7f, 0.8f}; + bgcolor = + pressed ? Vector3f{1.0f, 1.0f, 1.0f} : Vector3f{0.0, 0.0f, 0.0f}; + break; + case DevButtonStyle_::kBlackBright: + fgcolor = + pressed ? Vector3f{1.0f, 1.0f, 1.0f} : Vector3f{1.0f, 0.9f, 1.0f}; + bgcolor = + pressed ? Vector3f{0.4f, 0.4f, 0.4f} : Vector3f{0.25f, 0.2f, 0.25f}; + break; + case DevButtonStyle_::kBright: fgcolor = pressed ? Vector3f{0.0f, 0.0f, 0.0f} : Vector3f{0.9f, 0.8f, 0.9f}; bgcolor = @@ -605,6 +733,10 @@ void DevConsole::AddPythonTerminal() { "Exec", 0.5f * bs, DevConsoleHAnchor_::kRight, -33.0f * bs, 15.95f * bs, 32.0f * bs, 13.0f * bs, 2.0 * bs, DevButtonStyle_::kNormal, false, [this] { Exec(); })); + widgets_.emplace_back(std::make_unique( + "Copy History", 0.5f * bs, DevConsoleHAnchor_::kRight, -85.0f * bs, + Height() - 25.0f * bs, 80.0f * bs, 20.0f * bs, 4.0 * bs, + DevButtonStyle_::kNormal, false, [this] { CopyHistory(); })); python_terminal_visible_ = true; } @@ -1075,6 +1207,13 @@ auto DevConsole::HandleKeyRelease(const SDL_Keysym* keysym) -> bool { return state_ != State_::kInactive; } +void DevConsole::CopyHistory() { + BA_PRECONDITION(g_base->InLogicThread()); + g_base->python->objs() + .Get(BasePython::ObjID::kCopyDevConsoleHistoryCall) + .Call(); +} + void DevConsole::Exec() { BA_PRECONDITION(g_base->InLogicThread()); if (!input_enabled_) { diff --git a/src/ballistica/base/ui/dev_console.h b/src/ballistica/base/ui/dev_console.h index 924d6757..711a8984 100644 --- a/src/ballistica/base/ui/dev_console.h +++ b/src/ballistica/base/ui/dev_console.h @@ -57,6 +57,7 @@ class DevConsole { auto HandleMouseDown(int button, float x, float y) -> bool; void HandleMouseUp(int button, float x, float y); void Exec(); + void CopyHistory(); void AddButton(const char* label, float x, float y, float width, float height, PyObject* call, const char* h_anchor_str, float label_scale, diff --git a/src/ballistica/classic/support/classic_app_mode.cc b/src/ballistica/classic/support/classic_app_mode.cc index 607dd89f..c6a4872e 100644 --- a/src/ballistica/classic/support/classic_app_mode.cc +++ b/src/ballistica/classic/support/classic_app_mode.cc @@ -520,9 +520,9 @@ 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. + // prev-display-time-millisecs may be way far 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) { diff --git a/src/ballistica/scene_v1/support/host_session.cc b/src/ballistica/scene_v1/support/host_session.cc index b89f4fab..4f13403e 100644 --- a/src/ballistica/scene_v1/support/host_session.cc +++ b/src/ballistica/scene_v1/support/host_session.cc @@ -509,12 +509,13 @@ void HostSession::Update(int time_advance_millisecs, double time_advance) { } } - // Shouldn't be getting huge steps through here; watch out for that. + // We shouldn't be getting *huge* steps coming through here. Warn if that + // ever happens so we can fix it at the source. 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); + BA_LOG_ONCE(LogName::kBa, LogLevel::kError, + "HostSession::Update() got excessive time_advance (" + + std::to_string(time_advance_millisecs) + " ms, " + + std::to_string(time_advance) + " s); should not happen."); } // We can be killed at any time, so let's keep an eye out for that. diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index 58c87d83..4b12dde3 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int { namespace ballistica { // These are set automatically via script; don't modify them here. -const int kEngineBuildNumber = 22062; +const int kEngineBuildNumber = 22068; const char* kEngineVersion = "1.7.37"; const int kEngineApiVersion = 9; diff --git a/src/meta/babasemeta/pyembed/binding_base.py b/src/meta/babasemeta/pyembed/binding_base.py index f9db5a06..c80d5731 100644 --- a/src/meta/babasemeta/pyembed/binding_base.py +++ b/src/meta/babasemeta/pyembed/binding_base.py @@ -59,6 +59,7 @@ values = [ _hooks.get_dev_console_tab_names, # kGetDevConsoleTabNamesCall _hooks.unsupported_controller_message, # kUnsupportedControllerMessageCall _hooks.get_v2_account_id, # kGetV2AccountIdCall + _hooks.copy_dev_console_history, # kCopyDevConsoleHistoryCall _language.Lstr, # kLStrClass _general.Call, # kCallClass _apputils.garbage_collect_session_end, # kGarbageCollectSessionEndCall diff --git a/tools/bacommon/cloud.py b/tools/bacommon/cloud.py index 223fb714..6d71c36f 100644 --- a/tools/bacommon/cloud.py +++ b/tools/bacommon/cloud.py @@ -4,11 +4,11 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Annotated, override +from typing import TYPE_CHECKING, Annotated, override, assert_never from enum import Enum from efro.message import Message, Response -from efro.dataclassio import ioprepped, IOAttrs +from efro.dataclassio import ioprepped, IOAttrs, IOMultiType from bacommon.transfer import DirectoryManifest from bacommon.login import LoginType @@ -323,3 +323,49 @@ class BSPrivatePartyResponse(Response): tokens: Annotated[int, IOAttrs('t')] gold_pass: Annotated[bool, IOAttrs('g')] datacode: Annotated[str | None, IOAttrs('d')] + + +class CloudSubscriptionRequestTypeID(Enum): + """Type ID for each of our subclasses.""" + + TEST = 'test' + + +class CloudSubscriptionRequest(IOMultiType[CloudSubscriptionRequestTypeID]): + """Top level class for our multitype.""" + + @override + @classmethod + def get_type_id(cls) -> CloudSubscriptionRequestTypeID: + # Require child classes to supply this themselves. If we + # did a full type registry/lookup here it would require us + # to import everything and would prevent lazy loading. + raise NotImplementedError() + + @override + @classmethod + def get_type( + cls, type_id: CloudSubscriptionRequestTypeID + ) -> type[CloudSubscriptionRequest]: + """Return the subclass for each of our type-ids.""" + # pylint: disable=cyclic-import + out: type[CloudSubscriptionRequest] + + t = CloudSubscriptionRequestTypeID + if type_id is t.TEST: + out = TestCloudSubscriptionRequest + else: + # Important to make sure we provide all types. + assert_never(type_id) + return out + + +@ioprepped +@dataclass +class TestCloudSubscriptionRequest(CloudSubscriptionRequest): + """Just a test.""" + + @override + @classmethod + def get_type_id(cls) -> CloudSubscriptionRequestTypeID: + return CloudSubscriptionRequestTypeID.TEST diff --git a/tools/efro/message/_protocol.py b/tools/efro/message/_protocol.py index 4e3e1ccd..1d18ea57 100644 --- a/tools/efro/message/_protocol.py +++ b/tools/efro/message/_protocol.py @@ -47,6 +47,7 @@ class MessageProtocol: forward_clean_errors: bool = False, remote_errors_include_stack_traces: bool = False, log_errors_on_receiver: bool = True, + log_response_decode_errors: bool = True, ) -> None: """Create a protocol with a given configuration. @@ -78,14 +79,22 @@ class MessageProtocol: goal is usually to avoid returning opaque RemoteErrors and to instead return something meaningful as part of the expected response type (even if that value itself represents a logical - error state). If 'log_errors_on_receiver' is False, however, such - exceptions will *not* be logged on the receiver. This can be - useful in combination with 'remote_errors_include_stack_traces' - and 'forward_clean_errors' in situations where all error - logging/management will be happening on the sender end. Be - aware, however, that in that case it may be possible for - communication errors to prevent such error messages from - ever being seen. + error state). If 'log_errors_on_receiver' is False, however, + such exceptions will *not* be logged on the receiver. This can + be useful in combination with + 'remote_errors_include_stack_traces' and 'forward_clean_errors' + in situations where all error logging/management will be + happening on the sender end. Be aware, however, that in that + case it may be possible for communication errors to prevent some + errors from ever being acknowledged. + + If an error occurs when decoding a message response, a + RuntimeError is generated locally. However, in practice it is + likely for such errors to be silently ignored by message + handling code alongside more common communication type errors, + meaning serious protocol breakage could go unnoticed. To avoid + this, a log message is also printed in such cases. Pass + 'log_response_decode_errors' as False to disable this logging. """ # pylint: disable=too-many-locals self.message_types_by_id: dict[int, type[Message]] = {} @@ -170,6 +179,7 @@ class MessageProtocol: remote_errors_include_stack_traces ) self.log_errors_on_receiver = log_errors_on_receiver + self.log_response_decode_errors = log_response_decode_errors @staticmethod def encode_dict(obj: dict) -> str: @@ -252,7 +262,7 @@ class MessageProtocol: return out def message_from_dict(self, data: dict) -> Message: - """Decode a message from a json string.""" + """Decode a message from a dict.""" out = self._from_dict(data, self.message_types_by_id, 'message') assert isinstance(out, Message) return out diff --git a/tools/efro/message/_sender.py b/tools/efro/message/_sender.py index 93cd38f6..189c2183 100644 --- a/tools/efro/message/_sender.py +++ b/tools/efro/message/_sender.py @@ -6,6 +6,7 @@ Supports static typing for message types and possible return types. from __future__ import annotations +import logging from typing import TYPE_CHECKING from efro.error import CleanError, RemoteError, CommunicationError @@ -369,6 +370,19 @@ class MessageSender: bound_obj, message, response_dict, response ) except Exception as exc: + + # We pragmatically log by default if decoding fails. This + # means a message type was likely changed in a way that + # breaks the protocol, but individual message handlers are + # likely to lump all errors together (communication and + # otherwise) which could cause such breakage to go + # unnoticed. + if self.protocol.log_response_decode_errors: + logging.exception( + 'Error decoding message response;' + ' protocol might be broken.', + ) + response = ErrorSysResponse( error_message='Error decoding raw response.', error_type=ErrorSysResponse.ErrorType.LOCAL,