server lag fix and timer cleanup

This commit is contained in:
Eric 2023-06-14 18:34:44 -07:00
parent 7b21a7fccc
commit 782a8773a4
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
23 changed files with 279 additions and 93 deletions

88
.efrocachemap generated
View File

@ -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",

View File

@ -135,6 +135,7 @@
<w>apptime</w>
<w>apptimer</w>
<w>apptimers</w>
<w>apptimesecs</w>
<w>apputils</w>
<w>archbase</w>
<w>archivepath</w>
@ -2595,6 +2596,7 @@
<w>sirplus</w>
<w>sitebuiltins</w>
<w>skey</w>
<w>sleepsecs</w>
<w>sline</w>
<w>slist</w>
<w>slists</w>

View File

@ -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)

View File

@ -92,6 +92,7 @@
<w>apptime</w>
<w>apptimer</w>
<w>apptimers</w>
<w>apptimesecs</w>
<w>archbase</w>
<w>archos</w>
<w>argindex</w>
@ -1499,6 +1500,7 @@
<w>sincestr</w>
<w>sisssssssss</w>
<w>sixteenbits</w>
<w>sleepsecs</w>
<w>slist</w>
<w>slists</w>
<w>smod</w>

View File

@ -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

View File

@ -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

View File

@ -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 ""; }

View File

@ -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)

View File

@ -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.

View File

@ -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<double>(display_time_microsecs_) / 1000000.0;
display_time_increment_ =
static_cast<double>(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<double>(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<double>(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<microsecs_t>(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>& 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) {

View File

@ -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>& 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<TimerList> display_timers_;
EventLoop* event_loop_{};
Timer* process_pending_work_timer_{};

View File

@ -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()};

View File

@ -103,7 +103,7 @@ auto PythonClassDisplayTimer::tp_new(PyTypeObject* type, PyObject* args,
return nullptr;
}
self->timer_id_ = g_base->logic->NewDisplayTimer(
static_cast<millisecs_t>(length * 1000.0), repeat,
static_cast<microsecs_t>(length * 1000000.0), repeat,
Object::New<Runnable, PythonContextCallRunnable>(call_obj));
self->have_timer_ = true;

View File

@ -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<millisecs_t>(length * 1000.0), false,
Object::New<Runnable, PythonContextCallRunnable>(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<millisecs_t>(length * 1000.0), false,
g_base->logic->NewDisplayTimer(
static_cast<microsecs_t>(length * 1000000.0), false,
Object::New<Runnable, PythonContextCallRunnable>(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",
};

View File

@ -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);
}

View File

@ -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) {

View File

@ -578,6 +578,14 @@ void HostSession::Update(int time_advance_millisecs, double time_advance) {
assert(test_ref.Exists());
}
auto HostSession::TimeToNextEvent() -> std::optional<microsecs_t> {
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 {

View File

@ -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<microsecs_t> override;
private:
void StepScene();

View File

@ -408,6 +408,25 @@ auto SceneV1AppMode::GetPartySize() const -> int {
return cJSON_GetArraySize(game_roster_);
}
auto SceneV1AppMode::GetHeadlessDisplayStep() -> microsecs_t {
std::optional<microsecs_t> 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<millisecs_t>(
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;

View File

@ -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();

View File

@ -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<microsecs_t> {
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*) {}

View File

@ -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<microsecs_t>;
// If this returns false, the screen will be cleared as part of rendering.
virtual auto DoesFillScreen() const -> bool = 0;

View File

@ -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 {