added show-demos-when-idle option

This commit is contained in:
Eric 2024-01-05 13:49:44 -08:00
parent 87bf4e3435
commit 6f2382fe69
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
28 changed files with 339 additions and 151 deletions

66
.efrocachemap generated
View File

@ -421,7 +421,7 @@
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26", "build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55", "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/arabic.json": "0db32e21b6d5337ccca478381744aa88",
"build/assets/ba_data/data/languages/belarussian.json": "a112dfca3e188387516788bd8229c5b0", "build/assets/ba_data/data/languages/belarussian.json": "a112dfca3e188387516788bd8229c5b0",
"build/assets/ba_data/data/languages/chinese.json": "93f3ca9f90d86dc7c8d0923f5f11ef46", "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/czech.json": "c9d518a324870066b987b8f412881dd3",
"build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e", "build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e",
"build/assets/ba_data/data/languages/dutch.json": "5cbf1a68a9d93dee00dbc27f834d878a", "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/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/french.json": "8bc35eb4b20a0b30c3348bcc9a3844a6",
"build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad", "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/greek.json": "287c0ec437b38772284ef9d3e4fb2fc3",
"build/assets/ba_data/data/languages/hindi.json": "8848f6b0caec0fcf9d85bc6e683809ec", "build/assets/ba_data/data/languages/hindi.json": "8848f6b0caec0fcf9d85bc6e683809ec",
"build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e", "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/tamil.json": "b4de1a2851afe4869c82e9acd94cd89c",
"build/assets/ba_data/data/languages/thai.json": "77755219bbf5fb7eea0d6b226684f403", "build/assets/ba_data/data/languages/thai.json": "77755219bbf5fb7eea0d6b226684f403",
"build/assets/ba_data/data/languages/turkish.json": "72fac3a6fd13ebb74fb4d68b9323e7d4", "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/venetian.json": "8e9714d98a85e428ce3543fc49188a46",
"build/assets/ba_data/data/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba", "build/assets/ba_data/data/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba",
"build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054", "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/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "6d1c198240f58211b14534263de11034", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "8d62386f109621ee08bf6bc699e560b4",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "0e29ddb400da245f7425e2779278b936", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "1ae0c73018caf6d563f7a2beed5630df",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "887c68a7be4c8d25c6eae21feb2fd39a", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cec12563653f88526ab0cdd56c35a659",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "99a237557104d145f061b674e158539f", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "c406758148e6a19a8ba776de09c9cf31",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "c813eb17692868fc54741e962d8ecca6", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "ec6b2f954c73f0a44d25eb12b76ae3df",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "67b39d7c54a801c2b65bd8beb55771a2", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "190572e9a537ab1cce44a3a307cfb65f",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "20af8a239c97cbb8fe876c06d57c3f1f", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "d1dd35ec23c4e42d7bcd05406375b3f3",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "60d09bd4c59e27f2f3c8b6331b650ee1", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "790f5c6df9bc038b9b0c1561e30cc17f",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "84733128a50a14b046306419419862d4", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "946739e98d88bbcdb615a97f66a3a9f5",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "b81062addf907d312a9409660733205c", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "e625d8908634593ca1917ae7a42846ea",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "2770d31f91042c005cb0fc5cb368112c", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "48b463fbb8ad28f4c9487feb5383a1a7",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "316e8424de85400d5b84c84395689776", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "a8fd4ed9a9db5878d21eee6d4d8035ce",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "15bd20491afa0d90e3cea147d04e25a6", "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "a40f333afd411df2323b236f7931c72f",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "4697883ea3e8f9e8d215c9a82396c8a4", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "8c1b885d6bcae9c43b8ca45c3538d9f7",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "cd24a57ef4815f02f3c4f054fc9ae878", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "10e25021c620ae9df514e952d6f7a14c",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "13b8e37ea683de88bce49e06a6cba24d", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "5fdbf8e2c9619f093e4c0505619178f2",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "50218a71f166a51c6bccec5f8a946835", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "74527ceb879d581e162eea3052b74b48",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "e20e058060ef6acc1e1f4cbccd69a7ce", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "4cbf3fa8efd3ac8051fdf7776684e80c",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "13a90cb0c4a15daa64c8d883afd895e9", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "0dc81a9db0d827fd803ba3eac90c4453",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "6af4eddfe3ed8a825a5ebd7702536fd7", "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/debug/libballisticaplus.a": "db535f0ca1e01af825f75f204fbc8928",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "97d51afca996ae15b61fd9f409a00459", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "97d51afca996ae15b61fd9f409a00459",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "db535f0ca1e01af825f75f204fbc8928", "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_gui/release/libballisticaplus.a": "452623f0495dd4375e5b5d9b80d643d5",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "ca49b32ed573feea11613d62cd89840c", "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/mac_x86_64_server/release/libballisticaplus.a": "452623f0495dd4375e5b5d9b80d643d5",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "3b19ca383e1a88bd8a0599181da738ba", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "8d7a27873efa3595c3209a19de05ff9d",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "2b9ee84d34b41dd7d92df7c7dece5ecc", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "54322d4265553cc935840cb2b966c18c",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "78fc011efdd0d3c5ad91ba5cc7e25b36", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "50254a1075a671d50fbb1a86aac86cb9",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "1c60cf6ee1b3b7f1c4ca10f18ad55c0b", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "fe1eb9d9b16128058c38c46646e37a79",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a9d3d2dd577409f19129a2a14f03a871", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "b7095b4bdeba71cb04dba2765e8d497c",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "c8cda906610d28b03a5d0abac69f752e", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "06303398d77e04dc56badaf9f8480132",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "951e19df7d1f5d9befd0b1fe5e4f6390", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "61f5d7b685ab8948995c4eba78b2bf3b",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "b75ac3bc471e3b6121d27a83b07573da", "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/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101", "src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f", "src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",

