From 6f2382fe698c031849187f52de415130d7b3ebda Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 5 Jan 2024 13:49:44 -0800 Subject: [PATCH] added show-demos-when-idle option --- .efrocachemap | 66 ++++++------ CHANGELOG.md | 11 +- src/assets/ba_data/python/babase/__init__.py | 2 + .../ba_data/python/baclassic/_benchmark.py | 102 +++++++++++------- .../ba_data/python/baclassic/_subsystem.py | 12 ++- src/assets/ba_data/python/baenv.py | 2 +- src/assets/ba_data/python/bascenev1/_lobby.py | 2 +- .../python/bascenev1/_multiteamsession.py | 4 + .../ba_data/python/bascenev1/_session.py | 2 +- .../ba_data/python/bascenev1lib/mainmenu.py | 28 ++++- src/assets/ba_data/python/bauiv1/__init__.py | 2 + .../ba_data/python/bauiv1lib/mainmenu.py | 2 +- .../python/bauiv1lib/settings/advanced.py | 31 +++++- .../base/input/device/input_device.cc | 22 ++-- .../base/input/device/input_device.h | 19 +++- .../base/input/device/joystick_input.cc | 8 +- .../base/input/device/joystick_input.h | 2 + .../base/input/device/test_input.cc | 15 ++- src/ballistica/base/input/input.cc | 56 +++++----- src/ballistica/base/input/input.h | 26 +++-- .../python/methods/python_methods_misc.cc | 21 ++++ src/ballistica/base/support/app_config.cc | 3 +- src/ballistica/base/support/app_config.h | 9 +- .../python/methods/python_methods_classic.cc | 13 ++- src/ballistica/classic/support/stress_test.cc | 10 +- src/ballistica/classic/support/stress_test.h | 5 +- .../python/class/python_class_input_device.cc | 13 +++ src/ballistica/shared/ballistica.cc | 2 +- 28 files changed, 339 insertions(+), 151 deletions(-) diff --git a/.efrocachemap b/.efrocachemap index 03af762d..c9466523 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -421,7 +421,7 @@ "build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26", "build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55", - "build/assets/ba_data/data/langdata.json": "6ae92385835c7f270dc35d7b4be3deef", + "build/assets/ba_data/data/langdata.json": "e71fd5c04d15c544dc15bc15bbe0798a", "build/assets/ba_data/data/languages/arabic.json": "0db32e21b6d5337ccca478381744aa88", "build/assets/ba_data/data/languages/belarussian.json": "a112dfca3e188387516788bd8229c5b0", "build/assets/ba_data/data/languages/chinese.json": "93f3ca9f90d86dc7c8d0923f5f11ef46", @@ -430,12 +430,12 @@ "build/assets/ba_data/data/languages/czech.json": "c9d518a324870066b987b8f412881dd3", "build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e", "build/assets/ba_data/data/languages/dutch.json": "5cbf1a68a9d93dee00dbc27f834d878a", - "build/assets/ba_data/data/languages/english.json": "2434e127b6788e3128d3523fcb1b8994", + "build/assets/ba_data/data/languages/english.json": "1c4037fea1066d39d6eced419f314f35", "build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880", - "build/assets/ba_data/data/languages/filipino.json": "e750fb1a95e4c5611115f9ece9ecab53", + "build/assets/ba_data/data/languages/filipino.json": "f897776672925499ecda8960b3aae607", "build/assets/ba_data/data/languages/french.json": "8bc35eb4b20a0b30c3348bcc9a3844a6", "build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad", - "build/assets/ba_data/data/languages/gibberish.json": "e24d391c9fd12f9afa92f7ff65a06d23", + "build/assets/ba_data/data/languages/gibberish.json": "b461539243e8efe3137137b886256ba7", "build/assets/ba_data/data/languages/greek.json": "287c0ec437b38772284ef9d3e4fb2fc3", "build/assets/ba_data/data/languages/hindi.json": "8848f6b0caec0fcf9d85bc6e683809ec", "build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e", @@ -455,7 +455,7 @@ "build/assets/ba_data/data/languages/tamil.json": "b4de1a2851afe4869c82e9acd94cd89c", "build/assets/ba_data/data/languages/thai.json": "77755219bbf5fb7eea0d6b226684f403", "build/assets/ba_data/data/languages/turkish.json": "72fac3a6fd13ebb74fb4d68b9323e7d4", - "build/assets/ba_data/data/languages/ukrainian.json": "e5c861187c4c6db37d1a033f4ef3dd5a", + "build/assets/ba_data/data/languages/ukrainian.json": "b54a38e93deebafa5706ba2d1f626892", "build/assets/ba_data/data/languages/venetian.json": "8e9714d98a85e428ce3543fc49188a46", "build/assets/ba_data/data/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba", "build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054", @@ -4060,26 +4060,26 @@ "build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", - "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "6d1c198240f58211b14534263de11034", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "0e29ddb400da245f7425e2779278b936", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "887c68a7be4c8d25c6eae21feb2fd39a", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "99a237557104d145f061b674e158539f", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "c813eb17692868fc54741e962d8ecca6", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "67b39d7c54a801c2b65bd8beb55771a2", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "20af8a239c97cbb8fe876c06d57c3f1f", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "60d09bd4c59e27f2f3c8b6331b650ee1", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "84733128a50a14b046306419419862d4", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "b81062addf907d312a9409660733205c", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "2770d31f91042c005cb0fc5cb368112c", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "316e8424de85400d5b84c84395689776", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "15bd20491afa0d90e3cea147d04e25a6", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "4697883ea3e8f9e8d215c9a82396c8a4", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "cd24a57ef4815f02f3c4f054fc9ae878", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "13b8e37ea683de88bce49e06a6cba24d", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "50218a71f166a51c6bccec5f8a946835", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "e20e058060ef6acc1e1f4cbccd69a7ce", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "13a90cb0c4a15daa64c8d883afd895e9", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "6af4eddfe3ed8a825a5ebd7702536fd7", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "8d62386f109621ee08bf6bc699e560b4", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "1ae0c73018caf6d563f7a2beed5630df", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cec12563653f88526ab0cdd56c35a659", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "c406758148e6a19a8ba776de09c9cf31", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "ec6b2f954c73f0a44d25eb12b76ae3df", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "190572e9a537ab1cce44a3a307cfb65f", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "d1dd35ec23c4e42d7bcd05406375b3f3", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "790f5c6df9bc038b9b0c1561e30cc17f", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "946739e98d88bbcdb615a97f66a3a9f5", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "e625d8908634593ca1917ae7a42846ea", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "48b463fbb8ad28f4c9487feb5383a1a7", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "a8fd4ed9a9db5878d21eee6d4d8035ce", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "a40f333afd411df2323b236f7931c72f", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "8c1b885d6bcae9c43b8ca45c3538d9f7", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "10e25021c620ae9df514e952d6f7a14c", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "5fdbf8e2c9619f093e4c0505619178f2", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "74527ceb879d581e162eea3052b74b48", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "4cbf3fa8efd3ac8051fdf7776684e80c", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "0dc81a9db0d827fd803ba3eac90c4453", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "bbd66efc658747530ec46af2620066c2", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "db535f0ca1e01af825f75f204fbc8928", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "97d51afca996ae15b61fd9f409a00459", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "db535f0ca1e01af825f75f204fbc8928", @@ -4096,14 +4096,14 @@ "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "452623f0495dd4375e5b5d9b80d643d5", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "ca49b32ed573feea11613d62cd89840c", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "452623f0495dd4375e5b5d9b80d643d5", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "3b19ca383e1a88bd8a0599181da738ba", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "2b9ee84d34b41dd7d92df7c7dece5ecc", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "78fc011efdd0d3c5ad91ba5cc7e25b36", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "1c60cf6ee1b3b7f1c4ca10f18ad55c0b", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a9d3d2dd577409f19129a2a14f03a871", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "c8cda906610d28b03a5d0abac69f752e", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "951e19df7d1f5d9befd0b1fe5e4f6390", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "b75ac3bc471e3b6121d27a83b07573da", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "8d7a27873efa3595c3209a19de05ff9d", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "54322d4265553cc935840cb2b966c18c", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "50254a1075a671d50fbb1a86aac86cb9", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "fe1eb9d9b16128058c38c46646e37a79", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "b7095b4bdeba71cb04dba2765e8d497c", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "06303398d77e04dc56badaf9f8480132", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "61f5d7b685ab8948995c4eba78b2bf3b", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "45df75b7cbfba620aa63a23a47ecd42b", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101", "src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f", diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d709c47..eb74ef8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ -### 1.7.33 (build 21747, api 8, 2024-01-03) - +### 1.7.33 (build 21751, api 8, 2024-01-05) +- Stress test input-devices are now a bit smarter; they won't press any buttons + while UIs are up (this could cause lots of chaos if it happened). +- Added a 'Show Demos When Idle' option in advanced settings. If enabled, the + game will show gameplay demos (a slightly modified form of the stress test) + periodically after sitting idle at the main menu for a bit. Like an old arcade + game. I added this for an upcoming conference appearance but though people + might like to enable it in other cases. + ### 1.7.32 (build 21741, api 8, 2023-12-20) - Fixed a screen message that no one will ever see (Thanks vishal332008?...) - Plugins window now displays 'No Plugins Installed' when no plugins are present (Thanks vishal332008!) diff --git a/src/assets/ba_data/python/babase/__init__.py b/src/assets/ba_data/python/babase/__init__.py index 9d4379f3..28da01e6 100644 --- a/src/assets/ba_data/python/babase/__init__.py +++ b/src/assets/ba_data/python/babase/__init__.py @@ -48,6 +48,7 @@ from _babase import ( fatal_error, get_display_resolution, get_immediate_return_code, + get_input_idle_time, get_low_level_config_value, get_max_graphics_quality, get_replays_dir, @@ -236,6 +237,7 @@ __all__ = [ 'garbage_collect', 'get_display_resolution', 'get_immediate_return_code', + 'get_input_idle_time', 'get_ip_address_type', 'get_low_level_config_value', 'get_max_graphics_quality', diff --git a/src/assets/ba_data/python/baclassic/_benchmark.py b/src/assets/ba_data/python/baclassic/_benchmark.py index 569f9d64..42e64636 100644 --- a/src/assets/ba_data/python/baclassic/_benchmark.py +++ b/src/assets/ba_data/python/baclassic/_benchmark.py @@ -4,6 +4,7 @@ from __future__ import annotations import random +from dataclasses import dataclass from typing import TYPE_CHECKING import babase @@ -48,64 +49,74 @@ def run_cpu_benchmark() -> None: bascenev1.new_host_session(BenchmarkSession, benchmark_type='cpu') +@dataclass +class _StressTestArgs: + playlist_type: str + playlist_name: str + player_count: int + round_duration: int + attract_mode: bool + + def run_stress_test( playlist_type: str = 'Random', playlist_name: str = '__default__', player_count: int = 8, round_duration: int = 30, + attract_mode: bool = False, ) -> None: """Run a stress test.""" - babase.screenmessage( - "Beginning stress test.. use 'End Test' to stop testing.", - color=(1, 1, 0), - ) with babase.ContextRef.empty(): - start_stress_test( - { - 'playlist_type': playlist_type, - 'playlist_name': playlist_name, - 'player_count': player_count, - 'round_duration': round_duration, - } + if not attract_mode: + babase.screenmessage( + "Beginning stress test.. use 'End Test' to stop testing.", + color=(1, 1, 0), + ) + _start_stress_test( + _StressTestArgs( + playlist_type=playlist_type, + playlist_name=playlist_name, + player_count=player_count, + round_duration=round_duration, + attract_mode=attract_mode, + ) ) def stop_stress_test() -> None: """End a running stress test.""" - _baclassic.set_stress_testing(False, 0) assert babase.app.classic is not None - try: - if babase.app.classic.stress_test_reset_timer is not None: - babase.screenmessage('Ending stress test...', color=(1, 1, 0)) - except Exception: - pass - babase.app.classic.stress_test_reset_timer = None + + _baclassic.set_stress_testing(False, 0, False) + babase.app.classic.stress_test_update_timer = None + babase.app.classic.stress_test_update_timer_2 = None -def start_stress_test(args: dict[str, Any]) -> None: +def _start_stress_test(args: _StressTestArgs) -> None: """(internal)""" from bascenev1 import DualTeamSession, FreeForAllSession assert babase.app.classic is not None appconfig = babase.app.config - playlist_type = args['playlist_type'] + playlist_type = args.playlist_type if playlist_type == 'Random': if random.random() < 0.5: playlist_type = 'Teams' else: playlist_type = 'Free-For-All' - babase.screenmessage( - 'Running Stress Test (listType="' - + playlist_type - + '", listName="' - + args['playlist_name'] - + '")...' - ) + if not args.attract_mode: + babase.screenmessage( + 'Running Stress Test (listType="' + + playlist_type + + '", listName="' + + args.playlist_name + + '")...' + ) if playlist_type == 'Teams': - appconfig['Team Tournament Playlist Selection'] = args['playlist_name'] + appconfig['Team Tournament Playlist Selection'] = args.playlist_name appconfig['Team Tournament Playlist Randomize'] = 1 babase.apptimer( 1.0, @@ -115,7 +126,7 @@ def start_stress_test(args: dict[str, Any]) -> None: ), ) else: - appconfig['Free-for-All Playlist Selection'] = args['playlist_name'] + appconfig['Free-for-All Playlist Selection'] = args.playlist_name appconfig['Free-for-All Playlist Randomize'] = 1 babase.apptimer( 1.0, @@ -124,19 +135,38 @@ def start_stress_test(args: dict[str, Any]) -> None: babase.Call(bascenev1.new_host_session, FreeForAllSession), ), ) - _baclassic.set_stress_testing(True, args['player_count']) - babase.app.classic.stress_test_reset_timer = babase.AppTimer( - args['round_duration'], babase.Call(_reset_stress_test, args) + _baclassic.set_stress_testing(True, args.player_count, args.attract_mode) + babase.app.classic.stress_test_update_timer = babase.AppTimer( + args.round_duration, babase.Call(_reset_stress_test, args) ) + if args.attract_mode: + babase.app.classic.stress_test_update_timer_2 = babase.AppTimer( + 0.48, babase.Call(_update_attract_mode_test, args), repeat=True + ) -def _reset_stress_test(args: dict[str, Any]) -> None: - _baclassic.set_stress_testing(False, args['player_count']) - babase.screenmessage('Resetting stress test...') +def _update_attract_mode_test(args: _StressTestArgs) -> None: + if babase.get_input_idle_time() < 5.0: + _reset_stress_test(args) + + +def _reset_stress_test(args: _StressTestArgs) -> None: + _baclassic.set_stress_testing(False, args.player_count, False) + if not args.attract_mode: + babase.screenmessage('Resetting stress test...') session = bascenev1.get_foreground_host_session() assert session is not None session.end() - babase.apptimer(1.0, babase.Call(start_stress_test, args)) + + assert babase.app.classic is not None + babase.app.classic.stress_test_update_timer = None + babase.app.classic.stress_test_update_timer_2 = None + + # For regular stress tests we keep the party going. For attract-mode + # we just end back at the main menu. If things are idle there then + # we'll get sent back to a new stress test. + if not args.attract_mode: + babase.apptimer(1.0, babase.Call(_start_stress_test, args)) def run_gpu_benchmark() -> None: diff --git a/src/assets/ba_data/python/baclassic/_subsystem.py b/src/assets/ba_data/python/baclassic/_subsystem.py index 16299bdc..755c4dd8 100644 --- a/src/assets/ba_data/python/baclassic/_subsystem.py +++ b/src/assets/ba_data/python/baclassic/_subsystem.py @@ -69,7 +69,8 @@ class ClassicSubsystem(babase.AppSubsystem): # Misc. self.tips: list[str] = [] - self.stress_test_reset_timer: babase.AppTimer | None = None + self.stress_test_update_timer: babase.AppTimer | None = None + self.stress_test_update_timer_2: babase.AppTimer | None = None self.value_test_defaults: dict = {} self.special_offer: dict | None = None self.ping_thread_count = 0 @@ -555,11 +556,18 @@ class ClassicSubsystem(babase.AppSubsystem): playlist_name: str = '__default__', player_count: int = 8, round_duration: int = 30, + attract_mode: bool = False, ) -> None: """Run a stress test.""" from baclassic._benchmark import run_stress_test as run - run(playlist_type, playlist_name, player_count, round_duration) + run( + playlist_type=playlist_type, + playlist_name=playlist_name, + player_count=player_count, + round_duration=round_duration, + attract_mode=attract_mode, + ) def get_input_device_mapped_value( self, device: bascenev1.InputDevice, name: str diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index fb07e230..75a544b8 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -52,7 +52,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21747 +TARGET_BALLISTICA_BUILD = 21751 TARGET_BALLISTICA_VERSION = '1.7.33' diff --git a/src/assets/ba_data/python/bascenev1/_lobby.py b/src/assets/ba_data/python/bascenev1/_lobby.py index aa2ab16f..001aa2d0 100644 --- a/src/assets/ba_data/python/bascenev1/_lobby.py +++ b/src/assets/ba_data/python/bascenev1/_lobby.py @@ -441,7 +441,7 @@ class Chooser: # list might have changed. input_device = self._sessionplayer.inputdevice is_remote = input_device.is_remote_client - is_test_input = input_device.name.startswith('TestInput') + is_test_input = input_device.is_test_input # Pull this player's list of unlocked characters. if is_remote: diff --git a/src/assets/ba_data/python/bascenev1/_multiteamsession.py b/src/assets/ba_data/python/bascenev1/_multiteamsession.py index cdd1bf2a..2c787915 100644 --- a/src/assets/ba_data/python/bascenev1/_multiteamsession.py +++ b/src/assets/ba_data/python/bascenev1/_multiteamsession.py @@ -70,6 +70,10 @@ class MultiTeamSession(Session): show_tutorial = cfg.get('Show Tutorial', True) + # Special case: don't show tutorial while stress testing. + if classic.stress_test_update_timer is not None: + show_tutorial = False + self._tutorial_activity_instance: bascenev1.Activity | None if show_tutorial: from bascenev1lib.tutorial import TutorialActivity diff --git a/src/assets/ba_data/python/bascenev1/_session.py b/src/assets/ba_data/python/bascenev1/_session.py index d561f12d..9ee55313 100644 --- a/src/assets/ba_data/python/bascenev1/_session.py +++ b/src/assets/ba_data/python/bascenev1/_session.py @@ -253,7 +253,7 @@ class Session: # Limit player counts *unless* we're in a stress test. if ( babase.app.classic is not None - and babase.app.classic.stress_test_reset_timer is None + and babase.app.classic.stress_test_update_timer is None ): if len(self.sessionplayers) >= self.max_players: # Print a rejection message *only* to the client trying to diff --git a/src/assets/ba_data/python/bascenev1lib/mainmenu.py b/src/assets/ba_data/python/bascenev1lib/mainmenu.py index 6092f0ce..73e82959 100644 --- a/src/assets/ba_data/python/bascenev1lib/mainmenu.py +++ b/src/assets/ba_data/python/bascenev1lib/mainmenu.py @@ -42,6 +42,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]): self._language: str | None = None self._update_timer: bs.Timer | None = None self._news: NewsDisplay | None = None + self._attract_mode_timer: bs.Timer | None = None def on_transition_in(self) -> None: # pylint: disable=too-many-locals @@ -295,6 +296,10 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]): if not (env.demo or env.arcade) and not app.ui_v1.use_toolbars: self._news = NewsDisplay(self) + self._attract_mode_timer = bs.Timer( + 3.12, self._update_attract_mode, repeat=True + ) + # Bring up the last place we were, or start at the main menu otherwise. with bs.ContextRef.empty(): from bauiv1lib import specialoffer @@ -387,7 +392,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]): bs.app.ui_v1.set_main_menu_window( MainMenuWindow(transition=None).get_root_widget(), - from_window=None, + from_window=False, # Disable check here. ) # attempt to show any pending offers immediately. @@ -403,6 +408,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]): bui.apptimer(2.0, specialoffer.show_offer) bui.apptimer(2.0, try_again) + app.classic.main_menu_did_initial_transition = True def _update(self) -> None: @@ -836,6 +842,26 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]): bui.apptimer(0.5, _start_menu_music) + def _update_attract_mode(self) -> None: + if bui.app.classic is None: + return + + if not bui.app.config.resolve('Show Demos When Idle'): + return + + threshold = 20.0 + + # If we're idle *and* have been in this activity for that long, + # flip over to our cpu demo. + if bui.get_input_idle_time() > threshold and bs.time() > threshold: + bui.app.classic.run_stress_test( + playlist_type='Random', + playlist_name='__default__', + player_count=8, + round_duration=20, + attract_mode=True, + ) + class NewsDisplay: """Wrangles news display.""" diff --git a/src/assets/ba_data/python/bauiv1/__init__.py b/src/assets/ba_data/python/bauiv1/__init__.py index cd49fca8..4f633f30 100644 --- a/src/assets/ba_data/python/bauiv1/__init__.py +++ b/src/assets/ba_data/python/bauiv1/__init__.py @@ -47,6 +47,7 @@ from babase import ( do_once, fade_screen, get_display_resolution, + get_input_idle_time, get_ip_address_type, get_low_level_config_value, get_max_graphics_quality, @@ -156,6 +157,7 @@ __all__ = [ 'do_once', 'fade_screen', 'get_display_resolution', + 'get_input_idle_time', 'get_ip_address_type', 'get_low_level_config_value', 'get_max_graphics_quality', diff --git a/src/assets/ba_data/python/bauiv1lib/mainmenu.py b/src/assets/ba_data/python/bauiv1lib/mainmenu.py index 4562a7fa..c9d26f96 100644 --- a/src/assets/ba_data/python/bauiv1lib/mainmenu.py +++ b/src/assets/ba_data/python/bauiv1lib/mainmenu.py @@ -1110,7 +1110,7 @@ class MainMenuWindow(bui.Window): session = bs.get_foreground_host_session() return getattr(session, 'benchmark_type', None) == 'cpu' or ( bui.app.classic is not None - and bui.app.classic.stress_test_reset_timer is not None + and bui.app.classic.stress_test_update_timer is not None ) def _confirm_end_game(self) -> None: diff --git a/src/assets/ba_data/python/bauiv1lib/settings/advanced.py b/src/assets/ba_data/python/bauiv1lib/settings/advanced.py index 61c72cbb..38285375 100644 --- a/src/assets/ba_data/python/bauiv1lib/settings/advanced.py +++ b/src/assets/ba_data/python/bauiv1lib/settings/advanced.py @@ -94,7 +94,7 @@ class AdvancedSettingsWindow(bui.Window): self._scroll_width = self._width - (100 + 2 * x_inset) self._scroll_height = self._height - 115.0 self._sub_width = self._scroll_width * 0.95 - self._sub_height = 766.0 + self._sub_height = 808.0 if self._show_always_use_internal_keyboard: self._sub_height += 62 @@ -489,6 +489,17 @@ class AdvancedSettingsWindow(bui.Window): maxwidth=430, ) + v -= 42 + self._show_demos_when_idle_check_box = ConfigCheckBox( + parent=self._subcontainer, + position=(50, v), + size=(self._sub_width - 100, 30), + configkey='Show Demos When Idle', + displayname=bui.Lstr(resource=f'{self._r}.showDemosWhenIdleText'), + scale=1.0, + maxwidth=430, + ) + v -= 42 self._disable_camera_shake_check_box = ConfigCheckBox( parent=self._subcontainer, @@ -575,12 +586,18 @@ class AdvancedSettingsWindow(bui.Window): up_widget=self._always_use_internal_keyboard_check_box.widget, ) else: - bui.widget( - edit=self._modding_guide_button, - up_widget=self._kick_idle_players_check_box.widget, + # ew. + next_widget_up = ( + self._disable_gyro_check_box.widget + if self._disable_gyro_check_box is not None + else self._disable_camera_shake_check_box.widget ) bui.widget( - edit=self._kick_idle_players_check_box.widget, + edit=self._modding_guide_button, + up_widget=next_widget_up, + ) + bui.widget( + edit=next_widget_up, down_widget=self._modding_guide_button, ) @@ -803,6 +820,8 @@ class AdvancedSettingsWindow(bui.Window): sel_name = 'Benchmarks' elif sel == self._kick_idle_players_check_box.widget: sel_name = 'KickIdlePlayers' + elif sel == self._show_demos_when_idle_check_box.widget: + sel_name = 'ShowDemosWhenIdle' elif sel == self._show_game_ping_check_box.widget: sel_name = 'ShowPing' elif sel == self._disable_camera_shake_check_box.widget: @@ -870,6 +889,8 @@ class AdvancedSettingsWindow(bui.Window): sel = self._benchmarks_button elif sel_name == 'KickIdlePlayers': sel = self._kick_idle_players_check_box.widget + elif sel_name == 'ShowDemosWhenIdle': + sel = self._show_demos_when_idle_check_box.widget elif sel_name == 'ShowPing': sel = self._show_game_ping_check_box.widget elif sel_name == 'DisableCameraShake': diff --git a/src/ballistica/base/input/device/input_device.cc b/src/ballistica/base/input/device/input_device.cc index 8086e2b6..42b27a25 100644 --- a/src/ballistica/base/input/device/input_device.cc +++ b/src/ballistica/base/input/device/input_device.cc @@ -52,9 +52,9 @@ InputDevice::~InputDevice() { assert(g_base->InLogicThread()); } // control something please. void InputDevice::RequestPlayer() { assert(g_base->InLogicThread()); + // Make note that we're being used in some way. - last_input_time_millisecs_ = - static_cast(g_base->logic->display_time() * 1000.0); + UpdateLastActiveTime(); delegate_->RequestPlayer(); } @@ -69,11 +69,19 @@ auto InputDevice::AttachedToPlayer() const -> bool { void InputDevice::DetachFromPlayer() { delegate_->DetachFromPlayer(); } -void InputDevice::UpdateLastInputTime() { - // Keep our own individual time, and also let the overall input system - // know something happened. - last_input_time_millisecs_ = +void InputDevice::UpdateLastActiveTime() { + // Special case: in attract-mode, prevent our virtual test devices from + // affecting input last-active times otherwise it'll kick us out of + // attract mode. + if (allow_input_in_attract_mode_ && g_base->input->attract_mode()) { + return; + } + + // Mark active time on this specific device. + last_active_time_millisecs_ = static_cast(g_base->logic->display_time() * 1000.0); + + // Mark input in general as active also. g_base->input->MarkInputActive(); } @@ -81,7 +89,7 @@ void InputDevice::InputCommand(InputType type, float value) { assert(g_base->InLogicThread()); // Make note that we're being used in some way. - UpdateLastInputTime(); + UpdateLastActiveTime(); delegate_->InputCommand(type, value); } diff --git a/src/ballistica/base/input/device/input_device.h b/src/ballistica/base/input/device/input_device.h index 8d566a2f..e3f41b51 100644 --- a/src/ballistica/base/input/device/input_device.h +++ b/src/ballistica/base/input/device/input_device.h @@ -93,8 +93,8 @@ class InputDevice : public Object { /// that button activates default widgets (will cause a start icon to show up /// on them). virtual auto start_button_activates_default_widget() -> bool { return false; } - auto last_input_time_millisecs() const -> millisecs_t { - return last_input_time_millisecs_; + auto last_active_time_millisecs() const -> millisecs_t { + return last_active_time_millisecs_; } virtual auto ShouldBeHiddenFromUser() -> bool; @@ -117,7 +117,7 @@ class InputDevice : public Object { /// been added to the input-device list, have a valid ID, name, etc. virtual void OnAdded() {} - void UpdateLastInputTime(); + void UpdateLastActiveTime(); auto delegate() -> InputDeviceDelegate& { // TEMP - Tracking down a crash in the wild. @@ -136,18 +136,27 @@ class InputDevice : public Object { auto custom_default_player_name() const -> std::string { return custom_default_player_name_; } + void set_custom_default_player_name(const std::string& val) { custom_default_player_name_ = val; } + auto allow_input_in_attract_mode() const { + return allow_input_in_attract_mode_; + } + + void set_allow_input_in_attract_mode(bool allow) { + allow_input_in_attract_mode_ = allow; + } + private: Object::Ref delegate_; - // note: this is in base-net-time - millisecs_t last_input_time_millisecs_{}; + millisecs_t last_active_time_millisecs_{}; int index_{-1}; // Our overall device index. int number_{-1}; // Our type-specific number. + bool allow_input_in_attract_mode_{}; std::string custom_default_player_name_; diff --git a/src/ballistica/base/input/device/joystick_input.cc b/src/ballistica/base/input/device/joystick_input.cc index b9dc6e3f..08f7386c 100644 --- a/src/ballistica/base/input/device/joystick_input.cc +++ b/src/ballistica/base/input/device/joystick_input.cc @@ -44,10 +44,6 @@ JoystickInput::JoystickInput(int sdl_joystick_id, analog_calibration_val = 0.6f; } - if (custom_device_name == "TestInput") { - is_test_input_ = true; - } - sdl_joystick_id_ = sdl_joystick_id; // Non-negative values here mean its an SDL joystick. @@ -799,7 +795,9 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) { // Anything that would go to ui also counts to mark us as 'recently-used'. if (would_go_to_ui) { - UpdateLastInputTime(); + if (!(allow_input_in_attract_mode() && g_base->input->attract_mode())) { + UpdateLastActiveTime(); + } } if (would_go_to_ui && g_base->ui->GetWidgetForInput(this)) { diff --git a/src/ballistica/base/input/device/joystick_input.h b/src/ballistica/base/input/device/joystick_input.h index e7cd79b7..e2238bb2 100644 --- a/src/ballistica/base/input/device/joystick_input.h +++ b/src/ballistica/base/input/device/joystick_input.h @@ -59,6 +59,8 @@ class JoystickInput : public InputDevice { auto IsUIOnly() -> bool override { return ui_only_; } + void set_is_test_input(bool val) { is_test_input_ = val; } + auto IsTestInput() -> bool override { return is_test_input_; } auto IsRemoteApp() -> bool override { return is_remote_app_; } auto IsMFiController() -> bool override { return is_mfi_controller_; } diff --git a/src/ballistica/base/input/device/test_input.cc b/src/ballistica/base/input/device/test_input.cc index b7491c2e..39d1745c 100644 --- a/src/ballistica/base/input/device/test_input.cc +++ b/src/ballistica/base/input/device/test_input.cc @@ -4,16 +4,24 @@ #include "ballistica/base/input/device/joystick_input.h" #include "ballistica/base/input/input.h" +#include "ballistica/base/ui/ui.h" #include "ballistica/core/platform/support/min_sdl.h" #include "ballistica/shared/math/random.h" namespace ballistica::base { TestInput::TestInput() { + // In attract-mode (pretty demos) we want this to look more like + // real people connecting to the game, so just say 'Controller'. + const char* device_name = + g_base->input->attract_mode() ? "Controller" : "TestInput"; + joystick_ = Object::NewDeferred(-1, // not an sdl joystick - "TestInput", // device name + device_name, // device name false, // allow configuring? false); // calibrate?; + joystick_->set_allow_input_in_attract_mode(true); + joystick_->set_is_test_input(true); g_base->input->PushAddInputDeviceCall(joystick_, true); } @@ -56,6 +64,11 @@ void TestInput::Process(millisecs_t time) { return; } + // Do nothing while any UI is up. + if (g_base->ui->MainMenuVisible()) { + return; + } + float r = RandomFloat(); SDL_Event e; diff --git a/src/ballistica/base/input/input.cc b/src/ballistica/base/input/input.cc index 7718bc90..b754c0a9 100644 --- a/src/ballistica/base/input/input.cc +++ b/src/ballistica/base/input/input.cc @@ -170,9 +170,6 @@ void Input::AnnounceConnects_() { } else { // If there's been several connected, just give a number. if (newly_connected_controllers_.size() > 1) { - for (auto&& s : newly_connected_controllers_) { - Log(LogLevel::kInfo, "GOT CONTROLLER " + s); - } std::string s = g_base->assets->GetResourceString("controllersConnectedText"); Utils::StringReplaceOne( @@ -392,9 +389,9 @@ void Input::UpdateInputDeviceCounts_() { // just due to those) if (input_device.Exists() && ((*input_device).IsTouchScreen() || (*input_device).IsKeyboard() - || ((*input_device).last_input_time_millisecs() != 0 + || ((*input_device).last_active_time_millisecs() != 0 && current_time_millisecs - - (*input_device).last_input_time_millisecs() + - (*input_device).last_active_time_millisecs() < 60000))) { total++; if (!(*input_device).IsTouchScreen()) { @@ -441,9 +438,9 @@ auto Input::GetLocalActiveInputDeviceCount() -> int { if (input_device.Exists() && !input_device->IsKeyboard() && !input_device->IsTouchScreen() && !input_device->IsUIOnly() && input_device->IsLocal() - && (input_device->last_input_time_millisecs() != 0 + && (input_device->last_active_time_millisecs() != 0 && current_time_millisecs - - input_device->last_input_time_millisecs() + - input_device->last_active_time_millisecs() < 60000)) { count++; } @@ -639,8 +636,9 @@ void Input::UnlockAllInput(bool permanent, const std::string& label) { permanent ? "permanent unlock: " : "temp unlock: " + label + " time " + std::to_string(g_core->GetAppTimeMillisecs())); - while (recent_input_locks_unlocks_.size() > 10) + while (recent_input_locks_unlocks_.size() > 10) { recent_input_locks_unlocks_.pop_front(); + } if (permanent) { input_lock_count_permanent_--; @@ -722,15 +720,15 @@ void Input::PrintLockLabels_() { void Input::PushTextInputEvent(const std::string& text) { assert(g_base->logic->event_loop()); g_base->logic->event_loop()->PushCall([this, text] { + // Mark as active even if input is locked. MarkInputActive(); - // If the app doesn't want direct text input right now, ignore. - if (!g_base->app_adapter->HasDirectKeyboardInput()) { + if (IsInputLocked()) { return; } - // Ignore if input is locked. - if (IsInputLocked()) { + // If the app doesn't want direct text input right now, ignore. + if (!g_base->app_adapter->HasDirectKeyboardInput()) { return; } @@ -795,16 +793,14 @@ void Input::HandleJoystickEvent_(const SDL_Event& event, if (ShouldCompletelyIgnoreInputDevice(input_device)) { return; } - if (IsInputLocked()) { + + // Mark as active even if input is locked. + input_device->UpdateLastActiveTime(); + + if (IsInputLocked(input_device)) { return; } - // Make note that we're not idle. - MarkInputActive(); - - // And that this particular device isn't idle either. - input_device->UpdateLastInputTime(); - // If someone is capturing these events, give them a crack at it. if (joystick_input_capture_) { if (joystick_input_capture_(event, input_device)) { @@ -905,9 +901,9 @@ void Input::HandleKeyReleaseSimple_(int keycode) { void Input::HandleKeyPress_(const SDL_Keysym& keysym) { assert(g_base->InLogicThread()); + // Mark as active even if input is locked. MarkInputActive(); - // Ignore all key presses if input is locked. if (IsInputLocked()) { return; } @@ -1180,8 +1176,9 @@ void Input::PushMouseScrollEvent(const Vector2f& amount) { void Input::HandleMouseScroll_(const Vector2f& amount) { assert(g_base->InLogicThread()); - // If input is locked, allow it to mark us active but nothing more. + // Mark as active even if input is locked. MarkInputActive(); + if (IsInputLocked()) { return; } @@ -1217,8 +1214,9 @@ void Input::PushSmoothMouseScrollEvent(const Vector2f& velocity, void Input::HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum) { assert(g_base->InLogicThread()); - // If input is locked, allow it to mark us active but nothing more. + // Mark as active even if input is locked. MarkInputActive(); + if (IsInputLocked()) { return; } @@ -1259,6 +1257,7 @@ void Input::HandleMouseMotion_(const Vector2f& position) { assert(g_base); assert(g_base->InLogicThread()); + // Mark as active even if input is locked. MarkInputActive(); if (IsInputLocked()) { @@ -1309,6 +1308,7 @@ void Input::HandleMouseDown_(int button, const Vector2f& position) { assert(g_base); assert(g_base->InLogicThread()); + // Mark as active even if input is locked. MarkInputActive(); if (IsInputLocked()) { @@ -1416,12 +1416,13 @@ void Input::HandleTouchEvent_(const TouchEvent& e) { assert(g_base->InLogicThread()); assert(g_base->graphics); + // Mark as active even if input is locked. + MarkInputActive(); + if (IsInputLocked()) { return; } - MarkInputActive(); - if (g_buildconfig.ostype_ios_tvos()) { printf("FIXME: update touch handling\n"); } @@ -1564,4 +1565,11 @@ void Input::LsInputDevices() { Log(LogLevel::kInfo, out); } +auto Input::ShouldAllowInputInAttractMode_(InputDevice* device) const -> bool { + if (device == nullptr) { + return false; + } + return device->allow_input_in_attract_mode(); +} + } // namespace ballistica::base diff --git a/src/ballistica/base/input/input.h b/src/ballistica/base/input/input.h index a20a619d..5c23bf33 100644 --- a/src/ballistica/base/input/input.h +++ b/src/ballistica/base/input/input.h @@ -10,6 +10,7 @@ #include #include "ballistica/base/base.h" +#include "ballistica/shared/foundation/macros.h" #include "ballistica/shared/foundation/object.h" #include "ballistica/shared/foundation/types.h" @@ -62,7 +63,14 @@ class Input { void Reset(); void LockAllInput(bool permanent, const std::string& label); void UnlockAllInput(bool permanent, const std::string& label); - auto IsInputLocked() const -> bool { + auto IsInputLocked(InputDevice* device = nullptr) const -> bool { + // Special case; in attract-mode we ignore all input except our + // dummy controllers. + if (attract_mode_) { + if (!ShouldAllowInputInAttractMode_(device)) { + return true; + } + } return input_lock_count_temp_ > 0 || input_lock_count_permanent_ > 0; } auto cursor_pos_x() const -> float { return cursor_pos_x_; } @@ -92,12 +100,7 @@ class Input { } void Draw(FrameDef* frame_def); - // Get the total idle time for the system. - // FIXME - should better coordinate this with InputDevice::getLastUsedTime(). - // auto GetIdleTime() const -> millisecs_t; - - // Should be called whenever user-input of some form comes through. - // void ResetIdleTime() { last_input_time_ = GetAppTimeMillisecs(); } + /// Should be called whenever user-input of some form comes through. auto MarkInputActive() { input_active_ = true; } // returns true if more than one non-keyboard device has been active recently @@ -153,7 +156,11 @@ class Input { void ReleaseJoystickInput(); void RebuildInputDeviceDelegates(); + auto attract_mode() const { return attract_mode_; } + void set_attract_mode(bool val) { attract_mode_ = val; } + private: + auto ShouldAllowInputInAttractMode_(InputDevice* device) const -> bool; void UpdateInputDeviceCounts_(); auto GetNewNumberedIdentifier_(const std::string& name, const std::string& identifier) -> int; @@ -183,8 +190,9 @@ class Input { int max_controller_count_so_far_{}; int local_active_input_device_count_{}; int mouse_move_count_{}; - int input_lock_count_temp_{}; - int input_lock_count_permanent_{}; + int8_t input_lock_count_temp_{}; + int8_t input_lock_count_permanent_{}; + bool attract_mode_{}; bool input_active_{}; bool have_button_using_inputs_{}; bool have_start_activated_default_button_inputs_{}; diff --git a/src/ballistica/base/python/methods/python_methods_misc.cc b/src/ballistica/base/python/methods/python_methods_misc.cc index 7b324b24..c1ba7116 100644 --- a/src/ballistica/base/python/methods/python_methods_misc.cc +++ b/src/ballistica/base/python/methods/python_methods_misc.cc @@ -1805,6 +1805,26 @@ static PyMethodDef PyOpenFileExternallyDef = { "Open the provided file in the default external app.", }; +// --------------------------- get_input_idle_time ----------------------------- + +static auto PyGetInputIdleTime(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + + BA_PRECONDITION(g_base->InLogicThread()); + return PyFloat_FromDouble(0.001 * g_base->input->input_idle_time()); + + BA_PYTHON_CATCH; +} + +static PyMethodDef PyGetInputIdleTimeDef = { + "get_input_idle_time", // name + (PyCFunction)PyGetInputIdleTime, // method + METH_NOARGS, // flags + + "get_input_idle_time() -> float\n" + "\n" + "Return seconds since any local input occurred (touch, keypress, etc.).", +}; // ----------------------------------------------------------------------------- auto PythonMethodsMisc::GetMethods() -> std::vector { @@ -1873,6 +1893,7 @@ auto PythonMethodsMisc::GetMethods() -> std::vector { PyNativeReviewRequestDef, PyTempTestingDef, PyOpenFileExternallyDef, + PyGetInputIdleTimeDef, }; } diff --git a/src/ballistica/base/support/app_config.cc b/src/ballistica/base/support/app_config.cc index 7fdc7957..ca21e754 100644 --- a/src/ballistica/base/support/app_config.cc +++ b/src/ballistica/base/support/app_config.cc @@ -169,7 +169,6 @@ void AppConfig::CompleteMap(const T& entry_map) { void AppConfig::SetupEntries() { // Register all our typed entries. - float_entries_[FloatID::kScreenGamma] = FloatEntry("Screen Gamma", 1.0F); float_entries_[FloatID::kScreenPixelScale] = FloatEntry("Screen Pixel Scale", 1.0F); float_entries_[FloatID::kTouchControlsScale] = @@ -236,6 +235,8 @@ void AppConfig::SetupEntries() { BoolEntry("Disable Camera Shake", false); bool_entries_[BoolID::kDisableCameraGyro] = BoolEntry("Disable Camera Gyro", false); + bool_entries_[BoolID::kShowDemosWhenIdle] = + BoolEntry("Show Demos When Idle", false); // Now add everything to our name map and make sure all is kosher. CompleteMap(float_entries_); diff --git a/src/ballistica/base/support/app_config.h b/src/ballistica/base/support/app_config.h index 9381c49e..f170c23b 100644 --- a/src/ballistica/base/support/app_config.h +++ b/src/ballistica/base/support/app_config.h @@ -9,9 +9,10 @@ #include #include -// FIXME: this system is old and dumb. -// Should come up with a better one using the meta system -// based on Python dataclassio types or whatnot. +// FIXME: this system is old and dumb. It was built to make C++ stuff +// type-safe but does not handle the Python side at all. We should come up +// with something Python-centric using dataclasses/etc. where a C++ +// component gets autogenerated via the meta system/etc. namespace ballistica::base { @@ -24,7 +25,6 @@ class AppConfig { // Our official config values: enum class FloatID { - kScreenGamma, kScreenPixelScale, kTouchControlsScale, kTouchControlsScaleMovement, @@ -74,6 +74,7 @@ class AppConfig { kEnableRemoteApp, kDisableCameraShake, kDisableCameraGyro, + kShowDemosWhenIdle, kLast // Sentinel. }; diff --git a/src/ballistica/classic/python/methods/python_methods_classic.cc b/src/ballistica/classic/python/methods/python_methods_classic.cc index 52780c22..5ebda885 100644 --- a/src/ballistica/classic/python/methods/python_methods_classic.cc +++ b/src/ballistica/classic/python/methods/python_methods_classic.cc @@ -4,6 +4,7 @@ #include "ballistica/base/graphics/graphics.h" #include "ballistica/base/graphics/support/camera.h" +#include "ballistica/base/input/input.h" #include "ballistica/base/logic/logic.h" #include "ballistica/classic/support/stress_test.h" #include "ballistica/scene_v1/support/scene_v1_app_mode.h" @@ -154,11 +155,13 @@ static auto PySetStressTesting(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; int enable; int player_count; - if (!PyArg_ParseTuple(args, "pi", &enable, &player_count)) { + int attract_mode; + if (!PyArg_ParseTuple(args, "pip", &enable, &player_count, &attract_mode)) { return nullptr; } - g_base->logic->event_loop()->PushCall([enable, player_count] { - g_classic->stress_test()->Set(enable, player_count); + g_base->logic->event_loop()->PushCall([enable, player_count, attract_mode] { + g_classic->stress_test()->Set(enable, player_count, attract_mode); + g_base->input->set_attract_mode(enable && attract_mode); }); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -169,7 +172,9 @@ static PyMethodDef PySetStressTestingDef = { PySetStressTesting, // method METH_VARARGS, // flags - "set_stress_testing(testing: bool, player_count: int) -> None\n" + "set_stress_testing(testing: bool,\n" + " player_count: int,\n" + " attract_mode: bool) -> None\n" "\n" "(internal)", }; diff --git a/src/ballistica/classic/support/stress_test.cc b/src/ballistica/classic/support/stress_test.cc index 0debd86e..ad6d749a 100644 --- a/src/ballistica/classic/support/stress_test.cc +++ b/src/ballistica/classic/support/stress_test.cc @@ -11,16 +11,16 @@ namespace ballistica::classic { -void StressTest::Set(bool enable, int player_count) { +void StressTest::Set(bool enable, int player_count, bool attract_mode) { assert(g_base->InLogicThread()); bool was_stress_testing = stress_testing_; stress_testing_ = enable; stress_test_player_count_ = player_count; + attract_mode_ = attract_mode; // If we're turning on, reset our intervals and things. if (!was_stress_testing && stress_testing_) { // So our first sample is 1 interval from now. - // last_stress_test_update_time_ = g_core->GetAppTimeMillisecs(); // Reset our frames-rendered tally. if (g_base && g_base->graphics_server @@ -71,9 +71,11 @@ void StressTest::ProcessInputs(int player_count) { test_inputs_.push_back(new base::TestInput()); } - // Every so often lets kill the oldest one off. + // Every so often lets kill the oldest one off (less often in attract-mode + // though). + int odds = attract_mode_ ? 10000 : 2000; if (explicit_bool(true)) { - if (test_inputs_.size() > 0 && (rand() % 2000 < 3)) { // NOLINT + if (test_inputs_.size() > 0 && (rand() % odds < 3)) { // NOLINT stress_test_last_leave_time_ = time; // Usually do oldest; sometimes newest. diff --git a/src/ballistica/classic/support/stress_test.h b/src/ballistica/classic/support/stress_test.h index 6c848208..7c17c805 100644 --- a/src/ballistica/classic/support/stress_test.h +++ b/src/ballistica/classic/support/stress_test.h @@ -11,7 +11,7 @@ namespace ballistica::classic { class StressTest { public: - void Set(bool enable, int player_count); + void Set(bool enable, int player_count, bool attract_mode); void Update(); private: @@ -23,8 +23,7 @@ class StressTest { int stress_test_player_count_{8}; int last_total_frames_rendered_{}; bool stress_testing_{}; - // millisecs_t last_stress_test_update_time_{}; - // FILE* stress_test_stats_file_{}; + bool attract_mode_{}; Object::Ref update_timer_{}; }; diff --git a/src/ballistica/scene_v1/python/class/python_class_input_device.cc b/src/ballistica/scene_v1/python/class/python_class_input_device.cc index 515f2c35..4d54399b 100644 --- a/src/ballistica/scene_v1/python/class/python_class_input_device.cc +++ b/src/ballistica/scene_v1/python/class/python_class_input_device.cc @@ -73,6 +73,9 @@ void PythonClassInputDevice::SetupType(PyTypeObject* cls) { " is_remote_client (bool):\n" " Whether this input-device represents a remotely-connected\n" " client.\n" + "\n" + " is_test_input (bool):\n" + " Whether this input-device is a dummy device for testing.\n" "\n"; cls->tp_new = tp_new; @@ -265,6 +268,16 @@ auto PythonClassInputDevice::tp_getattro(PythonClassInputDevice* self, } else { Py_RETURN_FALSE; } + } else if (!strcmp(s, "is_test_input")) { + auto* delegate = self->input_device_delegate_->Get(); + if (!delegate) { + throw Exception(PyExcType::kInputDeviceNotFound); + } + if (delegate->input_device().IsTestInput()) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } } // Fall back to generic behavior. diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index ffd8e494..7cd26182 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 = 21747; +const int kEngineBuildNumber = 21751; const char* kEngineVersion = "1.7.33"; const int kEngineApiVersion = 8;