From d3f3679473aa10c74d2994a27764b7f9f0dc5a55 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 15 Sep 2022 12:40:56 -0700 Subject: [PATCH] lang updates and cache functionality for LogHandler --- .efrocachemap | 72 +++++------ CHANGELOG.md | 2 +- assets/src/ba_data/python/._ba_sources_hash | 2 +- assets/src/ba_data/python/_ba.py | 11 +- assets/src/ba_data/python/ba/_bootstrap.py | 5 +- src/ballistica/ballistica.cc | 2 +- src/ballistica/logic/logic.h | 6 +- .../python/methods/python_methods_app.cc | 14 +-- src/ballistica/python/python.cc | 2 +- tools/efro/log.py | 115 ++++++++++++++---- 10 files changed, 152 insertions(+), 79 deletions(-) diff --git a/.efrocachemap b/.efrocachemap index a767e9d7..9a8a07d8 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -420,8 +420,8 @@ "assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/60/ad/38269b7f1c7dc20cb9a506cd0681", "assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/72/85/d6fc4d16b7081d91fba2850b5b10", "assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/e9/ae/1d674d0c086eaa0bd1c3b1db0505", - "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/e8/42/a43c158be7fa45f2c0c3d4b84a1f", - "assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/5b/cf/4501b151257c3d8d6ee8d0497d14", + "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/d4/64/6fff42a428e5c775795c081474e6", + "assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/e2/24/5e7ea9ca5c9de4d3b7a28e53564d", "assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/61/03/89070ca765e06da3a419a579f503", "assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/f8/15/e1a2fa38697417bcf2cf19cd34ef", "assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/44/aa/c12568afb4558dc7f9f2fa155467", @@ -431,17 +431,17 @@ "assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/68/93/da8e9874f41a786edf52ba4ccaad", "assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/72/80/d6395c8a168558750c0d79ce769b", "assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/4c/c7/0184b8178869d1a3827a1bfcd5bb", - "assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/8f/73/093120ae2241d8f4b899ccda2d75", - "assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/25/65/1cb03566e73811fc6e1b841d9072", + "assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/e9/07/b2dc862601bcd70701b083d43279", + "assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/2e/48/b0a8fafc5e5436e99d9a3d697d23", "assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/ef/e6/d4909f571d7473fd04055728490e", "assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/8a/2a/b2bc00eed0608b2199b2bc379b2e", "assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/82/eb/37ff44af76812097f9c98f05c730", - "assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/50/e8/837be1324c8128507b3df89b689f", + "assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/91/98/42701cd595c2f70b7484614a8f49", "assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/d8/f2/aa16bc336bd7660cc86c3264bfc4", - "assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/e3/85/14e57e3f49505e5a190daf7fe276", + "assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/23/3b/26e9be528460af952a11e98c3b68", "assets/build/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/1a/10/9563348e729d1e5c8ae8c9cbc1f2", "assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/a8/e9/171a904f1331fdb7b1918a0f2598", - "assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/83/4a/ec10142ac479bf8d80455b47a62b", + "assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/df/b1/b2c9ebaad5e873ebedd365726d3d", "assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/19/e9/59c891b1fb85f3ba9f19283c233d", "assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/da/95/36797ec53a697a04e55b225a701d", "assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/d7/06/9d70642d0a4d1e3b1c2149d7a17c", @@ -452,7 +452,7 @@ "assets/build/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/91/0a/35c4baf539d5951fc03a794c0e0b", "assets/build/ba_data/data/languages/tamil.json": "https://files.ballistica.net/cache/ba1/94/1a/533bc718e676191bafc25e2dc98f", "assets/build/ba_data/data/languages/thai.json": "https://files.ballistica.net/cache/ba1/f7/df/7ba5f99c5c2c4c86fc0503fcf0b7", - "assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/14/a0/783cc6da2d122e9a7482c6a5ef8c", + "assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/9a/90/8e2ed626def09f88c3b9ab5215a3", "assets/build/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/ab/35/644e4239cfa62a597a905412b90c", "assets/build/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/53/9e/068074156b38bab7f732977a4031", "assets/build/ba_data/data/languages/vietnamese.json": "https://files.ballistica.net/cache/ba1/25/13/b64b849fc9fedcc18d81f6e08c4d", @@ -3995,26 +3995,26 @@ "assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e", "assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34", "ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a", - "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/15/b2/0744afc264f1e55a5944bf8ae964", - "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/8e/98/ea10bd892f89c7ba5aec76721667", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/aa/e4/7c73515c9044e051a5d07cb1e964", - "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ba/37/245e7c3dc79588d73a57f9f08b55", - "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ad/28/034d014eea6aeba4b67d51cfc262", - "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/77/df/c85dfe5cb062b6925da7df8e740c", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/58/6b/058b05227950f5d83f23b01617d1", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/bd/31/02ecb4d8c3bea8eb68d92befdd9d", - "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/aa/9d/96555101ebf8b14223b7639cea5b", - "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/1d/9a/16feda78b815cbed47b89983c48e", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b0/31/f60dc64681a35406595ed4836f0a", - "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b5/44/eb4645945794253446c55dc682bc", - "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/43/56/592cd419bfad9cbbb6e3f57c1007", - "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c6/55/0dfcc3d90a9cee9212e2e10a91b1", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/19/c6/994729e095b54e6963ac2d4bbd10", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ce/21/77cd76cd1b0307ae8cc7038152e7", - "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a9/e6/23835561d55b6c1d1862217fc115", - "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/21/3d/e0a1e20ae12dd7b5ba1d61aed074", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/be/b5/fc827900f411576b992fc15a25fc", - "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/6f/51/d6a0951b6ab9122dd99ef33d4427", + "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f3/e1/0ee9ade5e9943dc4749aa4cc2182", + "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/32/4e/a7c1b096c62864641a59e65e7778", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6a/f4/8b86611cbaa7237c8a52e8fe6428", + "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/13/f5/fa3c97269613316994f0c2134860", + "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f8/7a/87877003ee3a13ff7cbbf1ec76a2", + "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/81/0c/708a16fff55ee37338b2d87f7620", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/4a/91/89523b8f155261e0045a6bb6e277", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a9/04/da76c8e848852fef1e58577f59e3", + "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/dd/ac/33aed9cc1396ccba2111dfed02f6", + "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/1a/5a/0f1e671137a2ec60c2df8e95e078", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/92/50/ec5fbb3f8b9f4a60934f2dacf180", + "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7c/77/a430bf182d5210792716b6dbd8ee", + "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ef/6b/de6c811fa70bf4d5d1cdfe49eaae", + "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/92/e0/b9b1053231d7323d185298f8caaa", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/08/ef/c70efcd89a666758437f0736d956", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9d/94/1f0d32c943b64ff558f28ad89653", + "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a6/fb/a6e6a78d7b04d547c9c716ff0a8e", + "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/5a/b0/d0e087fcdf1756a0b9b5bf805f10", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/34/fb/e6c9fb0e20af0ba3376ca2abba29", + "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/b1/8b/5ba626c39ef586f3ba4bfd508181", "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d5/f6/d62e6e6d5e7fe1945f08ccbb9a8f", "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/02/c5/44d0082442b06153a7d7dce4c8ce", "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d0/00/61378fb26ddcbf023fd3c40e4ffb", @@ -4031,14 +4031,14 @@ "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ff/26/da4a58abecf5d9275477eaec0c17", "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/62/5d/8658a206b8c9b741be2422162784", "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fc/57/d59920e098d23a2d150c899cde29", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/fb/8e/3bb1c858451a447f1a102d77c281", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/ae/ad/13ae735e45d31d2944c89e8bdcec", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/e8/3e/6c2546278ad0428965a05c9bd536", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/d4/e5/0fe217cac5837cf663c7c7f1aa92", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ea/38/a5082d73202113900166a1ebd15f", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/ba/1c/16df69d156e23ab29e2c84465bd7", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/9e/b0/91b2e0b1dbd4541543f3f147bc06", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/c1/04/ad892d77317be76bc8fa035f9e86", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/39/a1/bce547622da143fcf9ea970cd3ab", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/d6/f3/8ca052a667342e316fa09655bc51", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/e0/b4/99c403ccbfd0c4b9b071e656d480", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/11/a0/e06103dae63a4648850bd089aacb", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/77/17/99eba0c7d42a9452306f7927ae4e", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/92/da/2faf9031e7c5ba5a99dd9e0c609d", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/64/2a/1b30ee7b4e8238ec2ea16bcacdeb", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/41/92/35b689da32ee45a4e7483feae8f8", "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523", "src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02", "src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6479bda6..b348d5cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.7 (build 20856, api 7, 2022-09-15) +### 1.7.7 (build 20857, api 7, 2022-09-15) - Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread. - Improved logging of missing playlist game types. - Some ba.Lstr functionality can now be used in background threads. diff --git a/assets/src/ba_data/python/._ba_sources_hash b/assets/src/ba_data/python/._ba_sources_hash index d78d9181..8eee74b6 100644 --- a/assets/src/ba_data/python/._ba_sources_hash +++ b/assets/src/ba_data/python/._ba_sources_hash @@ -1 +1 @@ -76251027805752156826413428926087661089 \ No newline at end of file +41453813326605937968225345803585801012 \ No newline at end of file diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py index 34e91a0a..0f4b6cb3 100644 --- a/assets/src/ba_data/python/_ba.py +++ b/assets/src/ba_data/python/_ba.py @@ -2518,11 +2518,12 @@ def screenmessage(message: str | ba.Lstr, Category: **General Utility Functions** If 'top' is True, the message will go to the top message area. - For 'top' messages, 'image' can be a texture to display alongside the - message. - If 'log' is True, the message will also be printed to the output log - 'clients' can be a list of client-ids the message should be sent to, - or None to specify that everyone should receive it. + For 'top' messages, 'image' must be a dict containing 'texture' + and 'tint_texture' textures and 'tint_color' and 'tint2_color' + colors. This defines an icon to display alongside the message. + If 'log' is True, the message will also be submitted to the log. + 'clients' can be a list of client-ids the message should be sent + to, or None to specify that everyone should receive it. If 'transient' is True, the message will not be included in the game-stream and thus will not show up when viewing replays. Currently the 'clients' option only works for transient messages. diff --git a/assets/src/ba_data/python/ba/_bootstrap.py b/assets/src/ba_data/python/ba/_bootstrap.py index 0b54ef3c..d3089177 100644 --- a/assets/src/ba_data/python/ba/_bootstrap.py +++ b/assets/src/ba_data/python/ba/_bootstrap.py @@ -35,7 +35,8 @@ def bootstrap() -> None: log_handler = setup_logging(log_path=None, level=LogLevel.DEBUG, suppress_non_root_debug=True, - log_stdout_stderr=True) + log_stdout_stderr=True, + cache_size_limit=1024 * 1024) log_handler.add_callback(_on_log) @@ -43,7 +44,7 @@ def bootstrap() -> None: # Give a soft warning if we're being used with a different binary # version than we expect. - expected_build = 20856 + expected_build = 20857 running_build: int = env['build_number'] if running_build != expected_build: print( diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc index 568e0e6c..676a9493 100644 --- a/src/ballistica/ballistica.cc +++ b/src/ballistica/ballistica.cc @@ -32,7 +32,7 @@ namespace ballistica { // These are set automatically via script; don't modify them here. -const int kAppBuildNumber = 20856; +const int kAppBuildNumber = 20857; const char* kAppVersion = "1.7.7"; // Our standalone globals. diff --git a/src/ballistica/logic/logic.h b/src/ballistica/logic/logic.h index 57799970..c1d55633 100644 --- a/src/ballistica/logic/logic.h +++ b/src/ballistica/logic/logic.h @@ -19,9 +19,9 @@ namespace ballistica { const int kMaxPartyNameCombinedSize = 25; -/// The Game Module generally runs on a dedicated thread; it manages -/// all game logic, builds frame_defs to send to the graphics-server for -/// rendering, etc. +/// The logic subsystem of the app. This runs on a dedicated thread +/// and is where high level app logic happens. Much app functionality +/// including UI calls must be run on the logic thread. class Logic { public: Logic(); diff --git a/src/ballistica/python/methods/python_methods_app.cc b/src/ballistica/python/methods/python_methods_app.cc index 44f2e45b..3ed0d566 100644 --- a/src/ballistica/python/methods/python_methods_app.cc +++ b/src/ballistica/python/methods/python_methods_app.cc @@ -988,14 +988,12 @@ auto PythonMethodsApp::GetMethods() -> std::vector { "Category: **General Utility Functions**\n" "\n" "If 'top' is True, the message will go to the top message area.\n" - "For 'top' messages, 'image' can be a texture to display alongside " - "the\n" - "message.\n" - "If 'log' is True, the message will also be printed to the output " - "log\n" - "'clients' can be a list of client-ids the message should be sent " - "to,\n" - "or None to specify that everyone should receive it.\n" + "For 'top' messages, 'image' must be a dict containing 'texture'\n" + "and 'tint_texture' textures and 'tint_color' and 'tint2_color'\n" + "colors. This defines an icon to display alongside the message.\n" + "If 'log' is True, the message will also be submitted to the log.\n" + "'clients' can be a list of client-ids the message should be sent\n" + "to, or None to specify that everyone should receive it.\n" "If 'transient' is True, the message will not be included in the\n" "game-stream and thus will not show up when viewing replays.\n" "Currently the 'clients' option only works for transient messages."}, diff --git a/src/ballistica/python/python.cc b/src/ballistica/python/python.cc index 3be6a998..56eeb33e 100644 --- a/src/ballistica/python/python.cc +++ b/src/ballistica/python/python.cc @@ -70,7 +70,7 @@ namespace ballistica { auto Python::LoggingCall(LogLevel loglevel, const std::string& msg) -> void { // If we've not yet captured our Python logging calls, stash this call away. - // We'll submit all accumulated entries after we bootstrap python. + // We'll submit all accumulated entries after we bootstrap Python. if (!objexists(ObjID::kLoggingCriticalCall)) { std::scoped_lock lock(early_log_lock_); early_logs_.emplace_back(std::make_pair(loglevel, msg)); diff --git a/tools/efro/log.py b/tools/efro/log.py index 684e6a69..339a517c 100644 --- a/tools/efro/log.py +++ b/tools/efro/log.py @@ -67,6 +67,21 @@ class LogEntry: time: Annotated[datetime.datetime, IOAttrs('t')] +@ioprepped +@dataclass +class LogArchive: + """Info and data for a log.""" + + # Total number of entries submitted to the log. + log_size: Annotated[int, IOAttrs('t')] + + # Offset for the entries contained here. + # (10 means our first entry is the 10th in the log, etc.) + start_index: Annotated[int, IOAttrs('c')] + + entries: Annotated[list[LogEntry], IOAttrs('e')] + + class LogHandler(logging.Handler): """Fancy-pants handler for logging output. @@ -83,7 +98,8 @@ class LogHandler(logging.Handler): def __init__(self, path: str | Path | None, echofile: TextIO | None, - suppress_non_root_debug: bool = False): + suppress_non_root_debug: bool = False, + cache_size_limit: int = 0): super().__init__() # pylint: disable=consider-using-with self._file = (None @@ -97,14 +113,20 @@ class LogHandler(logging.Handler): 'stdout': None, 'stderr': None } + self._cache_size = 0 + assert cache_size_limit >= 0 + self._cache_size_limit = cache_size_limit + self._cache: list[tuple[int, LogEntry]] = [] + self._cache_index_offset = 0 + self._cache_lock = Lock() self._printed_callback_error = False self._thread_bootstrapped = False self._thread = Thread(target=self._thread_main, daemon=True) self._thread.start() - # Spin until our thread has set up its basic stuff; - # otherwise we could wind up trying to push stuff to our - # event loop before the loop exists. + # 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. while not self._thread_bootstrapped: time.sleep(0.001) @@ -124,6 +146,37 @@ class LogHandler(logging.Handler): self._thread_bootstrapped = True self._event_loop.run_forever() + def get_archive(self, + start_index: int = 0, + max_entries: int | None = None) -> LogArchive: + """Build and return an archive of log entries. + + This will only return entries that have been processed by the + background thread so may not include just-submitted logs. + Entries in the range [start_index:start_index+max_entries] that + are still in the cache will be returned. Be aware that this may + not be the full requested range. + """ + + assert start_index >= 0 + if max_entries is not None: + assert max_entries >= 0 + with self._cache_lock: + # Transform start_index to our present cache space. + start_index -= self._cache_index_offset + # Calc end-index in our present cache space. + end_index = (len(self._cache) + if max_entries is None else start_index + max_entries) + + # Clamp both indexes to both ends of our present space. + start_index = max(0, min(start_index, len(self._cache))) + end_index = max(0, min(end_index, len(self._cache))) + + return LogArchive( + log_size=self._cache_index_offset + len(self._cache), + start_index=start_index + self._cache_index_offset, + entries=[e[1] for e in self._cache[start_index:end_index]]) + def emit(self, record: logging.LogRecord) -> None: # Called by logging to send us records. # We simply package them up and ship them to our thread. @@ -143,22 +196,23 @@ class LogHandler(logging.Handler): # didn't expect to be stringified. msg = self.format(record) - # Also 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: - cbegin: str - cend: str - cbegin, cend = LEVELNO_COLOR_CODES.get(record.levelno, ('', '')) - self._echofile.write(f'{cbegin}{msg}{cend}\n') + ends = LEVELNO_COLOR_CODES.get(record.levelno) + if ends is not None: + self._echofile.write(f'{ends[0]}{msg}{ends[1]}\n') + else: + self._echofile.write(f'{msg}\n') self._event_loop.call_soon_threadsafe( - tpartial(self._emit_in_loop, record.name, record.levelno, + tpartial(self._emit_in_thread, record.name, record.levelno, record.created, msg)) - def _emit_in_loop(self, name: str, levelno: int, created: float, - message: str) -> None: + def _emit_in_thread(self, name: str, levelno: int, created: float, + message: str) -> None: try: self._emit_entry( LogEntry(name=name, @@ -174,9 +228,9 @@ class LogHandler(logging.Handler): """Send raw stdout/stderr output to the logger to be collated.""" self._event_loop.call_soon_threadsafe( - tpartial(self._file_write_in_loop, name, output)) + tpartial(self._file_write_in_thread, name, output)) - def _file_write_in_loop(self, name: str, output: str) -> None: + def _file_write_in_thread(self, name: str, output: str) -> None: try: assert name in ('stdout', 'stderr') @@ -222,9 +276,26 @@ class LogHandler(logging.Handler): self._file_chunk_ship_task[name] = None def _emit_entry(self, entry: LogEntry) -> None: - # This runs in our bg event loop thread and does most of the work. assert current_thread() is self._thread + # Store to our cache. + if self._cache_size_limit > 0: + with self._cache_lock: + # Do a rough calc of how many bytes this entry consumes. + entry_size = sum( + sys.getsizeof(x) + for x in (entry, entry.name, entry.message, entry.level, + entry.time)) + self._cache.append((entry_size, entry)) + self._cache_size += entry_size + + # Prune old until we are back at or under our limit. + while self._cache_size > self._cache_size_limit: + popped = self._cache.pop(0) + self._cache_size -= popped[0] + self._cache_index_offset += 1 + + # Pass to callbacks. with self._callbacks_lock: for call in self._callbacks: try: @@ -271,11 +342,12 @@ class FileLogEcho: def setup_logging(log_path: str | Path | None, level: LogLevel, suppress_non_root_debug: bool = False, - log_stdout_stderr: bool = False) -> LogHandler: + log_stdout_stderr: bool = False, + cache_size_limit: int = 0) -> LogHandler: """Set up our logging environment. Returns the custom handler which can be used to fetch information - about logs that have passed through it. (worst log-levels, etc.). + about logs that have passed through it. (worst log-levels, caches, etc.). """ lmap = { @@ -295,7 +367,8 @@ def setup_logging(log_path: str | Path | None, loghandler = LogHandler( log_path, echofile=sys.stderr if sys.stderr.isatty() else None, - suppress_non_root_debug=suppress_non_root_debug) + suppress_non_root_debug=suppress_non_root_debug, + cache_size_limit=cache_size_limit) # Note: going ahead with force=True here so that we replace any # existing logger. Though we warn if it looks like we are doing