a bit more shutdown process cleanup

This commit is contained in:
Eric 2023-09-10 13:28:28 -07:00
parent a34a78f4e6
commit f23365726b
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
13 changed files with 147 additions and 118 deletions

88
.efrocachemap generated
View File

@ -4064,50 +4064,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": "be7456a804d5eb5d2ff3e3b0530b0017",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "b399f5228f71d01ad463f9adf6f9f3e3",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "c4571dc43b79a99f26a4063fe99cd24c",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "26e0d031dbb08e82f7972a605a00f54b",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "dfa74ed91d8a078ce7b1f55b81eef757",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "8e3910433139fefbe38aa1c82860a74d",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "21bc802d885c62fd08e5401c194d9ade",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "c197698cbf697bc2a9dcde4360e77774",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "15a86a0b661bdc17dd45e090499f543a",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "fa7e82973a3c7c3c65ab34289dcf1347",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "d90df2394233c26521a95fd28480eea7",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "fa6046684edee83a2e284713fa95d429",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "0705a4932544b892d270211232635d40",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "400866a75e8e2e7c585c52ab1d1d31e7",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "935d50a2ed3a47b7e91a31f9270702c5",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "cb26b566d37c2855f76d4e5d0d282389",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "67030df7a210f36f7bc0440c710aabd3",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "76febafca7f3234053af1b9aec0c2e17",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "679e7c1cc7429e7d6e3d2c3acf7af7e5",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "4a05fa84099fdb1ac4a1079e80c2820c",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "c60d7bbdc75fc714a982213c8ca8dddb",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "38c3726c684e5a87c9b875ba33d416d2",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "c60d7bbdc75fc714a982213c8ca8dddb",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "38c3726c684e5a87c9b875ba33d416d2",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "ed4ba7cce56cc2b91d344f03b22ec23d",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "b2a242985db24c2d486d72e0c4d06bde",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "ed4ba7cce56cc2b91d344f03b22ec23d",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "b2a242985db24c2d486d72e0c4d06bde",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "57d5225333eaf9e0df25cd54a1f58411",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "e17654c90848f85140030c45e5a6ed6b",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "57d5225333eaf9e0df25cd54a1f58411",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "e17654c90848f85140030c45e5a6ed6b",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "8d4d62bb78e1bae5947a9e1c5f602425",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "e00d5c375cabf7026557f2780edc6589",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "cafeecd06961bee4ca51e2c34a25f137",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "e00d5c375cabf7026557f2780edc6589",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "202043a4df67abe971cb887cc9ab40ce",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "73aada04161cc1e64306ba8964b65273",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "808a7fe357ceb8fc3f387d44b9c82890",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "5e757aa1dcdc5a66826c1f21e31a85fd",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "aec6cbbbaff1be15c197999b4a2adb76",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "b6dc26173fd02722a96e6994a775a3f6",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "e799ebd0d74fec947b2d8696701b5719",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "fd08bc9cc162d1339394145d20516447",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "44dec65bbb43c2424334cce255b55836",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "55489d3d62fd081b83c4df871e40ad27",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "21530b0be2f54d1c457a8c2ca5bfb480",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "a3d058738fc7891bc1d0139654b5fc26",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "8d4b813a4955b6574b4e0e6b413ad7cf",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "7c618d9dac85afc6a7be8c7927693e81",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "2b2ddfe86feb7e701d472264c5d7ea83",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "63f59ed473e1f954786284d6988c1a2b",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "b703788a1e3aef102349db7968b2dd99",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "dd4ea149455ddf77db357bbcf92622ac",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "d0651d0ed865c44f45a0e86a93fdf46b",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "65f43cfa50bf3fb198ccdacb5ac7dfe4",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "a2b3388c4deec4e980a0268b0757ac3a",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "a60d7430b9ba3cf71bc9ffe5944026fc",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "d08625da75aba13159ea4e649b87eff9",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "c265fede38b25b8257ae1e6acb1d8036",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "74f47c43f480f60732b43a0f9b80f76d",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "c9b4d66d5ce318e5cecb7412771fbfba",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "fe6ad3d4cdaadd56326f5e616588b3fb",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "772769bf8f5c49031782f68eaa49c0d3",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a075beb846859a3bee6b4fc1c4d9369b",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "208a67fb7e7b942988e8520f9570138e",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a075beb846859a3bee6b4fc1c4d9369b",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "208a67fb7e7b942988e8520f9570138e",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "2a98d808b017ddac714d2f266d443394",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "24c7f0248a8f59e5349db9c040e6bd4f",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "2a98d808b017ddac714d2f266d443394",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "24c7f0248a8f59e5349db9c040e6bd4f",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "e8f8be3a0ba00a2ecb8956c2459107ec",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "741e277f99d48437a5a1b9dacef107ee",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "e8f8be3a0ba00a2ecb8956c2459107ec",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "741e277f99d48437a5a1b9dacef107ee",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "3db2e9a04f23052f3a14390a0f7ba00e",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "15cf0e78e70d952c14c4b5e9ad6ef749",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "a0b27fbfca2dd7404a20997fbfa10a7f",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "15cf0e78e70d952c14c4b5e9ad6ef749",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "25d06d95141284fff10db4a55ed481eb",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "708878d75d73b8510b354b2f353da621",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "b8b36fd481e253e83b3cf90734a7d627",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "17cb96c46a7e1763fdfbc6f48199f547",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ae3b27deef1240beb1b32a17a46b7d90",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "0e2c5cec39ac27d42cb5cd635da996bd",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "6b278874c0c0526494bd94aad4a817ae",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "55d3224895c30042caca26e6d77e406b",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ad347097a38e0d7ede9eb6dec6a80ee9",

