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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<InputDeviceDelegate> 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_;

View File

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

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@
#include <vector>
#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_{};

View File

@ -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<PyMethodDef> {
@ -1873,6 +1893,7 @@ auto PythonMethodsMisc::GetMethods() -> std::vector<PyMethodDef> {
PyNativeReviewRequestDef,
PyTempTestingDef,
PyOpenFileExternallyDef,
PyGetInputIdleTimeDef,
};
}

View File

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

View File

@ -9,9 +9,10 @@
#include <string>
#include <vector>
// 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.
};

View File

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

View File

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

View File

@ -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<base::AppTimer> update_timer_{};
};

View File

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

View File

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