diff --git a/.efrocachemap b/.efrocachemap
index 300321f8..7cec964e 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -421,7 +421,7 @@
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55",
- "build/assets/ba_data/data/langdata.json": "bb60812044af0a8d1bdefee759ff2522",
+ "build/assets/ba_data/data/langdata.json": "29a60e77c26b1f397617f8d8c8d652f9",
"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",
@@ -429,23 +429,23 @@
"build/assets/ba_data/data/languages/croatian.json": "766532c67af5bd0144c2d63cab0516fa",
"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/dutch.json": "b0900d572c9141897d53d6574c471343",
"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": "0031cbb8eb6a638a94fb43c5d892346c",
- "build/assets/ba_data/data/languages/french.json": "8bc35eb4b20a0b30c3348bcc9a3844a6",
+ "build/assets/ba_data/data/languages/french.json": "cc8ac601f5443dd539893728db983f5c",
"build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad",
"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",
- "build/assets/ba_data/data/languages/indonesian.json": "408fb026e84c24a8dd7a43cb2b794541",
+ "build/assets/ba_data/data/languages/indonesian.json": "9103845242b572aa8ba48e24f81ddb68",
"build/assets/ba_data/data/languages/italian.json": "f550810b6866ea9bcf1985b7228f8cff",
"build/assets/ba_data/data/languages/korean.json": "03fd99d5e1155e81053fc028f69df982",
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38",
"build/assets/ba_data/data/languages/persian.json": "9728d631cf7d9ad3b209ae1244bb59c0",
"build/assets/ba_data/data/languages/polish.json": "3a90b2d9e2c59305580c96f8098fc839",
- "build/assets/ba_data/data/languages/portuguese.json": "0274cb9a4b7d2bd49c8eb8120144a1bf",
+ "build/assets/ba_data/data/languages/portuguese.json": "b52164747c6308fc9d054eb6c0ff3c54",
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
"build/assets/ba_data/data/languages/russian.json": "30d5f3d2415088e1fb6558fcd6ccfa98",
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
@@ -4060,50 +4060,50 @@
"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": "cd19dfdf480de6e73949db674e1b02d2",
- "build/prefab/full/linux_arm64_gui/release/ballisticakit": "8c08cdda59e731a3830624000de5ca7f",
- "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "14685eca62b8540cc2a268883d0ebc5d",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "f81876d7827a10be412306c52b03fa08",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "b8370845743ebba86ed6eaa6ee1d79d5",
- "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "cdf04825dedae8fb2c26502ec2a505db",
- "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "4036493f98646b58de8bf425bee227cb",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "1cf36c63f68ecaa954fb9c48a132725e",
- "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "b757a940c5157197a0138e12e308f859",
- "build/prefab/full/mac_arm64_gui/release/ballisticakit": "92a07f83fceeddf3b29cfe2ead57f7e3",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "90002c085c66be4af378d4b3fc8e0260",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "eeb363d0d48e68f5f2ac2e536a26aeeb",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "e119b480fc7e542f33ceb16e8c04585f",
- "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "6e657aa09d052765ed891789ec60dfb2",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "2d4eab8ea8399defd1afdbe548216e9c",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "b03bb9d5d1eb695a11843f64f24906ef",
- "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "163fbc40b479ef1db1c753f7beb73c0f",
- "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "37291abd76871f4556348f77e12dd363",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "acca6904f2f2f952ecae99922c602b9d",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "3c2dfd9cf26e77a0b803ed43c85df113",
- "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "3af5cf00e5eb30d55030e8705b83353a",
- "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "be337c05f72235b5b486277bb1a9c259",
- "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "3af5cf00e5eb30d55030e8705b83353a",
- "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "be337c05f72235b5b486277bb1a9c259",
- "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "17c7d0041bc7c84077bf6692b16e3988",
- "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "4affbfea91e8a33ab62da763ffc07ddd",
- "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "17c7d0041bc7c84077bf6692b16e3988",
- "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "4affbfea91e8a33ab62da763ffc07ddd",
- "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "58656b49d34e6c650983fbf79b5c41ae",
- "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "3ce5652e0ff5d277e256f517dec4eb61",
- "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "58656b49d34e6c650983fbf79b5c41ae",
- "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "3ce5652e0ff5d277e256f517dec4eb61",
- "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "b94fff3a719003c1d8f5dd16dffdb3fc",
- "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "5d68e1957febe6053815bbee3f068e76",
- "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "85bbca447ca8a1d0fad984afc6f0700a",
- "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "5d68e1957febe6053815bbee3f068e76",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "7f2ed141a475e051d3350d571ef6cb0c",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "93fb764531ae16a30d4886eb183c3681",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "750cd7ef428f4faf65ccbeff50c21f8e",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "104cf85df9f1f7ffbf4de5997f7c6879",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "af85fb387d755b152c42f8dfb0891ad7",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "01ce8e0619b342e4cc2cc5f18f81a727",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "f8aae2e04f95f4cfb863791da36ff931",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "66252d58a1be97db8523bd0bf8098a16",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "c1f5b6a9fe5aee5baab40248a00eb606",
+ "build/prefab/full/linux_arm64_gui/release/ballisticakit": "88888a9139b8a047d1dde25ae0aa9ea6",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "e51014b672283258ba29f546f3a8833e",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "811973441c2d7998445182b2ab4bcc24",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "01e0fe6152b79211acec7e88a99c6579",
+ "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "9e2b7acbcd3892af6be5f85bfe328c2c",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "2f54025a53bb947c89eee520131eae52",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "b9df392dca06e52b958ea3c9b676ae6a",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f2ed10e0b5956ce8ef5b87c3e0c75eea",
+ "build/prefab/full/mac_arm64_gui/release/ballisticakit": "e1c27e7ecb5272f92e6cfdb91b0750d3",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "67827bd210a484f7d93974b581b95a85",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "c03d44f4096cbffe0d1b3391a7b09555",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "a78c8b08a19dc23d1881eee37fdbcfdd",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ca2caba96a855ac6fa7459855bfa49bb",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "9521bca3d6dc02e896fa01ab0ccdfd0c",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "31762c816772ba2c0516c26589428c08",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "ab72b100f5c1eaaac6264b8f662fabb7",
+ "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "edf145cc7855aced80f60766a9420df5",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "4d4e6e57f179666e8f5a50352b9cda9b",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "20babca2eb9cc062f583ecea557a945e",
+ "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "8ccecbffbaa9886636741cca293d2893",
+ "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "42679aef4dd8b35b39a2c968ff20ed45",
+ "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "8ccecbffbaa9886636741cca293d2893",
+ "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "42679aef4dd8b35b39a2c968ff20ed45",
+ "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "b2c2c33149201227ba96459dbeeba012",
+ "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "2b27b84fe8305b7f3829aa28ab5e5705",
+ "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "b2c2c33149201227ba96459dbeeba012",
+ "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "2b27b84fe8305b7f3829aa28ab5e5705",
+ "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "f6b49cee7baf1ee49598b803566d9914",
+ "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "a15dc59a2de8a8d32b32f07fe25d41b1",
+ "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "f6b49cee7baf1ee49598b803566d9914",
+ "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "a15dc59a2de8a8d32b32f07fe25d41b1",
+ "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "80fd9070d92797ff6e26c0a5493dcb42",
+ "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "07c2b1098c1c08d2e28934a6f0e1fcf2",
+ "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "724d8dcd34fb6c2c0109906d44a4fb12",
+ "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "07c2b1098c1c08d2e28934a6f0e1fcf2",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "698b55a6c11f38e5c28587a7039e6175",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "753db5761ce823966fcef472b73e06d1",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "b1e41720089a0d7724337071442238c9",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "37d3e9fa31a003dd9609f476d4165d12",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "105f4dc0399235e4a9c25c3977febec8",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "820c04dd9965698a86e4f5efffdb729d",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "070727ad10a727b22beef6ee736c7a5f",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "6d2969401a8a022e28f4680b01004a50",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "b611c090513a21e2fe90e56582724e9d",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1480580..f4aa736e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-### 1.7.33 (build 21757, api 8, 2024-01-06)
+### 1.7.33 (build 21760, api 8, 2024-01-16)
- 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
diff --git a/ballisticakit-cmake/.idea/misc.xml b/ballisticakit-cmake/.idea/misc.xml
index f01f08b8..fbb3740e 100644
--- a/ballisticakit-cmake/.idea/misc.xml
+++ b/ballisticakit-cmake/.idea/misc.xml
@@ -49,9 +49,6 @@
-
-
-
diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py
index 4a021ab8..2b0191f1 100644
--- a/src/assets/ba_data/python/babase/_app.py
+++ b/src/assets/ba_data/python/babase/_app.py
@@ -1,12 +1,13 @@
# Released under the MIT License. See LICENSE for details.
#
+# pylint: disable=too-many-lines
"""Functionality related to the high level state of the app."""
from __future__ import annotations
import os
import logging
from enum import Enum
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, TypeVar
from concurrent.futures import ThreadPoolExecutor
from functools import cached_property
@@ -26,7 +27,7 @@ from babase._devconsole import DevConsoleSubsystem
if TYPE_CHECKING:
import asyncio
- from typing import Any, Callable, Coroutine
+ from typing import Any, Callable, Coroutine, Generator, Awaitable
from concurrent.futures import Future
import babase
@@ -42,6 +43,8 @@ if TYPE_CHECKING:
# __FEATURESET_APP_SUBSYSTEM_IMPORTS_END__
+T = TypeVar('T')
+
class App:
"""A class for high level app functionality and state.
@@ -199,7 +202,8 @@ class App:
self._called_on_running = False
self._subsystem_registration_ended = False
self._pending_apply_app_config = False
- self._aioloop: asyncio.AbstractEventLoop | None = None
+ self._asyncio_loop: asyncio.AbstractEventLoop | None = None
+ self._asyncio_tasks: set[asyncio.Task] = set()
self._asyncio_timer: babase.AppTimer | None = None
self._config: babase.AppConfig | None = None
self._pending_intent: AppIntent | None = None
@@ -239,18 +243,68 @@ class App:
return _babase.app_is_active()
@property
- def aioloop(self) -> asyncio.AbstractEventLoop:
+ def asyncio_loop(self) -> asyncio.AbstractEventLoop:
"""The logic thread's asyncio event loop.
This allow async tasks to be run in the logic thread.
+
+ Generally you should call App.create_async_task() to schedule
+ async code to run instead of using this directly. That will
+ handle retaining the task and logging errors automatically.
+ Only schedule tasks onto asyncio_loop yourself when you intend
+ to hold on to the returned task and await its results. Releasing
+ the task reference can lead to subtle bugs such as unreported
+ errors and garbage-collected tasks disappearing before their
+ work is done.
+
Note that, at this time, the asyncio loop is encapsulated
and explicitly stepped by the engine's logic thread loop and
- thus things like asyncio.get_running_loop() will not return this
- loop from most places in the logic thread; only from within a
- task explicitly created in this loop.
+ thus things like asyncio.get_running_loop() will unintuitively
+ *not* return this loop from most places in the logic thread;
+ only from within a task explicitly created in this loop.
+ Hopefully this situation will be improved in the future with a
+ unified event loop.
"""
- assert self._aioloop is not None
- return self._aioloop
+ assert _babase.in_logic_thread()
+ assert self._asyncio_loop is not None
+ return self._asyncio_loop
+
+ def create_async_task(
+ self,
+ coro: Generator[Any, Any, T] | Coroutine[Any, Any, T],
+ *,
+ name: str | None = None,
+ ) -> None:
+ """Create a fully managed async task.
+
+ This will automatically retain and release a reference to the task
+ and log any exceptions that occur in it. If you need to await a task
+ or otherwise need more control, schedule a task directly using
+ App.asyncio_loop.
+ """
+ assert _babase.in_logic_thread()
+ # Hold a strong reference to the task until it is done.
+ # Otherwise it is possible for it to be garbage collected and
+ # disappear midway if the caller does not hold on to the
+ # returned task, which seems like a great way to introduce
+ # hard-to-track bugs.
+ task = self.asyncio_loop.create_task(coro, name=name)
+ self._asyncio_tasks.add(task)
+ task.add_done_callback(self._on_task_done)
+ # return task
+
+ def _on_task_done(self, task: asyncio.Task) -> None:
+ # Report any errors that occurred.
+ try:
+ exc = task.exception()
+ if exc is not None:
+ logging.error(
+ "Error in async task '%s'.", task.get_name(), exc_info=exc
+ )
+ except Exception:
+ logging.exception('Error reporting async task error.')
+
+ self._asyncio_tasks.remove(task)
@property
def config(self) -> babase.AppConfig:
@@ -594,7 +648,7 @@ class App:
_env.on_app_state_initing()
- self._aioloop = _asyncio.setup_asyncio()
+ self._asyncio_loop = _asyncio.setup_asyncio()
self.health_monitor = AppHealthMonitor()
# __FEATURESET_APP_SUBSYSTEM_CREATE_BEGIN__
@@ -874,8 +928,8 @@ class App:
)
# Now kick off any async shutdown task(s).
- assert self._aioloop is not None
- self._shutdown_task = self._aioloop.create_task(self._shutdown())
+ assert self._asyncio_loop is not None
+ self._shutdown_task = self._asyncio_loop.create_task(self._shutdown())
def _on_shutdown_complete(self) -> None:
"""(internal)"""
diff --git a/src/assets/ba_data/python/baclassic/_ads.py b/src/assets/ba_data/python/baclassic/_ads.py
index 2373df36..5cd454d0 100644
--- a/src/assets/ba_data/python/baclassic/_ads.py
+++ b/src/assets/ba_data/python/baclassic/_ads.py
@@ -229,9 +229,7 @@ class AdsSubsystem:
await asyncio.sleep(1.0)
payload.run(fallback=True)
- _fallback_task = babase.app.aioloop.create_task(
- add_fallback_task()
- )
+ babase.app.create_async_task(add_fallback_task())
self.show_ad('between_game', on_completion_call=payload.run)
else:
babase.pushcall(call) # Just run the callback without the ad.
diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py
index 664ef95d..d1521b91 100644
--- a/src/assets/ba_data/python/baenv.py
+++ b/src/assets/ba_data/python/baenv.py
@@ -52,7 +52,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
-TARGET_BALLISTICA_BUILD = 21757
+TARGET_BALLISTICA_BUILD = 21760
TARGET_BALLISTICA_VERSION = '1.7.33'
diff --git a/src/assets/ba_data/python/bauiv1lib/promocode.py b/src/assets/ba_data/python/bauiv1lib/promocode.py
index 3cf745b5..6be09ab6 100644
--- a/src/assets/ba_data/python/bauiv1lib/promocode.py
+++ b/src/assets/ba_data/python/bauiv1lib/promocode.py
@@ -5,9 +5,13 @@
from __future__ import annotations
import time
+from typing import TYPE_CHECKING
import bauiv1 as bui
+if TYPE_CHECKING:
+ from typing import Any
+
class PromoCodeWindow(bui.Window):
"""Window for entering promo codes."""
@@ -167,9 +171,6 @@ class PromoCodeWindow(bui.Window):
if not self._root_widget or self._root_widget.transitioning_out:
return
- plus = bui.app.plus
- assert plus is not None
-
bui.containerwidget(
edit=self._root_widget, transition=self._transition_out
)
@@ -179,11 +180,22 @@ class PromoCodeWindow(bui.Window):
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
from_window=self._root_widget,
)
- plus.add_v1_account_transaction(
- {
- 'type': 'PROMO_CODE',
- 'expire_time': time.time() + 5,
- 'code': bui.textwidget(query=self._text_field),
- }
- )
- plus.run_v1_account_transactions()
+
+ code: Any = bui.textwidget(query=self._text_field)
+ assert isinstance(code, str)
+
+ bui.app.create_async_task(_run_code(code))
+
+
+async def _run_code(code: str) -> None:
+ plus = bui.app.plus
+ assert plus is not None
+
+ plus.add_v1_account_transaction(
+ {
+ 'type': 'PROMO_CODE',
+ 'expire_time': time.time() + 5,
+ 'code': code,
+ }
+ )
+ plus.run_v1_account_transactions()
diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc
index 4badfe1f..fa6f3b9a 100644
--- a/src/ballistica/shared/ballistica.cc
+++ b/src/ballistica/shared/ballistica.cc
@@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica {
// These are set automatically via script; don't modify them here.
-const int kEngineBuildNumber = 21757;
+const int kEngineBuildNumber = 21760;
const char* kEngineVersion = "1.7.33";
const int kEngineApiVersion = 8;
diff --git a/tools/bacommon/transfer.py b/tools/bacommon/transfer.py
index f6704b26..a53c6153 100644
--- a/tools/bacommon/transfer.py
+++ b/tools/bacommon/transfer.py
@@ -31,7 +31,7 @@ class DirectoryManifest:
files: Annotated[dict[str, DirectoryManifestFile], IOAttrs('f')]
- _empty_hash: str | None = None
+ # _empty_hash: str | None = None
@classmethod
def create_from_disk(cls, path: Path) -> DirectoryManifest:
@@ -92,12 +92,12 @@ class DirectoryManifest:
)
break # 1 error is enough for now.
- @classmethod
- def get_empty_hash(cls) -> str:
- """Return the hash for an empty file."""
- if cls._empty_hash is None:
- import hashlib
+ # @classmethod
+ # def get_empty_hash(cls) -> str:
+ # """Return the hash for an empty file."""
+ # if cls._empty_hash is None:
+ # import hashlib
- sha = hashlib.sha256()
- cls._empty_hash = sha.hexdigest()
- return cls._empty_hash
+ # sha = hashlib.sha256()
+ # cls._empty_hash = sha.hexdigest()
+ # return cls._empty_hash
diff --git a/tools/efro/dataclassio/_base.py b/tools/efro/dataclassio/_base.py
index 6ef37105..afe19deb 100644
--- a/tools/efro/dataclassio/_base.py
+++ b/tools/efro/dataclassio/_base.py
@@ -70,6 +70,13 @@ class IOExtendedData:
Can be overridden to migrate old data formats to new, etc.
"""
+ def did_input(self) -> None:
+ """Called on a class instance after created from data.
+
+ Can be useful to correct values from the db, etc. in the
+ type-safe form.
+ """
+
def _is_valid_for_codec(obj: Any, codec: Codec) -> bool:
"""Return whether a value consists solely of json-supported types.
diff --git a/tools/efro/dataclassio/_inputter.py b/tools/efro/dataclassio/_inputter.py
index d6650a65..97075528 100644
--- a/tools/efro/dataclassio/_inputter.py
+++ b/tools/efro/dataclassio/_inputter.py
@@ -64,11 +64,23 @@ class _Inputter(Generic[T]):
# For special extended data types, call their 'will_output' callback.
tcls = self._cls
+
if issubclass(tcls, IOExtendedData):
+ is_ext = True
tcls.will_input(values)
+ else:
+ is_ext = False
out = self._dataclass_from_input(self._cls, '', values)
assert isinstance(out, self._cls)
+
+ if is_ext:
+ # mypy complains that we're no longer returning a T
+ # if we operate on out directly.
+ out2 = out
+ assert isinstance(out2, IOExtendedData)
+ out2.did_input()
+
return out
def _value_from_input(
diff --git a/tools/efro/util.py b/tools/efro/util.py
index 8bc4542a..807d4c00 100644
--- a/tools/efro/util.py
+++ b/tools/efro/util.py
@@ -174,17 +174,20 @@ def empty_weakref(objtype: type[T]) -> weakref.ref[T]:
# Just create an object and let it die. Is there a cleaner way to do this?
# return weakref.ref(_EmptyObj()) # type: ignore
+ # Sharing a single ones seems at least a bit better.
return _g_empty_weak_ref # type: ignore
-def data_size_str(bytecount: int) -> str:
+def data_size_str(bytecount: int, compact: bool = False) -> str:
"""Given a size in bytes, returns a short human readable string.
- This should be 6 or fewer chars for most all sane file sizes.
+ In compact mode this should be 6 or fewer chars for most all
+ sane file sizes.
"""
# pylint: disable=too-many-return-statements
if bytecount <= 999:
- return f'{bytecount} B'
+ suffix = 'B' if compact else 'bytes'
+ return f'{bytecount} {suffix}'
kbytecount = bytecount / 1024
if round(kbytecount, 1) < 10.0:
return f'{kbytecount:.1f} KB'
@@ -623,7 +626,7 @@ def check_non_optional(obj: T | None) -> T:
Use assert_non_optional for a more efficient (but less safe) equivalent.
"""
if obj is None:
- raise TypeError('Got None value in check_non_optional.')
+ raise ValueError('Got None value in check_non_optional.')
return obj