View File

@ -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) ### 1.7.32 (build 21741, api 8, 2023-12-20)
- Fixed a screen message that no one will ever see (Thanks vishal332008?...) - 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!) - Plugins window now displays 'No Plugins Installed' when no plugins are present (Thanks vishal332008!)

View File

@ -48,6 +48,7 @@ from _babase import (
fatal_error, fatal_error,
get_display_resolution, get_display_resolution,
get_immediate_return_code, get_immediate_return_code,
get_input_idle_time,
get_low_level_config_value, get_low_level_config_value,
get_max_graphics_quality, get_max_graphics_quality,
get_replays_dir, get_replays_dir,
@ -236,6 +237,7 @@ __all__ = [
'garbage_collect', 'garbage_collect',
'get_display_resolution', 'get_display_resolution',
'get_immediate_return_code', 'get_immediate_return_code',
'get_input_idle_time',
'get_ip_address_type', 'get_ip_address_type',
'get_low_level_config_value', 'get_low_level_config_value',
'get_max_graphics_quality', 'get_max_graphics_quality',

View File

@ -4,6 +4,7 @@
from __future__ import annotations from __future__ import annotations
import random import random
from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import babase import babase
@ -48,64 +49,74 @@ def run_cpu_benchmark() -> None:
bascenev1.new_host_session(BenchmarkSession, benchmark_type='cpu') 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( def run_stress_test(
playlist_type: str = 'Random', playlist_type: str = 'Random',
playlist_name: str = '__default__', playlist_name: str = '__default__',
player_count: int = 8, player_count: int = 8,
round_duration: int = 30, round_duration: int = 30,
attract_mode: bool = False,
) -> None: ) -> None:
"""Run a stress test.""" """Run a stress test."""
babase.screenmessage(
"Beginning stress test.. use 'End Test' to stop testing.",
color=(1, 1, 0),
)
with babase.ContextRef.empty(): with babase.ContextRef.empty():
start_stress_test( if not attract_mode:
{ babase.screenmessage(
'playlist_type': playlist_type, "Beginning stress test.. use 'End Test' to stop testing.",
'playlist_name': playlist_name, color=(1, 1, 0),
'player_count': player_count, )
'round_duration': round_duration, _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: def stop_stress_test() -> None:
"""End a running stress test.""" """End a running stress test."""
_baclassic.set_stress_testing(False, 0)
assert babase.app.classic is not None assert babase.app.classic is not None
try:
if babase.app.classic.stress_test_reset_timer is not None: _baclassic.set_stress_testing(False, 0, False)
babase.screenmessage('Ending stress test...', color=(1, 1, 0)) babase.app.classic.stress_test_update_timer = None
except Exception: babase.app.classic.stress_test_update_timer_2 = None
pass
babase.app.classic.stress_test_reset_timer = None
def start_stress_test(args: dict[str, Any]) -> None: def _start_stress_test(args: _StressTestArgs) -> None:
"""(internal)""" """(internal)"""
from bascenev1 import DualTeamSession, FreeForAllSession from bascenev1 import DualTeamSession, FreeForAllSession
assert babase.app.classic is not None assert babase.app.classic is not None
appconfig = babase.app.config appconfig = babase.app.config
playlist_type = args['playlist_type'] playlist_type = args.playlist_type
if playlist_type == 'Random': if playlist_type == 'Random':
if random.random() < 0.5: if random.random() < 0.5:
playlist_type = 'Teams' playlist_type = 'Teams'
else: else:
playlist_type = 'Free-For-All' playlist_type = 'Free-For-All'
babase.screenmessage( if not args.attract_mode:
'Running Stress Test (listType="' babase.screenmessage(
+ playlist_type 'Running Stress Test (listType="'
+ '", listName="' + playlist_type
+ args['playlist_name'] + '", listName="'
+ '")...' + args.playlist_name
) + '")...'
)
if playlist_type == 'Teams': 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 appconfig['Team Tournament Playlist Randomize'] = 1
babase.apptimer( babase.apptimer(
1.0, 1.0,
@ -115,7 +126,7 @@ def start_stress_test(args: dict[str, Any]) -> None:
), ),
) )
else: 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 appconfig['Free-for-All Playlist Randomize'] = 1
babase.apptimer( babase.apptimer(
1.0, 1.0,
@ -124,19 +135,38 @@ def start_stress_test(args: dict[str, Any]) -> None:
babase.Call(bascenev1.new_host_session, FreeForAllSession), babase.Call(bascenev1.new_host_session, FreeForAllSession),
), ),
) )
_baclassic.set_stress_testing(True, args['player_count']) _baclassic.set_stress_testing(True, args.player_count, args.attract_mode)
babase.app.classic.stress_test_reset_timer = babase.AppTimer( babase.app.classic.stress_test_update_timer = babase.AppTimer(
args['round_duration'], babase.Call(_reset_stress_test, args) 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: def _update_attract_mode_test(args: _StressTestArgs) -> None:
_baclassic.set_stress_testing(False, args['player_count']) if babase.get_input_idle_time() < 5.0:
babase.screenmessage('Resetting stress test...') _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() session = bascenev1.get_foreground_host_session()
assert session is not None assert session is not None
session.end() 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: def run_gpu_benchmark() -> None:

View File

@ -69,7 +69,8 @@ class ClassicSubsystem(babase.AppSubsystem):
# Misc. # Misc.
self.tips: list[str] = [] 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.value_test_defaults: dict = {}
self.special_offer: dict | None = None self.special_offer: dict | None = None
self.ping_thread_count = 0 self.ping_thread_count = 0
@ -555,11 +556,18 @@ class ClassicSubsystem(babase.AppSubsystem):
playlist_name: str = '__default__', playlist_name: str = '__default__',
player_count: int = 8, player_count: int = 8,
round_duration: int = 30, round_duration: int = 30,
attract_mode: bool = False,
) -> None: ) -> None:
"""Run a stress test.""" """Run a stress test."""
from baclassic._benchmark import run_stress_test as run 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( def get_input_device_mapped_value(
self, device: bascenev1.InputDevice, name: str self, device: bascenev1.InputDevice, name: str

View File

@ -52,7 +52,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be # Build number and version of the ballistica binary we expect to be
# using. # using.
TARGET_BALLISTICA_BUILD = 21747 TARGET_BALLISTICA_BUILD = 21751
TARGET_BALLISTICA_VERSION = '1.7.33' TARGET_BALLISTICA_VERSION = '1.7.33'

View File

@ -441,7 +441,7 @@ class Chooser:
# list might have changed. # list might have changed.
input_device = self._sessionplayer.inputdevice input_device = self._sessionplayer.inputdevice
is_remote = input_device.is_remote_client 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. # Pull this player's list of unlocked characters.
if is_remote: if is_remote:

View File

@ -70,6 +70,10 @@ class MultiTeamSession(Session):
show_tutorial = cfg.get('Show Tutorial', True) 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 self._tutorial_activity_instance: bascenev1.Activity | None
if show_tutorial: if show_tutorial:
from bascenev1lib.tutorial import TutorialActivity from bascenev1lib.tutorial import TutorialActivity

View File

@ -253,7 +253,7 @@ class Session:
# Limit player counts *unless* we're in a stress test. # Limit player counts *unless* we're in a stress test.
if ( if (
babase.app.classic is not None 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: if len(self.sessionplayers) >= self.max_players:
# Print a rejection message *only* to the client trying to # Print a rejection message *only* to the client trying to

View File

@ -42,6 +42,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
self._language: str | None = None self._language: str | None = None
self._update_timer: bs.Timer | None = None self._update_timer: bs.Timer | None = None
self._news: NewsDisplay | None = None self._news: NewsDisplay | None = None
self._attract_mode_timer: bs.Timer | None = None
def on_transition_in(self) -> None: def on_transition_in(self) -> None:
# pylint: disable=too-many-locals # 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: if not (env.demo or env.arcade) and not app.ui_v1.use_toolbars:
self._news = NewsDisplay(self) 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. # Bring up the last place we were, or start at the main menu otherwise.
with bs.ContextRef.empty(): with bs.ContextRef.empty():
from bauiv1lib import specialoffer from bauiv1lib import specialoffer
@ -387,7 +392,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
bs.app.ui_v1.set_main_menu_window( bs.app.ui_v1.set_main_menu_window(
MainMenuWindow(transition=None).get_root_widget(), MainMenuWindow(transition=None).get_root_widget(),
from_window=None, from_window=False, # Disable check here.
) )
# attempt to show any pending offers immediately. # 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, specialoffer.show_offer)
bui.apptimer(2.0, try_again) bui.apptimer(2.0, try_again)
app.classic.main_menu_did_initial_transition = True app.classic.main_menu_did_initial_transition = True
def _update(self) -> None: def _update(self) -> None:
@ -836,6 +842,26 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
bui.apptimer(0.5, _start_menu_music) 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: class NewsDisplay:
"""Wrangles news display.""" """Wrangles news display."""

View File

@ -47,6 +47,7 @@ from babase import (
do_once, do_once,
fade_screen, fade_screen,
get_display_resolution, get_display_resolution,
get_input_idle_time,
get_ip_address_type, get_ip_address_type,
get_low_level_config_value, get_low_level_config_value,
get_max_graphics_quality, get_max_graphics_quality,
@ -156,6 +157,7 @@ __all__ = [
'do_once', 'do_once',
'fade_screen', 'fade_screen',
'get_display_resolution', 'get_display_resolution',
'get_input_idle_time',
'get_ip_address_type', 'get_ip_address_type',
'get_low_level_config_value', 'get_low_level_config_value',
'get_max_graphics_quality', 'get_max_graphics_quality',

View File

@ -1110,7 +1110,7 @@ class MainMenuWindow(bui.Window):
session = bs.get_foreground_host_session() session = bs.get_foreground_host_session()
return getattr(session, 'benchmark_type', None) == 'cpu' or ( return getattr(session, 'benchmark_type', None) == 'cpu' or (
bui.app.classic is not None 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: def _confirm_end_game(self) -> None:

View File

@ -94,7 +94,7 @@ class AdvancedSettingsWindow(bui.Window):
self._scroll_width = self._width - (100 + 2 * x_inset) self._scroll_width = self._width - (100 + 2 * x_inset)
self._scroll_height = self._height - 115.0 self._scroll_height = self._height - 115.0
self._sub_width = self._scroll_width * 0.95 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: if self._show_always_use_internal_keyboard:
self._sub_height += 62 self._sub_height += 62
@ -489,6 +489,17 @@ class AdvancedSettingsWindow(bui.Window):
maxwidth=430, 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 v -= 42
self._disable_camera_shake_check_box = ConfigCheckBox( self._disable_camera_shake_check_box = ConfigCheckBox(
parent=self._subcontainer, parent=self._subcontainer,
@ -575,12 +586,18 @@ class AdvancedSettingsWindow(bui.Window):
up_widget=self._always_use_internal_keyboard_check_box.widget, up_widget=self._always_use_internal_keyboard_check_box.widget,
) )
else: else:
bui.widget( # ew.
edit=self._modding_guide_button, next_widget_up = (
up_widget=self._kick_idle_players_check_box.widget, 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( 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, down_widget=self._modding_guide_button,
) )
@ -803,6 +820,8 @@ class AdvancedSettingsWindow(bui.Window):
sel_name = 'Benchmarks' sel_name = 'Benchmarks'
elif sel == self._kick_idle_players_check_box.widget: elif sel == self._kick_idle_players_check_box.widget:
sel_name = 'KickIdlePlayers' 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: elif sel == self._show_game_ping_check_box.widget:
sel_name = 'ShowPing' sel_name = 'ShowPing'
elif sel == self._disable_camera_shake_check_box.widget: elif sel == self._disable_camera_shake_check_box.widget:
@ -870,6 +889,8 @@ class AdvancedSettingsWindow(bui.Window):
sel = self._benchmarks_button sel = self._benchmarks_button
elif sel_name == 'KickIdlePlayers': elif sel_name == 'KickIdlePlayers':
sel = self._kick_idle_players_check_box.widget 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': elif sel_name == 'ShowPing':
sel = self._show_game_ping_check_box.widget sel = self._show_game_ping_check_box.widget
elif sel_name == 'DisableCameraShake': elif sel_name == 'DisableCameraShake':

View File

@ -52,9 +52,9 @@ InputDevice::~InputDevice() { assert(g_base->InLogicThread()); }
// control something please. // control something please.
void InputDevice::RequestPlayer() { void InputDevice::RequestPlayer() {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
// Make note that we're being used in some way. // Make note that we're being used in some way.
last_input_time_millisecs_ = UpdateLastActiveTime();
static_cast<millisecs_t>(g_base->logic->display_time() * 1000.0);
delegate_->RequestPlayer(); delegate_->RequestPlayer();
} }
@ -69,11 +69,19 @@ auto InputDevice::AttachedToPlayer() const -> bool {
void InputDevice::DetachFromPlayer() { delegate_->DetachFromPlayer(); } void InputDevice::DetachFromPlayer() { delegate_->DetachFromPlayer(); }
void InputDevice::UpdateLastInputTime() { void InputDevice::UpdateLastActiveTime() {
// Keep our own individual time, and also let the overall input system // Special case: in attract-mode, prevent our virtual test devices from
// know something happened. // affecting input last-active times otherwise it'll kick us out of
last_input_time_millisecs_ = // 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<millisecs_t>(g_base->logic->display_time() * 1000.0); static_cast<millisecs_t>(g_base->logic->display_time() * 1000.0);
// Mark input in general as active also.
g_base->input->MarkInputActive(); g_base->input->MarkInputActive();
} }
@ -81,7 +89,7 @@ void InputDevice::InputCommand(InputType type, float value) {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
// Make note that we're being used in some way. // Make note that we're being used in some way.
UpdateLastInputTime(); UpdateLastActiveTime();
delegate_->InputCommand(type, value); delegate_->InputCommand(type, value);
} }

View File

@ -93,8 +93,8 @@ class InputDevice : public Object {
/// that button activates default widgets (will cause a start icon to show up /// that button activates default widgets (will cause a start icon to show up
/// on them). /// on them).
virtual auto start_button_activates_default_widget() -> bool { return false; } virtual auto start_button_activates_default_widget() -> bool { return false; }
auto last_input_time_millisecs() const -> millisecs_t { auto last_active_time_millisecs() const -> millisecs_t {
return last_input_time_millisecs_; return last_active_time_millisecs_;
} }
virtual auto ShouldBeHiddenFromUser() -> bool; 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. /// been added to the input-device list, have a valid ID, name, etc.
virtual void OnAdded() {} virtual void OnAdded() {}
void UpdateLastInputTime(); void UpdateLastActiveTime();
auto delegate() -> InputDeviceDelegate& { auto delegate() -> InputDeviceDelegate& {
// TEMP - Tracking down a crash in the wild. // TEMP - Tracking down a crash in the wild.
@ -136,18 +136,27 @@ class InputDevice : public Object {
auto custom_default_player_name() const -> std::string { auto custom_default_player_name() const -> std::string {
return custom_default_player_name_; return custom_default_player_name_;
} }
void set_custom_default_player_name(const std::string& val) { void set_custom_default_player_name(const std::string& val) {
custom_default_player_name_ = 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: private:
Object::Ref<InputDeviceDelegate> delegate_; Object::Ref<InputDeviceDelegate> delegate_;
// note: this is in base-net-time millisecs_t last_active_time_millisecs_{};
millisecs_t last_input_time_millisecs_{};
int index_{-1}; // Our overall device index. int index_{-1}; // Our overall device index.
int number_{-1}; // Our type-specific number. int number_{-1}; // Our type-specific number.
bool allow_input_in_attract_mode_{};
std::string custom_default_player_name_; std::string custom_default_player_name_;

View File

@ -44,10 +44,6 @@ JoystickInput::JoystickInput(int sdl_joystick_id,
analog_calibration_val = 0.6f; analog_calibration_val = 0.6f;
} }
if (custom_device_name == "TestInput") {
is_test_input_ = true;
}
sdl_joystick_id_ = sdl_joystick_id; sdl_joystick_id_ = sdl_joystick_id;
// Non-negative values here mean its an SDL joystick. // 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'. // Anything that would go to ui also counts to mark us as 'recently-used'.
if (would_go_to_ui) { 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)) { if (would_go_to_ui && g_base->ui->GetWidgetForInput(this)) {

View File

@ -59,6 +59,8 @@ class JoystickInput : public InputDevice {
auto IsUIOnly() -> bool override { return ui_only_; } 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 IsTestInput() -> bool override { return is_test_input_; }
auto IsRemoteApp() -> bool override { return is_remote_app_; } auto IsRemoteApp() -> bool override { return is_remote_app_; }
auto IsMFiController() -> bool override { return is_mfi_controller_; } auto IsMFiController() -> bool override { return is_mfi_controller_; }

View File

@ -4,16 +4,24 @@
#include "ballistica/base/input/device/joystick_input.h" #include "ballistica/base/input/device/joystick_input.h"
#include "ballistica/base/input/input.h" #include "ballistica/base/input/input.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/core/platform/support/min_sdl.h" #include "ballistica/core/platform/support/min_sdl.h"
#include "ballistica/shared/math/random.h" #include "ballistica/shared/math/random.h"
namespace ballistica::base { namespace ballistica::base {
TestInput::TestInput() { 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<JoystickInput>(-1, // not an sdl joystick joystick_ = Object::NewDeferred<JoystickInput>(-1, // not an sdl joystick
"TestInput", // device name device_name, // device name
false, // allow configuring? false, // allow configuring?
false); // calibrate?; false); // calibrate?;
joystick_->set_allow_input_in_attract_mode(true);
joystick_->set_is_test_input(true);
g_base->input->PushAddInputDeviceCall(joystick_, true); g_base->input->PushAddInputDeviceCall(joystick_, true);
} }
@ -56,6 +64,11 @@ void TestInput::Process(millisecs_t time) {
return; return;
} }
// Do nothing while any UI is up.
if (g_base->ui->MainMenuVisible()) {
return;
}
float r = RandomFloat(); float r = RandomFloat();
SDL_Event e; SDL_Event e;

View File

@ -170,9 +170,6 @@ void Input::AnnounceConnects_() {
} else { } else {
// If there's been several connected, just give a number. // If there's been several connected, just give a number.
if (newly_connected_controllers_.size() > 1) { if (newly_connected_controllers_.size() > 1) {
for (auto&& s : newly_connected_controllers_) {
Log(LogLevel::kInfo, "GOT CONTROLLER " + s);
}
std::string s = std::string s =
g_base->assets->GetResourceString("controllersConnectedText"); g_base->assets->GetResourceString("controllersConnectedText");
Utils::StringReplaceOne( Utils::StringReplaceOne(
@ -392,9 +389,9 @@ void Input::UpdateInputDeviceCounts_() {
// just due to those) // just due to those)
if (input_device.Exists() if (input_device.Exists()
&& ((*input_device).IsTouchScreen() || (*input_device).IsKeyboard() && ((*input_device).IsTouchScreen() || (*input_device).IsKeyboard()
|| ((*input_device).last_input_time_millisecs() != 0 || ((*input_device).last_active_time_millisecs() != 0
&& current_time_millisecs && current_time_millisecs
- (*input_device).last_input_time_millisecs() - (*input_device).last_active_time_millisecs()
< 60000))) { < 60000))) {
total++; total++;
if (!(*input_device).IsTouchScreen()) { if (!(*input_device).IsTouchScreen()) {
@ -441,9 +438,9 @@ auto Input::GetLocalActiveInputDeviceCount() -> int {
if (input_device.Exists() && !input_device->IsKeyboard() if (input_device.Exists() && !input_device->IsKeyboard()
&& !input_device->IsTouchScreen() && !input_device->IsUIOnly() && !input_device->IsTouchScreen() && !input_device->IsUIOnly()
&& input_device->IsLocal() && input_device->IsLocal()
&& (input_device->last_input_time_millisecs() != 0 && (input_device->last_active_time_millisecs() != 0
&& current_time_millisecs && current_time_millisecs
- input_device->last_input_time_millisecs() - input_device->last_active_time_millisecs()
< 60000)) { < 60000)) {
count++; count++;
} }
@ -639,8 +636,9 @@ void Input::UnlockAllInput(bool permanent, const std::string& label) {
permanent ? "permanent unlock: " permanent ? "permanent unlock: "
: "temp unlock: " + label + " time " : "temp unlock: " + label + " time "
+ std::to_string(g_core->GetAppTimeMillisecs())); + 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(); recent_input_locks_unlocks_.pop_front();
}
if (permanent) { if (permanent) {
input_lock_count_permanent_--; input_lock_count_permanent_--;
@ -722,15 +720,15 @@ void Input::PrintLockLabels_() {
void Input::PushTextInputEvent(const std::string& text) { void Input::PushTextInputEvent(const std::string& text) {
assert(g_base->logic->event_loop()); assert(g_base->logic->event_loop());
g_base->logic->event_loop()->PushCall([this, text] { g_base->logic->event_loop()->PushCall([this, text] {
// Mark as active even if input is locked.
MarkInputActive(); MarkInputActive();
// If the app doesn't want direct text input right now, ignore. if (IsInputLocked()) {
if (!g_base->app_adapter->HasDirectKeyboardInput()) {
return; return;
} }
// Ignore if input is locked. // If the app doesn't want direct text input right now, ignore.
if (IsInputLocked()) { if (!g_base->app_adapter->HasDirectKeyboardInput()) {
return; return;
} }
@ -795,16 +793,14 @@ void Input::HandleJoystickEvent_(const SDL_Event& event,
if (ShouldCompletelyIgnoreInputDevice(input_device)) { if (ShouldCompletelyIgnoreInputDevice(input_device)) {
return; return;
} }
if (IsInputLocked()) {
// Mark as active even if input is locked.
input_device->UpdateLastActiveTime();
if (IsInputLocked(input_device)) {
return; 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 someone is capturing these events, give them a crack at it.
if (joystick_input_capture_) { if (joystick_input_capture_) {
if (joystick_input_capture_(event, input_device)) { if (joystick_input_capture_(event, input_device)) {
@ -905,9 +901,9 @@ void Input::HandleKeyReleaseSimple_(int keycode) {
void Input::HandleKeyPress_(const SDL_Keysym& keysym) { void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
// Mark as active even if input is locked.
MarkInputActive(); MarkInputActive();
// Ignore all key presses if input is locked.
if (IsInputLocked()) { if (IsInputLocked()) {
return; return;
} }
@ -1180,8 +1176,9 @@ void Input::PushMouseScrollEvent(const Vector2f& amount) {
void Input::HandleMouseScroll_(const Vector2f& amount) { void Input::HandleMouseScroll_(const Vector2f& amount) {
assert(g_base->InLogicThread()); 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(); MarkInputActive();
if (IsInputLocked()) { if (IsInputLocked()) {
return; return;
} }
@ -1217,8 +1214,9 @@ void Input::PushSmoothMouseScrollEvent(const Vector2f& velocity,
void Input::HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum) { void Input::HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum) {
assert(g_base->InLogicThread()); 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(); MarkInputActive();
if (IsInputLocked()) { if (IsInputLocked()) {
return; return;
} }
@ -1259,6 +1257,7 @@ void Input::HandleMouseMotion_(const Vector2f& position) {
assert(g_base); assert(g_base);
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
// Mark as active even if input is locked.
MarkInputActive(); MarkInputActive();
if (IsInputLocked()) { if (IsInputLocked()) {
@ -1309,6 +1308,7 @@ void Input::HandleMouseDown_(int button, const Vector2f& position) {
assert(g_base); assert(g_base);
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
// Mark as active even if input is locked.
MarkInputActive(); MarkInputActive();
if (IsInputLocked()) { if (IsInputLocked()) {
@ -1416,12 +1416,13 @@ void Input::HandleTouchEvent_(const TouchEvent& e) {
assert(g_base->InLogicThread()); assert(g_base->InLogicThread());
assert(g_base->graphics); assert(g_base->graphics);
// Mark as active even if input is locked.
MarkInputActive();
if (IsInputLocked()) { if (IsInputLocked()) {
return; return;
} }
MarkInputActive();
if (g_buildconfig.ostype_ios_tvos()) { if (g_buildconfig.ostype_ios_tvos()) {
printf("FIXME: update touch handling\n"); printf("FIXME: update touch handling\n");
} }
@ -1564,4 +1565,11 @@ void Input::LsInputDevices() {
Log(LogLevel::kInfo, out); 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 } // namespace ballistica::base

View File

@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "ballistica/base/base.h" #include "ballistica/base/base.h"
#include "ballistica/shared/foundation/macros.h"
#include "ballistica/shared/foundation/object.h" #include "ballistica/shared/foundation/object.h"
#include "ballistica/shared/foundation/types.h" #include "ballistica/shared/foundation/types.h"
@ -62,7 +63,14 @@ class Input {
void Reset(); void Reset();
void LockAllInput(bool permanent, const std::string& label); void LockAllInput(bool permanent, const std::string& label);
void UnlockAllInput(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; return input_lock_count_temp_ > 0 || input_lock_count_permanent_ > 0;
} }
auto cursor_pos_x() const -> float { return cursor_pos_x_; } auto cursor_pos_x() const -> float { return cursor_pos_x_; }
@ -92,12 +100,7 @@ class Input {
} }
void Draw(FrameDef* frame_def); void Draw(FrameDef* frame_def);
// Get the total idle time for the system. /// Should be called whenever user-input of some form comes through.
// 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(); }
auto MarkInputActive() { input_active_ = true; } auto MarkInputActive() { input_active_ = true; }
// returns true if more than one non-keyboard device has been active recently // returns true if more than one non-keyboard device has been active recently
@ -153,7 +156,11 @@ class Input {
void ReleaseJoystickInput(); void ReleaseJoystickInput();
void RebuildInputDeviceDelegates(); void RebuildInputDeviceDelegates();
auto attract_mode() const { return attract_mode_; }
void set_attract_mode(bool val) { attract_mode_ = val; }
private: private:
auto ShouldAllowInputInAttractMode_(InputDevice* device) const -> bool;
void UpdateInputDeviceCounts_(); void UpdateInputDeviceCounts_();
auto GetNewNumberedIdentifier_(const std::string& name, auto GetNewNumberedIdentifier_(const std::string& name,
const std::string& identifier) -> int; const std::string& identifier) -> int;
@ -183,8 +190,9 @@ class Input {
int max_controller_count_so_far_{}; int max_controller_count_so_far_{};
int local_active_input_device_count_{}; int local_active_input_device_count_{};
int mouse_move_count_{}; int mouse_move_count_{};
int input_lock_count_temp_{}; int8_t input_lock_count_temp_{};
int input_lock_count_permanent_{}; int8_t input_lock_count_permanent_{};
bool attract_mode_{};
bool input_active_{}; bool input_active_{};
bool have_button_using_inputs_{}; bool have_button_using_inputs_{};
bool have_start_activated_default_button_inputs_{}; bool have_start_activated_default_button_inputs_{};

View File

@ -1805,6 +1805,26 @@ static PyMethodDef PyOpenFileExternallyDef = {
"Open the provided file in the default external app.", "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<PyMethodDef> { auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
@ -1873,6 +1893,7 @@ auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
PyNativeReviewRequestDef, PyNativeReviewRequestDef,
PyTempTestingDef, PyTempTestingDef,
PyOpenFileExternallyDef, PyOpenFileExternallyDef,
PyGetInputIdleTimeDef,
}; };
} }

View File

@ -169,7 +169,6 @@ void AppConfig::CompleteMap(const T& entry_map) {
void AppConfig::SetupEntries() { void AppConfig::SetupEntries() {
// Register all our typed entries. // Register all our typed entries.
float_entries_[FloatID::kScreenGamma] = FloatEntry("Screen Gamma", 1.0F);
float_entries_[FloatID::kScreenPixelScale] = float_entries_[FloatID::kScreenPixelScale] =
FloatEntry("Screen Pixel Scale", 1.0F); FloatEntry("Screen Pixel Scale", 1.0F);
float_entries_[FloatID::kTouchControlsScale] = float_entries_[FloatID::kTouchControlsScale] =
@ -236,6 +235,8 @@ void AppConfig::SetupEntries() {
BoolEntry("Disable Camera Shake", false); BoolEntry("Disable Camera Shake", false);
bool_entries_[BoolID::kDisableCameraGyro] = bool_entries_[BoolID::kDisableCameraGyro] =
BoolEntry("Disable Camera Gyro", false); 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. // Now add everything to our name map and make sure all is kosher.
CompleteMap(float_entries_); CompleteMap(float_entries_);

View File

@ -9,9 +9,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
// FIXME: this system is old and dumb. // FIXME: this system is old and dumb. It was built to make C++ stuff
// Should come up with a better one using the meta system // type-safe but does not handle the Python side at all. We should come up
// based on Python dataclassio types or whatnot. // with something Python-centric using dataclasses/etc. where a C++
// component gets autogenerated via the meta system/etc.
namespace ballistica::base { namespace ballistica::base {
@ -24,7 +25,6 @@ class AppConfig {
// Our official config values: // Our official config values:
enum class FloatID { enum class FloatID {
kScreenGamma,
kScreenPixelScale, kScreenPixelScale,
kTouchControlsScale, kTouchControlsScale,
kTouchControlsScaleMovement, kTouchControlsScaleMovement,
@ -74,6 +74,7 @@ class AppConfig {
kEnableRemoteApp, kEnableRemoteApp,
kDisableCameraShake, kDisableCameraShake,
kDisableCameraGyro, kDisableCameraGyro,
kShowDemosWhenIdle,
kLast // Sentinel. kLast // Sentinel.
}; };

View File

@ -4,6 +4,7 @@
#include "ballistica/base/graphics/graphics.h" #include "ballistica/base/graphics/graphics.h"
#include "ballistica/base/graphics/support/camera.h" #include "ballistica/base/graphics/support/camera.h"
#include "ballistica/base/input/input.h"
#include "ballistica/base/logic/logic.h" #include "ballistica/base/logic/logic.h"
#include "ballistica/classic/support/stress_test.h" #include "ballistica/classic/support/stress_test.h"
#include "ballistica/scene_v1/support/scene_v1_app_mode.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; BA_PYTHON_TRY;
int enable; int enable;
int player_count; 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; return nullptr;
} }
g_base->logic->event_loop()->PushCall([enable, player_count] { g_base->logic->event_loop()->PushCall([enable, player_count, attract_mode] {
g_classic->stress_test()->Set(enable, player_count); g_classic->stress_test()->Set(enable, player_count, attract_mode);
g_base->input->set_attract_mode(enable && attract_mode);
}); });
Py_RETURN_NONE; Py_RETURN_NONE;
BA_PYTHON_CATCH; BA_PYTHON_CATCH;
@ -169,7 +172,9 @@ static PyMethodDef PySetStressTestingDef = {
PySetStressTesting, // method PySetStressTesting, // method
METH_VARARGS, // flags 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" "\n"
"(internal)", "(internal)",
}; };

View File

@ -11,16 +11,16 @@
namespace ballistica::classic { 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()); assert(g_base->InLogicThread());
bool was_stress_testing = stress_testing_; bool was_stress_testing = stress_testing_;
stress_testing_ = enable; stress_testing_ = enable;
stress_test_player_count_ = player_count; stress_test_player_count_ = player_count;
attract_mode_ = attract_mode;
// If we're turning on, reset our intervals and things. // If we're turning on, reset our intervals and things.
if (!was_stress_testing && stress_testing_) { if (!was_stress_testing && stress_testing_) {
// So our first sample is 1 interval from now. // So our first sample is 1 interval from now.
// last_stress_test_update_time_ = g_core->GetAppTimeMillisecs();
// Reset our frames-rendered tally. // Reset our frames-rendered tally.
if (g_base && g_base->graphics_server if (g_base && g_base->graphics_server
@ -71,9 +71,11 @@ void StressTest::ProcessInputs(int player_count) {
test_inputs_.push_back(new base::TestInput()); 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 (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; stress_test_last_leave_time_ = time;
// Usually do oldest; sometimes newest. // Usually do oldest; sometimes newest.

View File

@ -11,7 +11,7 @@ namespace ballistica::classic {
class StressTest { class StressTest {
public: public:
void Set(bool enable, int player_count); void Set(bool enable, int player_count, bool attract_mode);
void Update(); void Update();
private: private:
@ -23,8 +23,7 @@ class StressTest {
int stress_test_player_count_{8}; int stress_test_player_count_{8};
int last_total_frames_rendered_{}; int last_total_frames_rendered_{};
bool stress_testing_{}; bool stress_testing_{};
// millisecs_t last_stress_test_update_time_{}; bool attract_mode_{};
// FILE* stress_test_stats_file_{};
Object::Ref<base::AppTimer> update_timer_{}; Object::Ref<base::AppTimer> update_timer_{};
}; };

View File

@ -73,6 +73,9 @@ void PythonClassInputDevice::SetupType(PyTypeObject* cls) {
" is_remote_client (bool):\n" " is_remote_client (bool):\n"
" Whether this input-device represents a remotely-connected\n" " Whether this input-device represents a remotely-connected\n"
" client.\n" " client.\n"
"\n"
" is_test_input (bool):\n"
" Whether this input-device is a dummy device for testing.\n"
"\n"; "\n";
cls->tp_new = tp_new; cls->tp_new = tp_new;
@ -265,6 +268,16 @@ auto PythonClassInputDevice::tp_getattro(PythonClassInputDevice* self,
} else { } else {
Py_RETURN_FALSE; 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. // Fall back to generic behavior.

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // 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 char* kEngineVersion = "1.7.33";
const int kEngineApiVersion = 8; const int kEngineApiVersion = 8;