new high-level feature-set copy/delete functionality

This commit is contained in:
Eric 2023-08-31 20:17:55 -07:00
parent b51bc29773
commit 0709e553d7
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
20 changed files with 460 additions and 119 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": "4542f7820b33b8f1f6719b17efa26453",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "c94898207acb63e3e09cb08b50ebd287",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "ad606ee2ee1367b906c9cbb18c53baf6",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "776e2461a852753010278d5b90ff32df",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "aefcfd8774114e7cebc418d0adad56f2",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "d21df3fc06db87786d68ccc1417d043e",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "7b218f79079e8e0d92b5f0a4afb0599d",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "94738f2f6fcbea9dad60af4bd6a7cd5d",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "033b39b34a10d5bce6e53572a816d0ef",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "f4eb1cc72a16eafcaba2d55c65f19a7b",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "fec5b1818aa97bc0dff1ddf127742574",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "76930acbbd2e8dd35f35639d85ce21ac",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "d5ab5ad85dd8d113125d15f03c221db3",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "29fbcf76221d71ea3ecb73d8ab353f7b",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "3d8b4b5d6ba376f2d280209e0aa3bcf2",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "fb9da4455aa7c0d8e5120a75ccaeabcb",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "056f8137ba8b1179b66eea59944aefb2",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "78f53224dd6052c93da31bda7ee1c84c",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "82128374a7eabc651061d778e099b923",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "2cd60cbebfbe25447791284b42c3caf9",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a589af5b31246539eac3264c829c41a0",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "3dfaf945474294cb9f3808a835fb667c",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a589af5b31246539eac3264c829c41a0",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "3dfaf945474294cb9f3808a835fb667c",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "47e08d2f265f4dda15b309fa67ba163b",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "c9a5225be07b4456e073014e1db2cafe",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "47e08d2f265f4dda15b309fa67ba163b",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "c9a5225be07b4456e073014e1db2cafe",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "f79382e5342db6f38f4c07170589d62d",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "b2d5386301891813a790f1a19d442022",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "f79382e5342db6f38f4c07170589d62d",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "b2d5386301891813a790f1a19d442022",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "899eda04958efce6903b7dd2abe6c76f",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "e98d76fe2d0af7e775801998d8591340",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "e9d551c0bfbb330470b1e0784028e4d3",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "e98d76fe2d0af7e775801998d8591340",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "77f25ad3348eb212969725c0ec7ebec8",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "df8038790ce73124950dc443c1e972ad",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "f184798b7973343cdc42d16e05aa335f",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "2e3ed4ea52261efd17d0927b21cf4075",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "1a5c374cfdd07ccbb2f1e9a44b886131",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "66da72816faa1d87d0b3fe677c6eb9c8",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "fbef69c5fba9b7c94d37e233eb6c9642",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "c37e1dc4f95a8db0b5059dae0b5f5241",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "3403caf882f5efc1c5a62bf452994bd1",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "c046dcf69b1d3cb122869ee1f8df06d6",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "10f90e814342ba8553c193d8ce3221d1",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "2827cb64562372b70fe78c30cc4dbf1e",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "37e25714c6b19f5edc1135820edb0ef4",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "bacec90936f644c739eb9230b61c1528",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "51e044f01c1f783f2c88d8ba490370a6",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "01aff77ee4c84b06b54664e26aaed0cf",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "7430ba5eadfdd15310d0b140d5ef3f2b",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "de88562b3c51a56a5a89bc35a7888e58",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "74b6f29023a4a3e90b2233d77c0c38bb",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "78150d99f0e8dfd2f6785af4335b3f49",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "ccc2f72a1a12a3314f6ffea3ea20875c",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "bab389beb2e52641e5a7e5cf9a62bf69",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "a3de45a3355c610719a477077babf451",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "cf35da279c2ff3d4a8241e51199fea5d",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "8fcb6c2f3be27b2aadee60a436ee5c82",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "695c693d94935c99e3990c885b882091",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "38a69802cca5b4bc3a344777f12679bd",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "4e24b9c6b980844de8c482dd1034fb97",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "b5a129d83796c9e7015ab5e319d2c22f",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5",
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "b5a129d83796c9e7015ab5e319d2c22f",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "13405a4a16a71d073b6b3cabbbcd9666",
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "86b26dc84cc7fa7095e51cfcae759c0b",
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "13405a4a16a71d073b6b3cabbbcd9666",
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "86b26dc84cc7fa7095e51cfcae759c0b",
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "678e09ecd5da367ce290ca7318617b61",
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "a9cdc9dd029dabc6dfa5b61d33de7927",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "678e09ecd5da367ce290ca7318617b61",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "a9cdc9dd029dabc6dfa5b61d33de7927",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "4811585805942428ddb217917e4ad843",
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "d34c0a142e7d391a109a33ea3cc77c08",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "4eb48f28a678a7f2eae8c10d4d86b879",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "675e12e534ebec6cb9760b066c158747",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "5d9bb2f2835069e186418b1b02598168",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "7fa0439993cdd80f098a2cf37d4e6b31",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "1bac9e9d670f8dfac75398085820d039",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "9ecb0b936f758250417f7c014f89d683",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "912935f68da6dfca6312b3859c14ee27",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "aa9ca81cff1cd3135af6fa81cb16851a",
"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

