diff --git a/.efrocachemap b/.efrocachemap index 2c79de44..8be752eb 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4072,50 +4072,50 @@ "build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/18/4b/787a9267e17be3c49966072581a5", "build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/20/f6/4ce9bc3c1f3732f6adf8237fbe9b", "build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/a5/30/9058181df0b1255bf6950cbc7813", - "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/8c/ed/211ad9931645ec21344cc09943cf", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/71/df/89649ebeee912d3671994700ca98", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/3a/55/2e767201ff0210baf2aab336b962", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/96/b7/14b9ff3221e0643c5808408889a0", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/96/a6/67826a50b92ef1b97c9350357bde", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/1a/00/5f26c8e62d546190ef1a0e5d86c1", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e9/d9/c37edc30a6d87dc6a3fff69cd69d", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/f0/96/eafa605033a621f0024ce23e8506", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/e7/0c/e5d1f7f76463dab3a593c7e39d28", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/23/98/40563ec129aeae3ae29fce9a4098", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/19/db/025a2e4224285384ca5138b528b9", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/3b/04/f7f76fbbb71c51d398999b91d962", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/ce/65/019b91c6590778d7e2f32a6dc86d", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/70/5d/74932199660c0faa295fc8b331e5", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e1/37/98c5e9424f0c180db6aa16a0a2e9", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c9/3f/3dc59e05f156376638a59e606954", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/6b/2b/6a98ef5411ea98d0190aae33ca8a", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/65/82/defe87b3e240bbabd8194beb71a2", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/39/10/6e8db73db72f65b094401dc66972", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/45/5d/a65990370464ca038cd0a1be680a", - "build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/3c/a8/5487ece199be8717cead6de157e3", - "build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/9d/71/77a0ae15f3216c51da44ccefeb87", - "build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/8c/de/a04c75afaee10276fedbd6dc12dd", - "build/prefab/lib/linux_arm64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/c1/45/f2143d53646891111b648a6cc014", - "build/prefab/lib/linux_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/4c/0c/2afa4f64c6030ba37cacdb62fb4d", - "build/prefab/lib/linux_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/d2/0a/0d4b99f583a4e7a55761c5493c06", - "build/prefab/lib/linux_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/5e/ff/b8cd37fe6d5fa5df6a7bc6103ecc", - "build/prefab/lib/linux_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/84/e3/815f8ba4a1c0ff4b01e44bd95682", - "build/prefab/lib/mac_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/10/98/48262f1e81f07154c28cfec87ee9", - "build/prefab/lib/mac_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/6e/97/e8029482a29578a9f6cc15813265", - "build/prefab/lib/mac_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/5d/ac/fbe75dcd9f4a4461552cdaf8a3be", - "build/prefab/lib/mac_arm64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/94/ec/cce65d8f6f76e240597b073f17e7", - "build/prefab/lib/mac_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/a9/15/89ec9af80cf169f837ea6c3c365d", - "build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/e1/d8/0949e33c04570cae7dafaf906770", - "build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/91/c5/f1c4dcdd2233d6ffc280db77ec99", - "build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/20/e5/3b83e186b63a95d704c69a6b5b76", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/bf/fa/f79dc4a7ca33a8cca9df0547eef4", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/9e/3c/09855cda12586dbba55884a259b0", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/47/f1/3eadde3dcabd1b0b9be32a7f247f", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/f3/f6/51991b000385d553f35563df4190", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/a3/7d/cae2bd2e4daf11f90e73937170fc", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/1e/cc/ab11419f2d792b8738605a1f3a24", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/11/eb/55affc8d77ccac52c0b8fd128ad7", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/5a/57/2df7d185467abcca2287a4410ee8", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/35/07/d6fdff6fe4bd1e69a223c3a620aa", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/3f/67/cdaf89a3ae87c92e7a69ead533d1", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/16/a7/857b4c971a3129c69ec7daa1d0bc", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/0e/65/21ae4a44d94cbd82e7f3a3a122b5", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/62/a4/5f233d592052188accbe49ab0ec2", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/2c/dd/417f3aefe03828172469ed81630b", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/2e/97/df7ecde6a7e82fd4a579cce8bae7", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/83/f7/dbf7a4c6a63ae84f0e0eff4974c3", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/57/1e/8208bc0c7de0def0e7cc399b412b", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/54/1f/f718b7159d58a8af4ab14caa9d06", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/78/f2/59cd0bcd802c0a977a3352ec8be4", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d7/09/709476d32c9d060225c8717366bc", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/e9/9a/4a1c14fd9ef8d96aa1052242671c", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/04/87/e2d9488fa7d0e44fcd2ae386ab5d", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/24/6f/c8a72d8010e6e3e721d64e1a80c7", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/9d/6a/806d3b173c7739a04cad0df3cb32", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/e0/70/aaf9248064e99fef9b934b52f486", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/91/0c/6e58dac0732e20de8536fa187a16", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/8a/95/23c7dadc9d94aabef250b352a5cd", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/c9/b8/afeaa6080be3c45430f73900bbcf", + "build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/d9/56/b0a16db98621500b473844ec39d6", + "build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/57/f9/f304c67d9958574b6b35de2ad13d", + "build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/04/55/a41ad832c081ff421039827d16c1", + "build/prefab/lib/linux_arm64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/65/a7/2008753f3dbe890a730ca081ee8f", + "build/prefab/lib/linux_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/a1/1f/cb01b3a6773cd8fc1a3ed1c320c5", + "build/prefab/lib/linux_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/49/a7/b65d9e50fc94a82e88e5802020b4", + "build/prefab/lib/linux_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/25/28/a1657fe9f97172fee036f8f31173", + "build/prefab/lib/linux_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/5c/5b/2edf7105dcd7a653fa449af25bab", + "build/prefab/lib/mac_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/c9/02/670a183eed30429ebd3c61a26229", + "build/prefab/lib/mac_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/7d/50/17bb34179dd958f1e3563634dab5", + "build/prefab/lib/mac_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/3f/9d/30372ce093efd265ac89955ef774", + "build/prefab/lib/mac_arm64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/ca/89/4d9fb94ed0ec2e5cc36f40908f34", + "build/prefab/lib/mac_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/cd/87/606884c0a1d45fbfc57db6c635cb", + "build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/88/1a/1ea680450c3c9bc97147df4bf99d", + "build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/91/b3/31b980ca2a000871408cbc7923e3", + "build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/67/d1/8b98ad3d0d67bf6bc23c2c9167cf", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/6d/bf/7b03106aec7089c1ff956aee1569", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/06/df/2cfcaec212dcb94432a247a631e7", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/e8/60/503824952252ee34eedfb779aa0e", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/28/1a/dfdeaffd5d7f997679ba534117db", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/47/6c/d9fa447b2fae36bfaa6ec1344a66", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/b0/c3/0f4e4ab477affd0451e44476ad0a", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/c9/a3/3fd02b8024684d98a361e3c0ec70", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/1c/20/a80e4f7eea1631ed3da2a1279340", "src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/52/c6/c11130af7b10d6c0321add5518fa", "src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/38/c3/1dedd5e74f2508efc5974c8815a1", "src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/ea/6a/6a4721b144e5e297b542d2a0eea2", diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index 4f60d122..cf48990b 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -135,6 +135,7 @@ apptime apptimer apptimers + apptimesecs apputils archbase archivepath @@ -2595,6 +2596,7 @@ sirplus sitebuiltins skey + sleepsecs sline slist slists diff --git a/CHANGELOG.md b/CHANGELOG.md index aeda0363..f0275112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.20 (build 21107, api 8, 2023-06-14) +### 1.7.20 (build 21108, api 8, 2023-06-14) - This seems like a good time for a `refactoring` release in anticipation of changes coming in 1.8. Basically this means that a lot of things will be @@ -321,6 +321,12 @@ would draw incorrectly. - (build 21106) Fixed an issue where in-game ping would always display green no matter how bad the ping was. +- (build 21107) Upped internal display-timer resolution from milliseconds to + microseconds. +- (build 21107) Finished implementing new scheduling system for headless mode. + This should fix the issue where 1.7.20 servers would have 100ms of lag by + default. Server performance should now be equal to or better than 1.7.19. + Please holler if not. ### 1.7.19 (build 20997, api 7, 2023-01-19) diff --git a/ballisticakit-cmake/.idea/dictionaries/ericf.xml b/ballisticakit-cmake/.idea/dictionaries/ericf.xml index 3df10246..884c233c 100644 --- a/ballisticakit-cmake/.idea/dictionaries/ericf.xml +++ b/ballisticakit-cmake/.idea/dictionaries/ericf.xml @@ -92,6 +92,7 @@ apptime apptimer apptimers + apptimesecs archbase archos argindex @@ -1499,6 +1500,7 @@ sincestr sisssssssss sixteenbits + sleepsecs slist slists smod diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index c6dadc35..c10da081 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -28,7 +28,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21107 +TARGET_BALLISTICA_BUILD = 21108 TARGET_BALLISTICA_VERSION = '1.7.20' _g_env_config: EnvConfig | None = None diff --git a/src/assets/ba_data/python/bauiv1lib/settings/plugins.py b/src/assets/ba_data/python/bauiv1lib/settings/plugins.py index d658867c..cccb2f2e 100644 --- a/src/assets/ba_data/python/bauiv1lib/settings/plugins.py +++ b/src/assets/ba_data/python/bauiv1lib/settings/plugins.py @@ -386,6 +386,8 @@ class PluginWindow(bui.Window): sel_name = 'Settings' elif sel == self._back_button: sel_name = 'Back' + elif sel == self._scrollwidget: + sel_name = 'Scroll' else: raise ValueError(f'unrecognized selection \'{sel}\'') assert bui.app.classic is not None diff --git a/src/ballistica/base/app/app_mode.cc b/src/ballistica/base/app/app_mode.cc index 3ee71a1a..a93105ba 100644 --- a/src/ballistica/base/app/app_mode.cc +++ b/src/ballistica/base/app/app_mode.cc @@ -42,6 +42,10 @@ void AppMode::ChangeGameSpeed(int offs) {} void AppMode::StepDisplayTime() {} +auto AppMode::GetHeadlessDisplayStep() -> microsecs_t { + return kAppModeMaxHeadlessDisplayStep; +} + auto AppMode::GetPartySize() const -> int { return 0; } auto AppMode::GetNetworkDebugString() -> std::string { return ""; } diff --git a/src/ballistica/base/app/app_mode.h b/src/ballistica/base/app/app_mode.h index 7df5bb96..f00f854b 100644 --- a/src/ballistica/base/app/app_mode.h +++ b/src/ballistica/base/app/app_mode.h @@ -9,6 +9,15 @@ namespace ballistica::base { +/// The max amount of time a headless app can sleep if no events are pending. +/// This should not be *too* high or it might cause delays when going from +/// no events present to events present. +const microsecs_t kAppModeMaxHeadlessDisplayStep{500000}; + +/// The min amount of time a headless app can sleep. This provides an upper +/// limit on stepping overhead in cases where events are densely packed. +const microsecs_t kAppModeMinHeadlessDisplayStep{1000}; + /// Represents 'what the app is doing'. The global app-mode can be switched /// as the app is running. Be aware that, unlike the App/App classes /// which operate in the main thread, most functionality here is based in the @@ -32,10 +41,20 @@ class AppMode { /// Apply the app config. virtual void ApplyAppConfig(); - /// Update the logic thread. Can be called at any frequency; generally - /// corresponds to frame draws or a fixed timer. + /// Update the logic thread for a new display-time. Can be called at any + /// frequency. In gui builds, generally corresponds with frame drawing. In + /// headless builds, generally corresponds with scene stepping or other + /// scheduled events. Check values on g_base->logic to see current + /// display-time and most recent step size applied. virtual void StepDisplayTime(); + /// Called right after stepping; should return the exact microseconds + /// between the current display time and the next event the app-mode has + /// scheduled. If no events are pending, should return + /// kAppModeMaxHeadlessDisplayStep. This will only be called on headless + /// builds. + virtual auto GetHeadlessDisplayStep() -> microsecs_t; + /// Create a delegate for an input-device. /// Return a raw pointer allocated using Object::NewDeferred. virtual auto CreateInputDeviceDelegate(InputDevice* device) diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc index 1e6a5ec8..3eeb7778 100644 --- a/src/ballistica/base/base.cc +++ b/src/ballistica/base/base.cc @@ -238,6 +238,9 @@ void BaseFeatureSet::set_app_mode(AppMode* mode) { input->RebuildInputDeviceDelegates(); app_mode_->OnActivate(); + + // Let some stuff know. + logic->OnAppModeChanged(); } catch (const Exception& exc) { // Anything going wrong while switching app-modes leaves us in an // undefined state; don't try to continue. diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index 18b0878f..233f4523 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -220,8 +220,10 @@ void Logic::CompleteAppBootstrapping() { // rate (10hz) to keep things efficient. Anyone dealing in display-time // should be able to handle a wide variety of rates anyway. if (g_core->HeadlessMode()) { + // NOTE: This length is currently milliseconds. headless_display_time_step_timer_ = event_loop()->NewTimer( - 1000 / 10, true, NewLambdaRunnable([this] { StepDisplayTime(); })); + kAppModeMinHeadlessDisplayStep / 1000, true, + NewLambdaRunnable([this] { StepDisplayTime(); })); } // Let our initial app-mode know it has become active. g_base->app_mode()->OnActivate(); @@ -267,7 +269,17 @@ void Logic::OnScreenSizeChange(float virtual_width, float virtual_height, void Logic::StepDisplayTime() { assert(g_base->InLogicThread()); - UpdateDisplayTime(); + // We have two different modes of operation here. When running in headless + // mode, display time is driven by upcoming events such as sim steps; we + // basically want to sleep as long as we can and run steps exactly when + // events occur. When running with a gui, our display-time is driven by + // real draw times and is intended to keep frame intervals as visually + // consistent and smooth looking as possible. + if (g_core->HeadlessMode()) { + UpdateDisplayTimeForHeadlessMode(); + } else { + UpdateDisplayTimeForFrameDraw(); + } // Give all our subsystems some update love. // Note: keep these in the same order as OnAppStart. @@ -285,10 +297,86 @@ void Logic::StepDisplayTime() { // Let's run display-timers *after* we step everything else so most things // they interact with will be in an up-to-date state. - display_timers_->Run(g_core->GetAppTimeMillisecs()); + display_timers_->Run(display_time_microsecs_); + + if (g_core->HeadlessMode()) { + PostUpdateDisplayTimeForHeadlessMode(); + } } -void Logic::UpdateDisplayTime() { +void Logic::OnAppModeChanged() { + assert(g_base->InLogicThread()); + + // Kick our headless stepping into high gear; this will snap us out of + // any long sleep we're currently in the middle of. + if (g_core->HeadlessMode()) { + if (debug_log_display_time_) { + Log(LogLevel::kDebug, + "Resetting headless display step timer due to app-mode change."); + } + assert(headless_display_time_step_timer_); + // NOTE: This is currently milliseconds. + headless_display_time_step_timer_->SetLength(kAppModeMinHeadlessDisplayStep + / 1000); + } +} + +void Logic::UpdateDisplayTimeForHeadlessMode() { + assert(g_base->InLogicThread()); + // In this case we just keep display time synced up with app time; we 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 app mode has events scheduled + // (or at least close enough so we can fudge it and tell them its that exact + // time). + + auto app_time_microsecs = g_core->GetAppTimeMicrosecs(); + + auto old_display_time_microsecs = display_time_microsecs_; + display_time_microsecs_ = app_time_microsecs; + display_time_increment_microsecs_ = + display_time_microsecs_ - old_display_time_microsecs; + + // In this path our float values are driven by our int ones. + display_time_ = static_cast(display_time_microsecs_) / 1000000.0; + display_time_increment_ = + static_cast(display_time_increment_microsecs_) / 1000000.0; + + if (debug_log_display_time_) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "stepping display-time at app-time %.4f", + static_cast(app_time_microsecs) / 1000000.0); + Log(LogLevel::kDebug, buffer); + } +} + +void Logic::PostUpdateDisplayTimeForHeadlessMode() { + assert(g_base->InLogicThread()); + // At this point we've stepped our app-mode, so let's ask it how + // long we've got until the next event. We'll plug this into our + // display-update timer so we can try to sleep until that point. + auto headless_display_step_microsecs = + std::max(std::min(g_base->app_mode()->GetHeadlessDisplayStep(), + kAppModeMaxHeadlessDisplayStep), + kAppModeMinHeadlessDisplayStep); + + if (debug_log_display_time_) { + auto sleepsecs = + static_cast(headless_display_step_microsecs) / 1000000.0; + auto apptimesecs = g_core->GetAppTimeSeconds(); + char buffer[256]; + snprintf(buffer, sizeof(buffer), + "will try to sleep for %.4f at app-time %.4f (until %.4f)", + sleepsecs, apptimesecs, apptimesecs + sleepsecs); + Log(LogLevel::kDebug, buffer); + } + + auto sleep_millisecs = headless_display_step_microsecs / 1000; + headless_display_time_step_timer_->SetLength(sleep_millisecs); +} + +void Logic::UpdateDisplayTimeForFrameDraw() { // Here we update our smoothed display-time-increment based on how fast // we are currently rendering frames. We want display-time to basically // be progressing at the same rate as app-time but in as constant @@ -371,13 +459,14 @@ void Logic::UpdateDisplayTime() { avg /= count; double range = max - min; - // If our range of recent increment values is somewhat large relative to an - // average value, things are probably chaotic so just use the current value - // to respond quickly to changes. If things are more calm, use our nice - // smoothed value. + // If our range of recent increment values is somewhat large relative to + // an average value, things are probably chaotic, so just use the + // current value to respond quickly to changes. If things are more calm, + // use our nice smoothed value. + // So in a case where we're seeing an average increment of 16ms, we snap - // out of avg mode if there's more than 8ms between the longest and shortest - // increments. + // out of avg mode if there's more than 8ms between the longest and + // shortest increments. double chaos = range / avg; bool use_avg = chaos < 0.5; auto used = use_avg ? avg : this_increment; @@ -388,14 +477,14 @@ void Logic::UpdateDisplayTime() { // range. // How far the smoothed increment value needs to get away from the final - // value to actually start moving it. - // Example: If our avg increment is 16.6ms (60fps), don't change our - // increment until the 'used' value is more than 0.5ms (16.6 * 0.03) from it - // in either direction. - // Note: In practice I'm seeing that higher framerates - // like 120 need buffers that are larger relative to avg to remain stable. - // Though perhaps a bit of jitter is not noticeable at high frame rates; - // just something to keep an eye on. + // value to actually start moving it. Example: If our avg increment is + // 16.6ms (60fps), don't change our increment until the 'used' value is + // more than 0.5ms (16.6 * 0.03) from it in either direction. + + // Note: In practice I'm seeing that higher framerates like 120 need + // buffers that are larger relative to avg to remain stable. Though + // perhaps a bit of jitter is not noticeable at high frame rates; just + // something to keep an eye on. auto trail_buffer{avg * 0.03}; auto trailing_diff = used - display_time_increment_; @@ -424,6 +513,11 @@ void Logic::UpdateDisplayTime() { } // Lastly, apply our updated increment value to our time. display_time_ += display_time_increment_; + + // In this path, our integer values just follow our float ones. + auto prev_microsecs = display_time_microsecs_; + display_time_microsecs_ = static_cast(display_time_ * 1000000.0); + display_time_increment_microsecs_ = display_time_microsecs_ - prev_microsecs; } // Set up our sleeping based on what we're doing. @@ -521,13 +615,13 @@ void Logic::SetAppTimerLength(int timer_id, millisecs_t length) { } } -auto Logic::NewDisplayTimer(millisecs_t length, bool repeat, +auto Logic::NewDisplayTimer(microsecs_t length, bool repeat, const Object::Ref& runnable) -> int { // Display-Timers go into a timer-list that we exec explicitly when we // step display-time. assert(g_base->InLogicThread()); int offset = 0; - Timer* t = display_timers_->NewTimer(g_core->GetAppTimeMillisecs(), length, + Timer* t = display_timers_->NewTimer(g_core->GetAppTimeMicrosecs(), length, offset, repeat ? -1 : 0, runnable); return t->id(); } @@ -537,7 +631,7 @@ void Logic::DeleteDisplayTimer(int timer_id) { display_timers_->DeleteTimer(timer_id); } -void Logic::SetDisplayTimerLength(int timer_id, millisecs_t length) { +void Logic::SetDisplayTimerLength(int timer_id, microsecs_t length) { assert(g_base->InLogicThread()); Timer* t = display_timers_->GetTimer(timer_id); if (t) { diff --git a/src/ballistica/base/logic/logic.h b/src/ballistica/base/logic/logic.h index a3339ad5..3fa62f75 100644 --- a/src/ballistica/base/logic/logic.h +++ b/src/ballistica/base/logic/logic.h @@ -31,6 +31,8 @@ class Logic { void OnAppResume(); void OnAppShutdown(); + void OnAppModeChanged(); + void ApplyAppConfig(); void OnScreenSizeChange(float virtual_width, float virtual_height, float pixel_width, float pixel_height); @@ -58,35 +60,48 @@ class Logic { void DeleteAppTimer(int timer_id); void SetAppTimerLength(int timer_id, millisecs_t length); - auto NewDisplayTimer(millisecs_t length, bool repeat, + auto NewDisplayTimer(microsecs_t length, bool repeat, const Object::Ref& runnable) -> int; void DeleteDisplayTimer(int timer_id); - void SetDisplayTimerLength(int timer_id, millisecs_t length); + void SetDisplayTimerLength(int timer_id, microsecs_t length); - /// Get current display-time for the app (in seconds). - /// Display-time is a seconds value that increments smoothly with - /// frame draws. + /// Get current display-time for the app in seconds. auto display_time() { return display_time_; } - /// Return current display-time increment (in seconds). This can shift with - /// framerate changes but should remain mostly constant. + /// Get current display-time for the app in microseconds. + auto display_time_microsecs() { return display_time_microsecs_; } + + /// Return current display-time increment in seconds. auto display_time_increment() -> double { return display_time_increment_; } + /// Return current display-time increment in microseconds. + auto display_time_increment_microsecs() -> double { + return display_time_increment_microsecs_; + } + auto applied_app_config() const { return applied_app_config_; } private: - void UpdateDisplayTime(); + void UpdateDisplayTimeForFrameDraw(); + void UpdateDisplayTimeForHeadlessMode(); + void PostUpdateDisplayTimeForHeadlessMode(); void CompleteAppBootstrapping(); void ProcessPendingWork(); void UpdatePendingWorkTimer(); void StepDisplayTime(); double display_time_{}; + microsecs_t display_time_microsecs_{}; double display_time_increment_{1.0 / 60.0}; + microsecs_t display_time_increment_microsecs_{1000000 / 60}; + + // GUI scheduling. double last_display_time_update_app_time_{-1.0}; double recent_display_time_increments_[kDisplayTimeSampleCount]{}; int recent_display_time_increments_index_{-1}; + // Headless scheduling. + std::unique_ptr display_timers_; EventLoop* event_loop_{}; Timer* process_pending_work_timer_{}; diff --git a/src/ballistica/base/platform/base_platform.cc b/src/ballistica/base/platform/base_platform.cc index 48b484ea..4ce933c5 100644 --- a/src/ballistica/base/platform/base_platform.cc +++ b/src/ballistica/base/platform/base_platform.cc @@ -194,8 +194,8 @@ auto BasePlatform::GetPublicDeviceUUID() -> std::string { // We used to plug version in directly here, but that caused uuids to // shuffle too rapidly during periods of rapid development. This // keeps it more constant. - // __last_rand_uuid_component_shuffle_date__ 2022 12 17 - auto rand_uuid_component{"BMCJPHH0SC22KB0WVJ1RAYD68TPEXL58"}; + // __last_rand_uuid_component_shuffle_date__ 2023 6 15 + auto rand_uuid_component{"JVRWZ82D4WMBO110OA0IFJV7JKMQV8W3"}; inputs.emplace_back(rand_uuid_component); auto gil{Python::ScopedInterpreterLock()}; diff --git a/src/ballistica/base/python/class/python_class_display_timer.cc b/src/ballistica/base/python/class/python_class_display_timer.cc index 11c7654c..eb9330bf 100644 --- a/src/ballistica/base/python/class/python_class_display_timer.cc +++ b/src/ballistica/base/python/class/python_class_display_timer.cc @@ -103,7 +103,7 @@ auto PythonClassDisplayTimer::tp_new(PyTypeObject* type, PyObject* args, return nullptr; } self->timer_id_ = g_base->logic->NewDisplayTimer( - static_cast(length * 1000.0), repeat, + static_cast(length * 1000000.0), repeat, Object::New(call_obj)); self->have_timer_ = true; diff --git a/src/ballistica/base/python/methods/python_methods_app.cc b/src/ballistica/base/python/methods/python_methods_app.cc index fb4f16dd..3d7b58d3 100644 --- a/src/ballistica/base/python/methods/python_methods_app.cc +++ b/src/ballistica/base/python/methods/python_methods_app.cc @@ -341,7 +341,7 @@ static auto PyAppTimer(PyObject* self, PyObject* args, PyObject* keywds) if (length < 0) { throw Exception("Timer length cannot be < 0.", PyExcType::kValue); } - g_base->logic->NewDisplayTimer( + g_base->logic->NewAppTimer( static_cast(length * 1000.0), false, Object::New(call_obj)); Py_RETURN_NONE; @@ -376,9 +376,9 @@ static PyMethodDef PyAppTimerDef = { "##### Examples\n" "Print some stuff through time:\n" ">>> babase.screenmessage('hello from now!')\n" - ">>> babase.apptimer(1.0, ba.Call(ba.screenmessage,\n" + ">>> babase.apptimer(1.0, babase.Call(babase.screenmessage,\n" " 'hello from the future!'))\n" - ">>> babase.apptimer(2.0, ba.Call(ba.screenmessage,\n" + ">>> babase.apptimer(2.0, babase.Call(babase.screenmessage,\n" "... 'hello from the future 2!'))\n", }; @@ -434,8 +434,8 @@ static auto PyDisplayTimer(PyObject* self, PyObject* args, PyObject* keywds) if (length < 0) { throw Exception("Timer length cannot be < 0.", PyExcType::kValue); } - g_base->logic->NewAppTimer( - static_cast(length * 1000.0), false, + g_base->logic->NewDisplayTimer( + static_cast(length * 1000000.0), false, Object::New(call_obj)); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -474,9 +474,9 @@ static PyMethodDef PyDisplayTimerDef = { "##### Examples\n" "Print some stuff through time:\n" ">>> babase.screenmessage('hello from now!')\n" - ">>> babase.displaytimer(1.0, ba.Call(ba.screenmessage,\n" + ">>> babase.displaytimer(1.0, babase.Call(babase.screenmessage,\n" "... 'hello from the future!'))\n" - ">>> babase.displaytimer(2.0, ba.Call(ba.screenmessage,\n" + ">>> babase.displaytimer(2.0, babase.Call(babase.screenmessage,\n" "... 'hello from the future 2!'))\n", }; diff --git a/src/ballistica/base/support/app_timer.h b/src/ballistica/base/support/app_timer.h index f6f8ba0e..e116c26a 100644 --- a/src/ballistica/base/support/app_timer.h +++ b/src/ballistica/base/support/app_timer.h @@ -19,7 +19,7 @@ class AppTimer : public Object { timer_id_ = base::g_base->logic->NewAppTimer(length, repeat, runnable); } - void SetLength(uint32_t length) { + void SetLength(millisecs_t length) { assert(g_base->InLogicThread()); base::g_base->logic->SetAppTimerLength(timer_id_, length); } diff --git a/src/ballistica/scene_v1/node/flag_node.cc b/src/ballistica/scene_v1/node/flag_node.cc index 902003df..7605508b 100644 --- a/src/ballistica/scene_v1/node/flag_node.cc +++ b/src/ballistica/scene_v1/node/flag_node.cc @@ -401,7 +401,7 @@ void FlagNode::Step() { dBodyID b = body_->body(); assert(b); - if (g_core->HeadlessMode()) { + if (!g_core->HeadlessMode()) { dVector3 p; FullShadowSet* full_shadows = full_shadow_set_.Get(); if (full_shadows) { diff --git a/src/ballistica/scene_v1/support/host_session.cc b/src/ballistica/scene_v1/support/host_session.cc index 60e602a1..4e572a30 100644 --- a/src/ballistica/scene_v1/support/host_session.cc +++ b/src/ballistica/scene_v1/support/host_session.cc @@ -578,6 +578,14 @@ void HostSession::Update(int time_advance_millisecs, double time_advance) { assert(test_ref.Exists()); } +auto HostSession::TimeToNextEvent() -> std::optional { + if (base_timers_.Empty()) { + return {}; + } + auto to_next_ms = base_timers_.TimeToNextExpire(base_time_millisecs_); + return to_next_ms * 1000; // to microsecs. +} + HostSession::~HostSession() { assert(g_base->InLogicThread()); try { diff --git a/src/ballistica/scene_v1/support/host_session.h b/src/ballistica/scene_v1/support/host_session.h index 7ba2460f..d1d97eca 100644 --- a/src/ballistica/scene_v1/support/host_session.h +++ b/src/ballistica/scene_v1/support/host_session.h @@ -103,6 +103,7 @@ class HostSession : public Session { auto GetUnusedPlayerName(Player* p, const std::string& base_name) -> std::string; auto ContextAllowsDefaultTimerTypes() -> bool override; + auto TimeToNextEvent() -> std::optional override; private: void StepScene(); diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc index 2ef2814f..2ac0915b 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc @@ -408,6 +408,25 @@ auto SceneV1AppMode::GetPartySize() const -> int { return cJSON_GetArraySize(game_roster_); } +auto SceneV1AppMode::GetHeadlessDisplayStep() -> microsecs_t { + std::optional min_time_to_next; + for (auto&& i : sessions_) { + if (!i.Exists()) { + continue; + } + auto this_time_to_next = i->TimeToNextEvent(); + if (this_time_to_next.has_value()) { + if (!min_time_to_next.has_value()) { + min_time_to_next = *this_time_to_next; + } else { + min_time_to_next = std::min(*min_time_to_next, *this_time_to_next); + } + } + } + return min_time_to_next.has_value() ? *min_time_to_next + : base::kAppModeMaxHeadlessDisplayStep; +} + void SceneV1AppMode::StepDisplayTime() { assert(g_base->InLogicThread()); @@ -426,7 +445,8 @@ void SceneV1AppMode::StepDisplayTime() { // each time). millisecs_t legacy_display_time_millisecs_inc; if (legacy_display_time_millisecs_prev_ < 0) { - // Convert directly *only* the first time when we don't have prev available. + // Convert directly *only* the first time when we don't have prev + // available. legacy_display_time_millisecs_inc = static_cast( g_base->logic->display_time_increment() * 1000.0); @@ -677,8 +697,8 @@ void SceneV1AppMode::UpdateKickVote() { 1, 1, 0); kick_vote_in_progress_ = false; - // Disallow kicking for a while for everyone.. but ESPECIALLY so for the guy - // who launched the failed vote. + // Disallow kicking for a while for everyone.. but ESPECIALLY so for the + // guy who launched the failed vote. for (ConnectionToClient* client : connections()->GetConnectionsToClients()) { millisecs_t delay = kKickVoteFailRetryDelay; diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.h b/src/ballistica/scene_v1/support/scene_v1_app_mode.h index 50900802..b1ab1b4d 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.h +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.h @@ -182,6 +182,7 @@ class SceneV1AppMode : public base::AppMode { auto buffer_time() const { return buffer_time_; } void set_buffer_time(int val) { buffer_time_ = val; } void OnActivate() override; + auto GetHeadlessDisplayStep() -> microsecs_t override; private: void PruneScanResults(); diff --git a/src/ballistica/scene_v1/support/session.cc b/src/ballistica/scene_v1/support/session.cc index 4115b44d..474278da 100644 --- a/src/ballistica/scene_v1/support/session.cc +++ b/src/ballistica/scene_v1/support/session.cc @@ -19,6 +19,12 @@ Session::~Session() { g_core->session_count--; } void Session::Update(int time_advance_millisecs, double time_advance) {} +auto Session::TimeToNextEvent() -> std::optional { + BA_LOG_ONCE(LogLevel::kError, + "Session::TimeToNextEvent() being called; should not happen."); + return 5000000; +} + auto Session::GetForegroundContext() -> base::ContextRef { return {}; } void Session::Draw(base::FrameDef*) {} diff --git a/src/ballistica/scene_v1/support/session.h b/src/ballistica/scene_v1/support/session.h index c15735ca..92b1b4da 100644 --- a/src/ballistica/scene_v1/support/session.h +++ b/src/ballistica/scene_v1/support/session.h @@ -19,6 +19,9 @@ class Session : public SceneV1Context { /// a modern seconds advance. virtual void Update(int time_advance_millisecs, double time_advance); + /// Note: this should be returned in microsecs. + virtual auto TimeToNextEvent() -> std::optional; + // If this returns false, the screen will be cleared as part of rendering. virtual auto DoesFillScreen() const -> bool = 0; diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index 74136458..6ff49b5d 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 = 21107; +const int kEngineBuildNumber = 21108; const char* kEngineVersion = "1.7.20"; auto MonolithicMain(const core::CoreConfig& core_config) -> int {