View File

@ -1,4 +1,4 @@
### 1.7.28 (build 21328, api 8, 2023-09-09)
### 1.7.28 (build 21329, api 8, 2023-09-10)
- Renamed Console to DevConsole, and added an option under advanced settings to
always show an ugly 'dev' button onscreen which can be used to toggle it. The

View File

@ -143,7 +143,6 @@ from babase._general import (
storagename,
getclass,
get_type_name,
json_prep,
)
from babase._keyboard import Keyboard
from babase._language import Lstr, LanguageSubsystem
@ -244,7 +243,6 @@ __all__ = [
'is_point_in_box',
'is_running_on_fire_tv',
'is_xcode_build',
'json_prep',
'Keyboard',
'LanguageSubsystem',
'lock_all_input',

View File

@ -97,6 +97,9 @@ class App:
# The app is shutting down.
SHUTTING_DOWN = 6
# The app has completed shutdown.
SHUTDOWN_COMPLETE = 7
class DefaultAppModeSelector(AppModeSelector):
"""Decides which AppModes to use to handle AppIntents.
@ -142,7 +145,8 @@ class App:
feature-set modules such as babase.
"""
# Hack for docs-generation.
# Hack for docs-generation: we can be imported with dummy modules
# instead of our actual binary ones, but we don't function.
if os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') == '1':
return
@ -174,6 +178,7 @@ class App:
self._native_start_called = False
self._native_paused = False
self._native_shutdown_called = False
self._native_shutdown_complete_called = False
self._initial_sign_in_completed = False
self._called_on_initing = False
self._called_on_loading = False
@ -193,16 +198,17 @@ class App:
]
def postinit(self) -> None:
"""Called after we've been inited and assigned to babase.app."""
"""Called after we've been inited and assigned to babase.app.
# Hack for docs-generation.
Anything that accesses babase.app as part of its init process
must go here instead of __init__.
"""
# Hack for docs-generation: we can be imported with dummy modules
# instead of our actual binary ones, but we don't function.
if os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') == '1':
return
# NOTE: the reason we need a postinit here is that some of this
# stuff accesses babase.app and that doesn't exist yet as of our
# __init__() call.
self.lang = LanguageSubsystem()
self.plugins = PluginSubsystem()
@ -308,9 +314,13 @@ class App:
Note that tasks will be killed after
App.SHUTDOWN_TASK_TIMEOUT_SECONDS if they are still running.
"""
if self.state is self.State.SHUTTING_DOWN:
if (
self.state is self.State.SHUTTING_DOWN
or self.state is self.State.SHUTDOWN_COMPLETE
):
stname = self.state.name
raise RuntimeError(
'Cannot add shutdown tasks with state SHUTTING_DOWN.'
f'Cannot add shutdown tasks with current state {stname}.'
)
self._shutdown_tasks.append(coro)
@ -398,6 +408,8 @@ class App:
def on_native_shutdown_complete(self) -> None:
"""Called by the native layer when the app is done shutting down."""
assert _babase.in_logic_thread()
self._native_shutdown_complete_called = True
self._update_state()
def read_config(self) -> None:
"""(internal)"""
@ -697,13 +709,21 @@ class App:
# pylint: disable=too-many-branches
assert _babase.in_logic_thread()
# Shutdown trumps all. Though we can't shut down until init is
# completed since we need our asyncio stuff to exist for the
# shutdown process.
if self._native_shutdown_called and self._init_completed:
# Shutdown-complete trumps absolutely all.
if self._native_shutdown_complete_called:
if self.state is not self.State.SHUTDOWN_COMPLETE:
self.state = self.State.SHUTDOWN_COMPLETE
_babase.lifecyclelog('app state shutdown complete')
self._on_shutdown_complete()
# Shutdown trumps all. Though we can't start shutting down until
# init is completed since we need our asyncio stuff to exist for
# the shutdown process.
elif self._native_shutdown_called and self._init_completed:
# Entering shutdown state:
if self.state is not self.State.SHUTTING_DOWN:
self.state = self.State.SHUTTING_DOWN
_babase.lifecyclelog('app state shutting down')
self._on_shutting_down()
elif self._native_paused:
@ -825,6 +845,21 @@ class App:
assert self._aioloop is not None
self._shutdown_task = self._aioloop.create_task(self._shutdown())
def _on_shutdown_complete(self) -> None:
"""(internal)"""
assert _babase.in_logic_thread()
# Inform app subsystems that we're done shutting down in the opposite
# order they were inited.
for subsystem in reversed(self._subsystems):
try:
subsystem.on_app_shutdown_complete()
except Exception:
logging.exception(
'Error in on_app_shutdown_complete for subsystem %s.',
subsystem,
)
async def _wait_for_shutdown_suppressions(self) -> None:
import asyncio

View File

@ -1,6 +1,6 @@
# Released under the MIT License. See LICENSE for details.
#
"""Provides AppMode functionality."""
"""Contains AppModeSelector base class."""
from __future__ import annotations
from typing import TYPE_CHECKING

View File

@ -18,8 +18,8 @@ class AppSubsystem:
An app 'subsystem' is a bit of a vague term, as pieces of the app
can technically be any class and are not required to use this, but
building one out of this base class provides some conveniences such
as predefined callbacks during app state changes.
building one out of this base class provides conveniences such as
predefined callbacks during app state changes.
Subsystems must be registered with the app before it completes its
transition to the 'running' state.
@ -48,5 +48,8 @@ class AppSubsystem:
def on_app_shutdown(self) -> None:
"""Called when the app is shutting down."""
def on_app_shutdown_complete(self) -> None:
"""Called when the app is done shutting down."""
def do_apply_app_config(self) -> None:
"""Called when the app config should be applied."""

View File

@ -6,12 +6,13 @@ from __future__ import annotations
import types
import weakref
import random
import logging
import inspect
from typing import TYPE_CHECKING, TypeVar, Protocol, NewType
from efro.terminal import Clr
import _babase
from babase._error import print_error, print_exception
if TYPE_CHECKING:
from typing import Any
@ -19,7 +20,8 @@ if TYPE_CHECKING:
# Declare distinct types for different time measurements we use so the
# type-checker can help prevent us from mixing and matching accidentally.
# type-checker can help prevent us from mixing and matching accidentally,
# even if the *actual* types being used are the same.
# Our monotonic time measurement that starts at 0 when the app launches
# and pauses while the app is suspended.
@ -85,39 +87,6 @@ def getclass(name: str, subclassof: type[T]) -> type[T]:
return cls
def json_prep(data: Any) -> Any:
"""Return a json-friendly version of the provided data.
This converts any tuples to lists and any bytes to strings
(interpreted as utf-8, ignoring errors). Logs errors (just once)
if any data is modified/discarded/unsupported.
"""
if isinstance(data, dict):
return dict(
(json_prep(key), json_prep(value))
for key, value in list(data.items())
)
if isinstance(data, list):
return [json_prep(element) for element in data]
if isinstance(data, tuple):
print_error('json_prep encountered tuple', once=True)
return [json_prep(element) for element in data]
if isinstance(data, bytes):
try:
return data.decode(errors='ignore')
except Exception:
from babase import _error
print_error('json_prep encountered utf-8 decode error', once=True)
return data.decode(errors='ignore')
if not isinstance(data, (str, float, bool, type(None), int)):
print_error(
'got unsupported type in json_prep:' + str(type(data)), once=True
)
return data
def utf8_all(data: Any) -> Any:
"""Convert any unicode data in provided sequence(s) to utf8 bytes."""
if isinstance(data, dict):
@ -136,7 +105,7 @@ def utf8_all(data: Any) -> Any:
def get_type_name(cls: type) -> str:
"""Return a full type name including module for a class."""
return cls.__module__ + '.' + cls.__name__
return f'{cls.__module__}.{cls.__name__}'
class _WeakCall:
@ -195,18 +164,12 @@ class _WeakCall:
else:
app = _babase.app
if not self._did_invalid_call_warning:
print(
(
'Warning: callable passed to babase.WeakCall() is not'
' weak-referencable ('
+ str(args[0])
+ '); use babase.Call() instead to avoid this '
'warning. Stack-trace:'
)
logging.warning(
'Warning: callable passed to babase.WeakCall() is not'
' weak-referencable (%s); use babase.Call() instead'
' to avoid this warning.',
stack_info=True,
)
import traceback
traceback.print_stack()
self._did_invalid_call_warning = True
self._call = args[0]
self._args = args[1:]
@ -320,7 +283,7 @@ def verify_object_death(obj: object) -> None:
try:
ref = weakref.ref(obj)
except Exception:
print_exception('Unable to create weak-ref in verify_object_death')
logging.exception('Unable to create weak-ref in verify_object_death')
return
# Use a slight range for our checks so they don't all land at once

View File

@ -466,6 +466,37 @@ class ClassicSubsystem(babase.AppSubsystem):
_analytics.game_begin_analytics()
@classmethod
def json_prep(cls, data: Any) -> Any:
"""Return a json-friendly version of the provided data.
This converts any tuples to lists and any bytes to strings
(interpreted as utf-8, ignoring errors). Logs errors (just once)
if any data is modified/discarded/unsupported.
"""
if isinstance(data, dict):
return dict(
(cls.json_prep(key), cls.json_prep(value))
for key, value in list(data.items())
)
if isinstance(data, list):
return [cls.json_prep(element) for element in data]
if isinstance(data, tuple):
logging.exception('json_prep encountered tuple')
return [cls.json_prep(element) for element in data]
if isinstance(data, bytes):
try:
return data.decode(errors='ignore')
except Exception:
logging.exception('json_prep encountered utf-8 decode error')
return data.decode(errors='ignore')
if not isinstance(data, (str, float, bool, type(None), int)):
logging.exception(
'got unsupported type in json_prep: %s', type(data)
)
return data
def master_server_v1_get(
self,
request: 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 = 21328
TARGET_BALLISTICA_BUILD = 21329
TARGET_BALLISTICA_VERSION = '1.7.28'

View File

@ -462,7 +462,7 @@ class Chooser:
# (non-unicode/non-json) version.
# Make sure they conform to our standards
# (unicode strings, no tuples, etc)
self._profiles = babase.json_prep(self._profiles)
self._profiles = app.classic.json_prep(self._profiles)
# Filter out any characters we're unaware of.
for profile in list(self._profiles.items()):

View File

@ -230,6 +230,8 @@ void BaseFeatureSet::OnAppShutdownComplete() {
assert(g_core);
assert(g_base);
g_core->LifecycleLog("app exiting (main thread)");
// Flag our own event loop to exit (or ask the OS to if they're managing).
if (app_adapter->ManagesEventLoop()) {
g_core->main_event_loop()->Quit();

View File

@ -221,8 +221,6 @@ void Logic::OnAppShutdown() {
assert(g_base->CurrentContext().IsEmpty());
assert(shutting_down_);
g_core->LifecycleLog("app state shutting down");
// Nuke the app from orbit if we get stuck while shutting down.
g_core->StartSuicideTimer("shutdown", 10000);
@ -259,13 +257,12 @@ void Logic::OnAppShutdownComplete() {
// Wrap up any last business here in the logic thread and then kick things
// over to the main thread to exit out of the main loop.
g_core->LifecycleLog("app shutdown complete");
// Let our logic subsystems know in case there's any last thing they'd
// like to do right before we exit.
// Note: Keep these in opposite order of OnAppStart.
// Note2: Any shutdown processes that take a non-zero amount of time
// should be registered as shutdown-tasks
// should be registered as shutdown-tasks.
g_base->python->OnAppShutdownComplete();
if (g_base->HavePlus()) {
g_base->plus()->OnAppShutdownComplete();

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 = 21328;
const int kEngineBuildNumber = 21329;
const char* kEngineVersion = "1.7.28";
const int kEngineApiVersion = 8;