@ -823,6 +823,7 @@
<w>dstent</w>
<w>dstfile</w>
<w>dstfin</w>
<w>dstfs</w>
<w>dstjson</w>
<w>dstlines</w>
<w>dstname</w>
@ -1085,6 +1086,7 @@
<w>flycheck</w>
<w>fmod</w>
<w>fname</w>
<w>fnamefilt</w>
<w>fnamefull</w>
<w>fnames</w>
<w>fnmatch</w>
@ -2669,6 +2671,7 @@
<w>shobs</w>
<w>shortname</w>
<w>shouldn</w>
<w>shouldnt</w>
<w>showbuffer</w>
<w>showpoints</w>
<w>showstats</w>
@ -2753,11 +2756,13 @@
<w>splitnumstr</w>
<w>spwd</w>
<w>squadcore</w>
<w>src's</w>
<w>srcabs</w>
<w>srcattr</w>
<w>srcdata</w>
<w>srcdir</w>
<w>srcfolder</w>
<w>srcfs</w>
<w>srcgrp</w>
<w>srcid</w>
<w>srcjson</w>
@ -2840,6 +2845,7 @@
<w>subdep</w>
<w>subdeps</w>
<w>subdirs</w>
<w>subdst</w>
<w>subfieldpath</w>
<w>subfolders</w>
<w>submpath</w>
@ -2852,6 +2858,7 @@
<w>subprocesses</w>
<w>subrepos</w>
<w>subsel</w>
<w>subsrc</w>
<w>subsys</w>
<w>subtypestr</w>
<w>subval</w>

View File

@ -1,5 +1,13 @@
### 1.7.28 (build 21289, api 8, 2023-08-31)
### 1.7.28 (build 21293, api 8, 2023-08-31)
- Added some high level functionality for copying and deleting feature-sets to
the `tools/spinoff` tool. For example, to create your own `poo` feature-set,
do `tools/spinoff fset-copy template_fs poo`. Then do `make update` and `make
cmake` to build and run the app, and from within it you should be able to do
`import bapoo` to get at your nice shiny poo feature-set. When you are done
playing, you can do `tools/spinoff fset-delete poo` to blow away any traces of
it.
### 1.7.27 (build 21282, api 8, 2023-08-30)
- Fixed a rare crash that could occur if the app shuts down while a background

View File

@ -512,6 +512,7 @@
<w>dstdir</w>
<w>dstdirfull</w>
<w>dstent</w>
<w>dstfs</w>
<w>dstnode</w>
<w>dstpath</w>
<w>dstr</w>
@ -662,6 +663,7 @@
<w>flopsy</w>
<w>flushhhhh</w>
<w>fname</w>
<w>fnamefilt</w>
<w>fnode</w>
<w>fnsu</w>
<w>fnumc</w>
@ -1619,9 +1621,11 @@
<w>spinups</w>
<w>spivak</w>
<w>spwd</w>
<w>src's</w>
<w>srcabs</w>
<w>srcattr</w>
<w>srcfolder</w>
<w>srcfs</w>
<w>srcgrp</w>
<w>srcid</w>
<w>srcname</w>
@ -1683,6 +1687,7 @@
<w>subargs</w>
<w>subc</w>
<w>subclsssing</w>
<w>subdst</w>
<w>subentities</w>
<w>subfieldpath</w>
<w>subitems</w>
@ -1691,6 +1696,7 @@
<w>subplatform</w>
<w>subscale</w>
<w>subscr</w>
<w>subsrc</w>
<w>subsys</w>
<w>subtypestr</w>
<w>successmsg</w>

View File

@ -109,6 +109,7 @@ from babase._apputils import (
is_browser_likely_available,
garbage_collect,
get_remote_app_name,
AppHealthMonitor,
)
from babase._cloud import CloudSubsystem
from babase._emptyappmode import EmptyAppMode
@ -176,6 +177,7 @@ __all__ = [
'app',
'App',
'AppConfig',
'AppHealthMonitor',
'AppIntent',
'AppIntentDefault',
'AppIntentExec',

View File

@ -13,6 +13,7 @@ from concurrent.futures import ThreadPoolExecutor
from functools import cached_property
from efro.call import tpartial
import _babase
from babase._language import LanguageSubsystem
from babase._plugin import PluginSubsystem

View File

@ -85,7 +85,9 @@ class MasterServerV1CallThread(threading.Thread):
# Tearing the app down while this is running can lead to
# rare crashes in LibSSL, so avoid that if at all possible.
babase.shutdown_suppress_begin()
if not babase.shutdown_suppress_begin():
# App is already shutting down, so we're a no-op.
return
try:
classic = babase.app.classic

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 = 21289
TARGET_BALLISTICA_BUILD = 21293
TARGET_BALLISTICA_VERSION = '1.7.28'
@ -461,11 +461,12 @@ def _modular_main() -> None:
# First baenv sets up things like Python paths the way the engine
# needs them, and then we import and run the engine.
#
# Below we're doing a slightly fancier version of that. Namely we do
# some processing of command line args to allow overriding of paths
# or running explicit commands or whatever else. Our goal is that
# this modular form of the app should be basically indistinguishable
# from the monolithic form when used from the command line.
# Below we're doing a slightly fancier version of that. Namely, we
# do some processing of command line args to allow overriding of
# paths or running explicit commands or whatever else. Our goal is
# that this modular form of the app should be basically
# indistinguishable from the monolithic form when used from the
# command line.
try:
# Take note that we're running via modular-main. The native

View File

@ -4,8 +4,12 @@
# ba_meta require api 8
# Package up various private bits (including stuff from our native
# module) into a nice clean public API.
from _batemplatefs import hello_again_world
from batemplatefs._subsystem import TemplateFsSubsystem
__all__ = [
'TemplateFsSubsystem',
'hello_again_world',
]

View File

@ -715,11 +715,33 @@ void BaseFeatureSet::DoPushObjCall(const PythonObjectSetBase* objset, int id,
}
auto BaseFeatureSet::IsAppStarted() const -> bool { return app_started_; }
void BaseFeatureSet::ShutdownSuppressBegin() { shutdown_suppress_count_++; }
auto BaseFeatureSet::ShutdownSuppressBegin() -> bool {
std::scoped_lock lock(shutdown_suppress_lock_);
if (!shutdown_suppress_disallowed_) {
return false;
}
shutdown_suppress_count_++;
return true;
}
void BaseFeatureSet::ShutdownSuppressEnd() {
std::scoped_lock lock(shutdown_suppress_lock_);
shutdown_suppress_count_--;
assert(shutdown_suppress_count_ >= 0);
}
auto BaseFeatureSet::ShutdownSuppressGetCount() -> int {
std::scoped_lock lock(shutdown_suppress_lock_);
return shutdown_suppress_count_;
}
void BaseFeatureSet::ShutdownSuppressDisallow() {
std::scoped_lock lock(shutdown_suppress_lock_);
assert(!shutdown_suppress_disallowed_);
shutdown_suppress_disallowed_ = true;
}
auto BaseFeatureSet::GetReturnValue() const -> int { return return_value(); }
} // namespace ballistica::base

View File

@ -3,6 +3,7 @@
#ifndef BALLISTICA_BASE_BASE_H_
#define BALLISTICA_BASE_BASE_H_
#include <mutex>
#include <set>
#include <string>
@ -690,9 +691,18 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
void DoPushObjCall(const PythonObjectSetBase* objset, int id,
const std::string& arg) override;
void OnReachedEndOfBaBaseImport();
void ShutdownSuppressBegin();
/// Begin a shutdown-suppressing operation. Returns true if the operation
/// can proceed; otherwise shutdown has already begun and the operation
/// should be aborted.
auto ShutdownSuppressBegin() -> bool;
/// End a shutddown-suppressing operation. Should only be called after a
/// successful begin.
void ShutdownSuppressEnd();
auto shutdown_suppress_count() const { return shutdown_suppress_count_; }
auto ShutdownSuppressGetCount() -> int;
void ShutdownSuppressDisallow();
/// Called in the logic thread once our screen is up and assets are
/// loading.
@ -756,6 +766,8 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
StressTest* stress_test_;
std::string console_startup_messages_;
std::mutex shutdown_suppress_lock_;
bool shutdown_suppress_disallowed_;
int shutdown_suppress_count_{};
bool tried_importing_plus_{};
bool tried_importing_classic_{};

View File

@ -226,7 +226,10 @@ void Logic::OnAppShutdown() {
// Nuke the app from orbit if we get stuck while shutting down.
g_core->StartSuicideTimer("shutdown", 10000);
// Let our subsystems know we're shutting down.
// Tell base to disallow shutdown-suppressors from here on out.
g_base->ShutdownSuppressDisallow();
// Let our logic thread subsystems know we're shutting down.
// 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

View File

@ -1495,8 +1495,12 @@ static PyMethodDef PyGetImmediateReturnCodeDef = {
static auto PyShutdownSuppressBegin(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
assert(g_base);
g_base->ShutdownSuppressBegin();
Py_RETURN_NONE;
auto val = g_base->ShutdownSuppressBegin();
if (val) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
BA_PYTHON_CATCH;
}
@ -1505,7 +1509,7 @@ static PyMethodDef PyShutdownSuppressBeginDef = {
(PyCFunction)PyShutdownSuppressBegin, // method
METH_NOARGS, // flags
"shutdown_suppress_begin() -> None\n"
"shutdown_suppress_begin() -> bool\n"
"\n"
"(internal)\n",
};
@ -1536,7 +1540,7 @@ static PyMethodDef PyShutdownSuppressEndDef = {
static auto PyShutdownSuppressCount(PyObject* self) -> PyObject* {
BA_PYTHON_TRY;
assert(g_base);
return PyLong_FromLong(g_base->shutdown_suppress_count());
return PyLong_FromLong(g_base->ShutdownSuppressGetCount());
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}

View File

@ -15,9 +15,8 @@ CoreFeatureSet* g_core{};
BaseSoftInterface* g_base_soft{};
auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
// In monolithic builds we can accept an explicit core-config the first
// time we're imported. In this case, Python is not even spun up yet so
// it can influence even that.
// In monolithic builds, we accept an explicit core-config the first time
// we're imported. It is fully up to the caller to build the config.
if (g_buildconfig.monolithic_build()) {
if (config != nullptr) {
if (g_core != nullptr) {
@ -29,17 +28,17 @@ auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
DoImport(*config);
}
} else {
// No config passed; use a default.
// If no config is passed, use a default. If the user wants env vars
// or anything else factored in, they should do so themselves in the
// config they pass (CoreConfig::ForEnvVars(), etc.).
if (g_core == nullptr) {
DoImport({});
}
}
} else {
// In modular builds we autogenerate a CoreConfig that takes into
// account only env-vars (or env-vars plus Python args if we're being
// run via the baenv script). In this case, Python is already spun up
// and baenv already handled any Python environment stuff so we have
// less to do.
// In modular builds, we generate a CoreConfig *after* Python is spun
// up, implicitly using Python's sys args and/or env vars when
// applicable.
if (config != nullptr) {
FatalError("CoreConfig can't be explicitly passed in modular builds.");
}
@ -57,6 +56,7 @@ auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
DoImport(CoreConfig::ForArgsAndEnvVars(static_cast<int>(argv.size()),
argv.data()));
} else {
// Not using Python sys args but we still want to process env vars.
DoImport(CoreConfig::ForEnvVars());
}
}

View File

@ -24,27 +24,26 @@ class CoreFeatureSet;
class BaseSoftInterface;
// Our feature-set's globals.
// Feature-sets should NEVER directly access globals in another feature-set's
// namespace. All functionality we need from other feature-sets should be
// imported into globals in our own namespace. Generally we do this when we
// are initially imported (just as regular Python modules do).
//
// Feature-sets should NEVER directly access globals in another
// feature-set's namespace. All functionality we need from other
// feature-sets should be imported into globals in our own namespace.
// Generally we do this when we are initially imported (just as regular
// Python modules do).
// Our pointer to our own feature-set.
extern CoreFeatureSet* g_core;
// We don't require the base feature-set but can use it if present.
// Base will supply us with this pointer if/when it spins up.
// So we must never assume this pointer is valid and must check for it
// with each use.
// We don't require the base feature-set but can use it if present. Base
// will supply us with this pointer if/when it spins up. So we must never
// assume this pointer is valid and must check for it with each use.
extern BaseSoftInterface* g_base_soft;
/// Platform-agnostic global state for our overall system.
/// This gets created whenever we are used in any capacity, even if
/// we don't create/run an app.
/// Ideally most things here should be migrated to more specific
/// subsystems.
/// Core engine functionality.
class CoreFeatureSet {
public:
/// Import the core feature set. A core-config can be passed ONLY
/// in monolithic builds when it is guaranteed that the Import will be
/// Import the core feature set. A core-config can be passed ONLY in
/// monolithic builds when it is guaranteed that the Import will be
/// allocating the CoreFeatureSet singleton.
static auto Import(const CoreConfig* config = nullptr) -> CoreFeatureSet*;
@ -76,32 +75,35 @@ class CoreFeatureSet {
auto HeadlessMode() -> bool;
/// Return current app-time in milliseconds.
///
/// App-time is basically the total time that the engine has been actively
/// running. (The 'App' here is a slight misnomer). It will stop progressing
/// while the app is suspended and will never go backwards.
/// running. (The 'App' here is a slight misnomer). It will stop
/// progressing while the app is suspended and will never go backwards.
auto GetAppTimeMillisecs() -> millisecs_t;
/// Return current app-time in microseconds.
///
/// App-time is basically the total time that the engine has been actively
/// running. (The 'App' here is a slight misnomer). It will stop progressing
/// while the app is suspended and will never go backwards.
/// running. (The 'App' here is a slight misnomer). It will stop
/// progressing while the app is suspended and will never go backwards.
auto GetAppTimeMicrosecs() -> microsecs_t;
/// Return current app-time in seconds.
///
/// App-time is basically the total time that the engine has been actively
/// running. (The 'App' here is a slight misnomer). It will stop progressing
/// while the app is suspended and will never go backwards.
/// running. (The 'App' here is a slight misnomer). It will stop
/// progressing while the app is suspended and will never go backwards.
auto GetAppTimeSeconds() -> double;
/// Are we in the thread the main event loop is running on?
/// Generally this is the thread that runs graphics and os event processing.
/// Are we in the thread the main event loop is running on? Generally this
/// is the thread that runs graphics and os event processing.
auto InMainThread() -> bool;
/// Log a boot-related message (only if core_config.lifecycle_log is true).
void LifecycleLog(const char* msg, double offset_seconds = 0.0);
/// Base path of build src dir so we can attempt to remove it from
/// any source file paths we print.
/// Base path of build src dir so we can attempt to remove it from any
/// source file paths we print.
auto build_src_dir() const { return build_src_dir_; }
const auto& legacy_user_agent_string() const {
@ -113,8 +115,8 @@ class CoreFeatureSet {
}
/// Return true if baenv values have been locked in: python paths, log
/// handling, etc. Early-running code may wish to explicitly avoid making log
/// calls until this condition is met to ensure predictable behavior.
/// handling, etc. Early-running code may wish to explicitly avoid making
/// log calls until this condition is met to ensure predictable behavior.
auto have_ba_env_vals() const { return have_ba_env_vals_; }
/// Return the directory where the app expects to find its bundled Python
@ -138,8 +140,8 @@ class CoreFeatureSet {
/// Return the directory where bundled 3rd party Python files live.
auto GetSitePythonDirectory() -> std::optional<std::string>;
// Are we using a non-standard app python dir (such as a 'sys' dir within a
// user-python-dir).
// Are we using a non-standard app python dir (such as a 'sys' dir within
// a user-python-dir).
auto using_custom_app_python_dir() const {
return using_custom_app_python_dir_;
}
@ -165,7 +167,6 @@ class CoreFeatureSet {
bool reset_vr_orientation{};
bool user_ran_commands{};
std::thread::id main_thread_id{};
bool vr_mode;
std::mutex thread_name_map_mutex;
std::unordered_map<std::thread::id, std::string> thread_name_map;
@ -177,11 +178,11 @@ class CoreFeatureSet {
#endif
private:
explicit CoreFeatureSet(CoreConfig config);
static void DoImport(const CoreConfig& config);
static auto CalcBuildSrcDir() -> std::string;
void RunSanityChecks();
static void DoImport(const CoreConfig& config);
void UpdateAppTime();
explicit CoreFeatureSet(CoreConfig config);
void PostInit();
bool tried_importing_base_{};
EventLoop* main_event_loop_{};

View File

@ -27,8 +27,9 @@
// ------------------------- PLATFORM SELECTION --------------------------------
// This ugly chunk of macros simply pulls in the correct platform class header
// for each platform and defines the actual class g_core->platform will be.
// This ugly chunk of macros simply pulls in the correct platform class
// header for each platform and defines the actual class g_core->platform
// will be.
// Android ---------------------------------------------------------------------
@ -85,6 +86,7 @@
// A call that can be used by custom built native libraries (Python, etc.)
// to forward along debug messages to us.
//
// FIXME: Reconcile this with our existing C++ version. This one does not
// require the engine to be spun up so it better suited for things like
// debugging native libs.

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

View File

@ -1,4 +1,4 @@
# Template Feature Set
# Template Fs Feature Set
This is an empty feature-set for use as reference or as a starting point when
implementing new ones.

View File

@ -9,16 +9,18 @@
namespace ballistica::template_fs {
// Declare a plain c PyInit_XXX function for our Python module;
// this is how Python inits our binary module (and by extension, our
// entire feature-set).
// Declare a plain C PyInit_XXX function for our Python module. This is how
// Python inits our binary module (and by extension, our entire
// feature-set).
extern "C" auto PyInit__batemplatefs() -> PyObject* {
auto* builder = new PythonModuleBuilder(
"_batemplatefs",
// Native methods to add.
// Our native methods.
{PythonMethodsTemplateFs::GetMethods()},
// Our module exec. Here we can add classes, import other modules,
// or whatever else (same as a regular Python script module).
// Our module exec. Here we can add classes, import other modules, or
// whatever else (same as a regular Python script module).
[](PyObject* module) -> int {
BA_PYTHON_TRY;
TemplateFsFeatureSet::OnModuleExec(module);
@ -38,14 +40,16 @@ void TemplateFsPython::ImportPythonObjs() {
void TemplateFsPython::HelloWorld() {
// Hold the GIL throughout this call so we can run in any thread.
// Alternately we could limit this function to the logic thread
// which always holds the GIL. In that case we'd want to
// stick a BA_PRECONDITION(InLogicThread()) here to be sure.
// Alternately, we could limit this function to the logic thread which
// always holds the GIL. In that case we'd want to stick a
// BA_PRECONDITION(InLogicThread()) here to so we'd raise an Exception if
// someone called us from another thread.
auto gil{Python::ScopedInterpreterLock()};
// Run the Python callable we grabbed. This will simply print any
// errors, but we could disable that print and look at the call
// results if any logic depended on this code running successfully.
// Run the Python callable we grabbed in our binding code. By default,
// this Call() will simply print any errors, but we could disable that
// print and look at the call results if any logic depended on this code
// running successfully.
objs_.Get(ObjID::kHelloWorldCall).Call();
}

View File

@ -9,13 +9,17 @@ import sys
import subprocess
from enum import Enum
from pathlib import Path
from typing import assert_never
from typing import assert_never, TYPE_CHECKING
from efro.error import CleanError
from efro.terminal import Clr
from efrotools import replace_exact
from batools.spinoff._context import SpinoffContext
if TYPE_CHECKING:
from batools.featureset import FeatureSet
class Command(Enum):
"""Our top level commands."""
@ -32,6 +36,8 @@ class Command(Enum):
FEATURESETS = 'featuresets'
CREATE = 'create'
ADD_SUBMODULE_PARENT = 'add-submodule-parent'
FEATURE_SET_COPY = 'fset-copy'
FEATURE_SET_DELETE = 'fset-delete'
def spinoff_main() -> None:
@ -101,6 +107,10 @@ def _main() -> None:
public = getprojectconfig(Path(dst_root))['public']
_do_add_submodule_parent(dst_root, is_new=False, public=public)
elif cmd is Command.FEATURE_SET_COPY:
_do_featureset_copy()
elif cmd is Command.FEATURE_SET_DELETE:
_do_featureset_delete()
else:
assert_never(cmd)
@ -250,6 +260,251 @@ def _do_featuresets(dst_root: str) -> None:
print(f' {Clr.BLU}{fset.name}{Clr.RST}')
def _fset_paths() -> list[str]:
"""Given a feature-set, return all paths associated with it."""
return [
'config/featuresets/featureset_$(NAME).py',
'src/assets/ba_data/python/$(NAME_PY_PKG)',
'src/ballistica/$(NAME)',
'src/meta/$(NAME_PY_PKG_META)',
]
def _filter_fset_path(path: str, fset: FeatureSet) -> str:
return (
path.replace('$(NAME)', fset.name)
.replace('$(NAME_PY_PKG)', fset.name_python_package)
.replace('$(NAME_PY_PKG_META)', fset.name_python_package_meta)
)
def _do_featureset_delete() -> None:
from batools.featureset import FeatureSet
args = sys.argv[2:]
if len(args) != 1:
raise CleanError('Expected a featureset name.')
name = args[0]
# Just make a theoretical new featureset in case only parts of it
# exist. (custom name formatting shouldnt matter here anyway)
fset = FeatureSet(name)
if not os.path.exists('config/featuresets'):
raise CleanError('Cannot run from this directory.')
paths_to_delete: list[str] = []
for path in _fset_paths():
paths_to_delete.append(_filter_fset_path(path, fset))
print(
'\n' + '' * 80 + f'\n{Clr.BLD}Deleting feature-set{Clr.RST}'
f' {Clr.SMAG}{Clr.BLD}{name}{Clr.RST}{Clr.BLD}...{Clr.RST}\n'
+ '' * 80
+ '\n'
)
found_something = False
for path in paths_to_delete:
if os.path.exists(path):
found_something = True
print(f' Deleting {Clr.MAG}{path}{Clr.RST}')
subprocess.run(['rm', '-rf', path], check=True)
if not found_something:
print(
f' {Clr.WHT}No feature-set components found;'
f' nothing to be done.{Clr.RST}'
)
print(
f"\n{Clr.GRN}{Clr.BLD}Job's done!{Clr.RST}\n"
f'{Clr.BLD}Next, run'
f' {Clr.BLU}`make update`{Clr.RST}{Clr.BLD} to update project'
f' files to reflect these changes.{Clr.RST}'
)
def _do_featureset_copy() -> None:
# pylint: disable=too-many-locals
from efrotools import extract_flag
from batools.featureset import FeatureSet
args = sys.argv[2:]
force = extract_flag(args, '--force')
if len(args) != 2:
raise CleanError('Expected a src and dst featureset name.')
src = args[0]
dst = args[1]
if not os.path.exists('config/featuresets'):
raise CleanError('Cannot run from this directory.')
# This will make sure both feature-set names are valid and give us
# name variations. Load src from the project to pick up custom title
# variations/etc.
fsets = {f.name: f for f in FeatureSet.get_all_for_project('.')}
if src not in fsets:
raise CleanError('src feature-set {src} not found.')
srcfs = fsets[src]
# Just go with defaults for dst. Note that this means any custom
# title forms in src's config script will get filtered to be setting
# the default form of dst, which is redundant. Maybe we could filter that
# out.
dstfs = FeatureSet(dst)
# Make sure src *does* exist.
if not os.path.exists(f'config/featuresets/featureset_{src}.py'):
raise CleanError(f"Src feature-set '{src}' not found.")
# Make sure dst does *not* exist (unless we're forcing).
if os.path.exists(f'config/featuresets/featureset_{dst}.py') and not force:
raise CleanError(
f"Dst feature-set '{dst}' already exists."
' Use --force to blow it away.'
)
paths_to_copy: list[tuple[str, str]] = []
for path in _fset_paths():
paths_to_copy.append(
(_filter_fset_path(path, srcfs), _filter_fset_path(path, dstfs))
)
# Replace variations of our name. Note that we don't have to include
# stuff like name_python_package_meta here because that is covered
# by our base name replacement. Also note that we include upper()
# for C/C++ header #ifndefs.
subs = [
(srcfs.name, dstfs.name),
(srcfs.name_compact, dstfs.name_compact),
(srcfs.name_title, dstfs.name_title),
(srcfs.name_camel, dstfs.name_camel),
(srcfs.name.upper(), dstfs.name.upper()),
]
# Sanity check: we don't currently support renaming subdirs, so error
# if that would happen.
for srcpath, _dstpath in paths_to_copy:
for root, dirs, _fnames in os.walk(srcpath):
for dname in dirs:
if any(sub[0] in dname for sub in subs):
raise CleanError(
'Directory name filtering is not supported'
f" (would filter '{root}/{dname}')."
)
# ------------------------------------------------------------------------
# Ok, at this point we get started working and assume things will succeed.
# If anything fails at this point we should add a pre-check for it above.
print(
'\n' + '' * 80 + f'\n{Clr.BLD}Copying feature-set{Clr.RST}'
f' {Clr.SMAG}{Clr.BLD}{src}{Clr.RST}'
f' {Clr.BLD}to{Clr.RST}'
f' {Clr.SMAG}{Clr.BLD}{dst}{Clr.RST}'
f'{Clr.BLD}...{Clr.RST}\n' + '' * 80
)
print(f'\n{Clr.BLD}Will filter the following text:{Clr.RST}')
for subsrc, subdst in subs:
print(
f' {Clr.MAG}{subsrc}{Clr.RST}'
f' {Clr.BLD}->{Clr.RST} {Clr.MAG}{subdst}{Clr.RST}'
)
print(f'\n{Clr.BLD}Copying/filtering files...{Clr.RST}')
for srcpath, dstpath in paths_to_copy:
_do_featureset_copy_dir(srcpath, dstpath, subs, force)
print(
f"\n{Clr.GRN}{Clr.BLD}Job's done!{Clr.RST}\n"
f'{Clr.BLD}Next, run'
f' {Clr.BLU}`make update`{Clr.RST}{Clr.BLD} to update project'
f' files to reflect these changes.{Clr.RST}'
)
def _do_featureset_copy_dir(
srcpath: str, dstpath: str, subs: list[tuple[str, str]], force: bool
) -> None:
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# This feature-set might not have this component. No biggie.
if not os.path.exists(srcpath):
return
if force:
subprocess.run(['rm', '-rf', dstpath], check=True)
if not os.path.exists(srcpath):
raise CleanError(f'src path {srcpath} is not a dir.')
if os.path.exists(dstpath):
raise CleanError(f'dst path {srcpath} already exists.')
filtered_exts = ['.cc', '.h', '.py', '.md', '.inc']
# Eww; reinventing the wheel here; should tap into existing
# spinoff logic or something.
cruft_names = ['.DS_Store', 'mgen', '_mgen']
# We currently just copy the full dir and then rename/filter
# individual files. If we need to filter subdir names at some point
# we'll need fancier code.
subprocess.run(['cp', '-r', srcpath, dstpath], check=True)
for root, dnames, fnames in os.walk(dstpath, topdown=True):
for dname in dnames:
if dname in cruft_names:
# Prevent us from recursing into it and blow it away.
dnames.remove(dname)
subprocess.run(
['rm', '-rf', os.path.join(root, dname)], check=True
)
for fname in fnames:
if fname in cruft_names:
os.unlink(os.path.join(root, fname))
continue
fnamefilt = fname
for subsrc, subdst in subs:
fnamefilt = fnamefilt.replace(subsrc, subdst)
if fnamefilt != fname:
subprocess.run(
[
'mv',
os.path.join(root, fname),
os.path.join(root, fnamefilt),
],
check=True,
)
# Now filter contents.
if not any(fname.endswith(ext) for ext in filtered_exts):
print(
f'{Clr.YLW}WARNING:'
f' not filtering file with unrecognized extension:'
f" '{fname}'{Clr.RST}"
)
continue
with open(
os.path.join(root, fnamefilt), encoding='utf-8'
) as infile:
contents = infile.read()
for subsrc, subdst in subs:
contents = contents.replace(subsrc, subdst)
with open(
os.path.join(root, fnamefilt), 'w', encoding='utf-8'
) as outfile:
outfile.write(contents)
print(
f' {Clr.MAG}{srcpath}{Clr.RST} {Clr.BLD}->{Clr.RST}'
f' {Clr.MAG}{dstpath}{Clr.RST}'
)
def _do_override(src_root: str | None, dst_root: str) -> None:
if src_root is None:
raise CleanError('This only works on dst projects.')
@ -364,7 +619,14 @@ def _print_available_commands() -> None:
' The same can be\n'
' achieved by passing --submodule-parent to'
' the \'create\'\n'
' command.'
' command.\n'
f' {bgn}fset-copy [src, dst]{end} Copy feature-set src to dst.'
' Replaces variations of src\n'
' feature-set name with dst equivalent,'
' though may need\n'
' some manual correction afterwards to be'
' functional.\n'
f' {bgn}fset-delete [name]{end} Delete a feature-set.'
),
file=sys.stderr,
)