diff --git a/.efrocachemap b/.efrocachemap index a0988d5d..f46eed85 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -3932,40 +3932,40 @@ "assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450", "assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e", "assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f", - "build/prefab/full/linux_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/77/45/69456cef2c9fddd5bb6a3497972b", - "build/prefab/full/linux_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/87/6d/1bc0e1c667216e278e7c95050955", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f6/b1/713661602c7580e060a5b2f5391c", - "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a4/2c/526bd742d1a1763d500bbfdfd763", - "build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/30/f1/57680bd744ac6825dc44641c7f3f", - "build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/71/5a/738514f3af01ecf3c806239e22e6", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/26/2145ff72ccd4f408a27da6b42b82", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/34/e1/9dc07498dd96f73540290c884287", - "build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e5/99/22214c0b91bcc3298568c87b6487", - "build/prefab/full/mac_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/75/ae/0d37c72901fdfb64e8104aa1406f", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cb/24/c5bb95114f793bc857504446a59e", - "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/53/0e/6725c8e8fa2c25e431e10b40384d", - "build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/06/5e/694a00eb244e4925a1c3f74b5f02", - "build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/32/f1/3a137d806dff7845d7e2dc766452", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f2/c6/b97c5eeea5307d0c679a9d2fa869", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/76/80/08157eac8b97c0d68802f90120f9", - "build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/c8/88/996fd4b6e10df4afbd23263da36f", - "build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/27/98/b2fff1ad82e5cbc25eac397e4ae6", - "build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/c1/69/62108258025dea6f8e7488e0e802", - "build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/3d/4d/b6cee501623ef5d78fb15bc87215", - "build/prefab/lib/linux_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b8/55/25ff15308d89838699e158024f9c", - "build/prefab/lib/linux_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/dd/84/13497d8139e3b6095a87940188af", - "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b5/de/6e368b0f405c9fbed0e9e6ab0e6a", - "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/86/f0/6296ed8275094c2c1c96bbe98b56", - "build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d9/7c/f166ddf0d1720ccea1720a1c2965", - "build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/75/b3/5cad6cf47697b6f03bea6369ef5b", - "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d5/3a/fa01e178b6da2879864b50252c98", - "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/48/4d/a3d1385420e5866559c210c584f1", - "build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6e/3e/1f7b71a7639703bb13d71fb36ae9", - "build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8e/c1/373ad978ad9807f64baf6215a230", - "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8a/d6/17652e3a94201b26b228d2fc53df", - "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7a/36/3267309a349385e52af77a2738a1", - "build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b8/d1/0318bfa712c7e6ef0d46c9b25033", - "build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/58/5f/1f0121bd900ad5345e9a27cd6198", - "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/be/53/9de41660d67305ac5669141e557c", - "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/27/d6/b5d5e1a56aaa00acf174dc609b59" + "build/prefab/full/linux_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/27/04/dcdd63a27fa11e8dc5bb02dc8209", + "build/prefab/full/linux_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ab/0d/18fc7018dd73403b6e6d8739808d", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f8/74/457bda7850348dbcdd235cfa9dcb", + "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f7/a3/26eaba91b4384e2838cadad11ff6", + "build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ef/80/64406ad4b21b8c75c3cf7dba3c98", + "build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/bc/d4/5efe586e439de930ccbba8869c06", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7c/65/b95d5b30731ed29e04cb10572fe5", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8e/7f/e179cf95dd54e437bfd22961afd7", + "build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/88/73/911011e52f90d326ddf9cea03c84", + "build/prefab/full/mac_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4b/21/08678761775f2207e2f62994f79b", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/94/0a/baa2efe432186b7b3c2c20883aed", + "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/95/6668eb64a8c30470c223f217baaf", + "build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/64/34/675c88ad196cbebdb0c19b51af41", + "build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/56/2d/e58c62aecbf09c9d68be9fb4ef39", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/2f/f3faa8cc336fddf54c55b9b35e7d", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c0/b7/26d1c7b5a21cae8dc6911d02c5db", + "build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/1d/5e/ef2468327ef8ec8a0023219e251c", + "build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3b/2a/f7f7ba687d28f429e416472c0c85", + "build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/bc/a1/703f357d2248c8b5674fa8bc3b8a", + "build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/82/c9/a5811e1a80394e227366778380c1", + "build/prefab/lib/linux_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9f/e1/17ec8710e6f9354dec40b134d963", + "build/prefab/lib/linux_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/02/90/d56f5d876b4d128f4c52a2c2ab01", + "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/25/0e/d813586a741bf4d15623899d5f74", + "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e2/dc/3164ed18198ff5021746a5e2e3a6", + "build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0c/be/6c258d57bf30c94b3dc7a03281e1", + "build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bd/f9/3b68d1abda8ba917f7084a675009", + "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4e/26/0f15c8533717ffeeae0b388546c2", + "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fb/89/e6c72ccad50cc3c0a34c4764d72c", + "build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/79/38/7fed4d2f7f8e158c44939ca6f09f", + "build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1b/cd/1643ee0514a1fc57071b673c283e", + "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a0/2d/413df7c810b283fa6d67b41e1fbe", + "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ff/e9/8eba3a5244b95e17887568fa3540", + "build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f7/4e/e98d85cbc5eeb67d916493776e53", + "build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a7/53/c9f849fc8985b5dbad0044b3e967", + "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/17/ea/1fd0bcc743cc452c898d04bf9da2", + "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7f/14/bfe45f3ce7a374810f5d91204c80" } \ No newline at end of file diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index dd496b3e..1d888e76 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -2048,6 +2048,7 @@ stickman storable storagename + storecmd storedhash storeitemui storename diff --git a/ballisticacore-cmake/.idea/dictionaries/ericf.xml b/ballisticacore-cmake/.idea/dictionaries/ericf.xml index 98ee9df9..950a1e42 100644 --- a/ballisticacore-cmake/.idea/dictionaries/ericf.xml +++ b/ballisticacore-cmake/.idea/dictionaries/ericf.xml @@ -895,6 +895,7 @@ stephane stepnum stepsize + storecmd strcasecmp strchr strcpy diff --git a/docs/ba_module.md b/docs/ba_module.md index 8866ae06..51583232 100644 --- a/docs/ba_module.md +++ b/docs/ba_module.md @@ -1,5 +1,5 @@ -

last updated on 2021-04-15 for Ballistica version 1.6.0 build 20338

+

last updated on 2021-04-15 for Ballistica version 1.6.0 build 20339

This page documents the Python classes and functions in the 'ba' module, which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please let me know. Happy modding!


diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc index 000d1378..314b77a0 100644 --- a/src/ballistica/ballistica.cc +++ b/src/ballistica/ballistica.cc @@ -21,7 +21,7 @@ namespace ballistica { // These are set automatically via script; don't change here. -const int kAppBuildNumber = 20339; +const int kAppBuildNumber = 20340; const char* kAppVersion = "1.6.0"; // Our standalone globals. diff --git a/src/ballistica/python/python.cc b/src/ballistica/python/python.cc index 2c096a68..6514319c 100644 --- a/src/ballistica/python/python.cc +++ b/src/ballistica/python/python.cc @@ -947,7 +947,9 @@ void Python::Reset(bool do_init) { + std::string(ver)); } - StoreObj(ObjID::kEmptyTuple, PyTuple_New(0)); + // Create a dict for execing our bootstrap code in so + // we don't pollute the __main__ namespace. + auto bootstrap_context{PythonRef(PyDict_New(), PythonRef::kSteal)}; // Get the app up and running. // Run a few core bootstrappy things first: @@ -956,21 +958,17 @@ void Python::Reset(bool do_init) { // - import and instantiate our app-state class #include "generated/ballistica/bootstrap.inc" - int result = PyRun_SimpleString(bootstrap_code); - if (result != 0) { + PyObject* result = + PyRun_String(bootstrap_code, Py_file_input, bootstrap_context.get(), + bootstrap_context.get()); + if (result == nullptr) { PyErr_PrintEx(0); // Throw a simple exception so we don't get a stack trace. throw std::logic_error( "Error in ba Python bootstrapping. See log for details."); } - PyObject* appstate = PythonCommand("app_state", "") - .RunReturnObj(false, nullptr); - if (appstate == nullptr) { - throw Exception("Unable to get value: '" + std::string("app_state") - + "'."); - } - StoreObj(ObjID::kApp, appstate); + Py_DECREF(result); // Import and grab all the Python stuff we use. #include "generated/ballistica/binding.inc" diff --git a/src/generated_src/ballistica/binding.py b/src/generated_src/ballistica/binding.py index 1102dcd4..62e22f3b 100644 --- a/src/generated_src/ballistica/binding.py +++ b/src/generated_src/ballistica/binding.py @@ -4,25 +4,36 @@ # Run make update to update the project after editing this.. # pylint: disable=missing-module-docstring, missing-function-docstring # pylint: disable=line-too-long -def get_binding_values() -> object: - from ba import _hooks - import _ba - import json - import copy - import ba - from ba import _language - from ba import _music - from ba import _input - from ba import _apputils - from ba import _account - from ba import _dependency - from ba import _enums - from ba import _player - # FIXME: There should be no bastd in here; - # should pull in bases from ba which get overridden by bastd (or other). - from bastd.ui.onscreenkeyboard import OnScreenKeyboardWindow - from bastd.ui import party +from __future__ import annotations + +import json +import copy +from typing import TYPE_CHECKING + +import ba +from ba import _language +from ba import _music +from ba import _input +from ba import _apputils +from ba import _dependency +from ba import _enums +from ba import _player +from ba import _hooks +import _ba + +# FIXME: There should be no bastd in here; +# should pull in bases from ba which get overridden by bastd (or other). +from bastd.ui.onscreenkeyboard import OnScreenKeyboardWindow +from bastd.ui import party + +if TYPE_CHECKING: + from typing import Tuple, Any + + +def get_binding_values() -> Tuple[Any, ...]: return ( + ba.app, # kApp + tuple(), # kEmptyTuple _ba.client_info_query_response, # kClientInfoQueryResponseCall _hooks.reset_to_main_menu, # kResetToMainMenuCall _hooks.set_config_fullscreen_on, # kSetConfigFullscreenOnCall diff --git a/src/generated_src/ballistica/bootstrap.py b/src/generated_src/ballistica/bootstrap.py index 50cc1def..a7782ea3 100644 --- a/src/generated_src/ballistica/bootstrap.py +++ b/src/generated_src/ballistica/bootstrap.py @@ -3,145 +3,136 @@ from __future__ import annotations +import sys +import signal +import threading from typing import TYPE_CHECKING +import _ba + if TYPE_CHECKING: from typing import Any, TextIO, Callable, List -def _ballistica_bootstrap() -> object: - import sys - import signal - import _ba - import threading +class _BAConsoleRedirect: - class _BAConsoleRedirect: + def __init__(self, original: TextIO, call: Callable[[str], None]) -> None: + self._lock = threading.Lock() + self._linebits: List[str] = [] + self._original = original + self._call = call + self._pending_ship = False - def __init__(self, original: TextIO, call: Callable[[str], - None]) -> None: - self._lock = threading.Lock() - self._linebits: List[str] = [] - self._original = original - self._call = call - self._pending_ship = False + def write(self, sval: Any) -> None: + """Override standard stdout write.""" - def write(self, sval: Any) -> None: - """Override standard stdout write.""" + self._call(sval) - self._call(sval) + # Now do logging: + # Add it to our accumulated line. + # If the message ends in a newline, we can ship it + # immediately as a log entry. Otherwise, schedule a ship + # next cycle (if it hasn't yet at that point) so that we + # can accumulate subsequent prints. + # (so stuff like print('foo', 123, 'bar') will ship as one entry) + with self._lock: + self._linebits.append(sval) + if sval.endswith('\n'): + self._shiplog() + else: + _ba.pushcall(self._shiplog, + from_other_thread=True, + suppress_other_thread_warning=True) - # Now do logging: - # Add it to our accumulated line. - # If the message ends in a newline, we can ship it - # immediately as a log entry. Otherwise, schedule a ship - # next cycle (if it hasn't yet at that point) so that we - # can accumulate subsequent prints. - # (so stuff like print('foo', 123, 'bar') will ship as one entry) - with self._lock: - self._linebits.append(sval) - if sval.endswith('\n'): - self._shiplog() - else: - _ba.pushcall(self._shiplog, - from_other_thread=True, - suppress_other_thread_warning=True) + def _shiplog(self) -> None: + with self._lock: + line = ''.join(self._linebits) + if not line: + return + self._linebits = [] - def _shiplog(self) -> None: - with self._lock: - line = ''.join(self._linebits) - if not line: - return - self._linebits = [] + # Log messages aren't expected to have trailing newlines. + if line.endswith('\n'): + line = line[:-1] + _ba.log(line, to_stdout=False) - # Log messages aren't expected to have trailing newlines. - if line.endswith('\n'): - line = line[:-1] - _ba.log(line, to_stdout=False) + def flush(self) -> None: + """Flush the file.""" + self._original.flush() - def flush(self) -> None: - """Flush the file.""" - self._original.flush() - - def isatty(self) -> bool: - """Are we a terminal?""" - return self._original.isatty() - - sys.stdout = _BAConsoleRedirect( # type: ignore - sys.stdout, _ba.print_stdout) - sys.stderr = _BAConsoleRedirect( # type: ignore - sys.stderr, _ba.print_stderr) - - # Let's lookup mods first (so users can do whatever they want). - # and then our bundled scripts last (don't want to encourage dists - # overriding proper python functionality) - sys.path.insert(0, _ba.env()['python_directory_user']) - sys.path.append(_ba.env()['python_directory_app']) - sys.path.append(_ba.env()['python_directory_app_site']) - - # Tell Python to not handle SIGINT itself (it normally generates - # KeyboardInterrupts which make a mess; we want to do a simple - # clean exit). We capture interrupts per-platform in the C++ layer. - # I tried creating a handler in Python but it seemed to often have - # a delay of up to a second before getting called. (not a huge deal - # but I'm picky). - signal.signal(signal.SIGINT, signal.SIG_DFL) # Do default handling. - - # ..though it turns out we need to set up our C signal handling AFTER - # we've told Python to disable its own; otherwise (on Mac at least) it - # wipes out our existing C handler. - _ba.setup_sigint() - - # Sanity check: we should always be run in UTF-8 mode. - if sys.flags.utf8_mode != 1: - print('ERROR: Python\'s UTF-8 mode is not set.' - ' This will likely result in errors.') - - # FIXME: I think we should init Python in the main thread, which should - # also avoid these issues. (and also might help us play better with - # Python debuggers?) - - # Gloriously hacky workaround here: - # Our 'main' Python thread is the game thread (not the app's main - # thread) which means it has a small stack compared to the main - # thread (at least on apple). Sadly it turns out this causes the - # debug build of Python to blow its stack immediately when doing - # some big imports. - # Normally we'd just give the game thread the same stack size as - # the main thread and that'd be the end of it. However - # we're using std::threads which it turns out have no way to set - # the stack size (as of fall '19). Grumble. - # - # However python threads *can* take custom stack sizes. - # (and it appears they might use the main thread's by default?..) - # ...so as a workaround in the debug version, we can run problematic - # heavy imports here in another thread and all is well. - # If we ever see stack overflows in our release build we'll have - # to take more drastic measures like switching from std::threads - # to pthreads. - - if _ba.env()['debug_build']: - - def _thread_func() -> None: - # pylint: disable=unused-import - import json - import urllib.request - - testthread = threading.Thread(target=_thread_func) - testthread.start() - testthread.join() - - # Now spin up our App instance, store it on both _ba and ba, - # and return it to the C++ layer. - # noinspection PyProtectedMember - from ba._app import App - import ba - app = App() - _ba.app = app - ba.app = app - return app + def isatty(self) -> bool: + """Are we a terminal?""" + return self._original.isatty() -app_state = _ballistica_bootstrap() +sys.stdout = _BAConsoleRedirect(sys.stdout, _ba.print_stdout) # type: ignore +sys.stderr = _BAConsoleRedirect(sys.stderr, _ba.print_stderr) # type: ignore -# This code runs in the main namespace, so clean up after ourself. -del _ballistica_bootstrap, TYPE_CHECKING +# Let's lookup mods first (so users can do whatever they want). +# and then our bundled scripts last (don't want to encourage dists +# overriding proper python functionality) +sys.path.insert(0, _ba.env()['python_directory_user']) +sys.path.append(_ba.env()['python_directory_app']) +sys.path.append(_ba.env()['python_directory_app_site']) + +# Tell Python to not handle SIGINT itself (it normally generates +# KeyboardInterrupts which make a mess; we want to do a simple +# clean exit). We capture interrupts per-platform in the C++ layer. +# I tried creating a handler in Python but it seemed to often have +# a delay of up to a second before getting called. (not a huge deal +# but I'm picky). +signal.signal(signal.SIGINT, signal.SIG_DFL) # Do default handling. + +# ..though it turns out we need to set up our C signal handling AFTER +# we've told Python to disable its own; otherwise (on Mac at least) it +# wipes out our existing C handler. +_ba.setup_sigint() + +# Sanity check: we should always be run in UTF-8 mode. +if sys.flags.utf8_mode != 1: + print('ERROR: Python\'s UTF-8 mode is not set.' + ' This will likely result in errors.') + +# FIXME: I think we should init Python in the main thread, which should +# also avoid these issues. (and also might help us play better with +# Python debuggers?) + +# Gloriously hacky workaround here: +# Our 'main' Python thread is the game thread (not the app's main +# thread) which means it has a small stack compared to the main +# thread (at least on apple). Sadly it turns out this causes the +# debug build of Python to blow its stack immediately when doing +# some big imports. +# Normally we'd just give the game thread the same stack size as +# the main thread and that'd be the end of it. However +# we're using std::threads which it turns out have no way to set +# the stack size (as of fall '19). Grumble. +# +# However python threads *can* take custom stack sizes. +# (and it appears they might use the main thread's by default?..) +# ...so as a workaround in the debug version, we can run problematic +# heavy imports here in another thread and all is well. +# If we ever see stack overflows in our release build we'll have +# to take more drastic measures like switching from std::threads +# to pthreads. + +if _ba.env()['debug_build']: + + def _thread_func() -> None: + # pylint: disable=unused-import + import json + import urllib.request + + testthread = threading.Thread(target=_thread_func) + testthread.start() + testthread.join() + del testthread + +# Now spin up our App instance, store it on both _ba and ba, +# and return it to the C++ layer. +# noinspection PyProtectedMember +# pylint: disable=wrong-import-position +from ba._app import App +import ba + +_ba.app = ba.app = App() diff --git a/tools/batools/build.py b/tools/batools/build.py index f11700ae..cf7e7d09 100644 --- a/tools/batools/build.py +++ b/tools/batools/build.py @@ -96,7 +96,7 @@ def _lazybuild_check_paths(inpaths: List[str], category: SourceCategory, def _testpath(path: str) -> bool: # Now see this path is newer than our target.. if mtime is None or os.path.getmtime(path) >= mtime: - print(f'{Clr.SMAG}Build of {tnamepretty} triggered by' + print(f'{Clr.SMAG}Build of {tnamepretty} triggered by change in' f" '{path}'{Clr.RST}") return True return False diff --git a/tools/batools/codegen.py b/tools/batools/codegen.py index 11c4e89f..4bae40e8 100644 --- a/tools/batools/codegen.py +++ b/tools/batools/codegen.py @@ -69,8 +69,9 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None: # Our C++ code first execs our input as a string. ccode = ('{const char* bindcode = ' + repr(pycode).replace("'", '"') + ';') - ccode += ('\nint result = PyRun_SimpleString(bindcode);\n' - 'if (result != 0) {\n' + ccode += ('\nPyObject* result = PyRun_String(bindcode, Py_file_input,' + ' bootstrap_context.get(), bootstrap_context.get());\n' + 'if (result == nullptr) {\n' ' PyErr_PrintEx(0);\n' ' // Use a standard error to avoid a useless stack trace.\n' ' throw std::logic_error("Error fetching required Python' @@ -80,7 +81,7 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None: # Then it grabs the function that was defined and runs it. ccode += ('PyObject* bindvals = PythonCommand("get_binding_values()",' ' "")' - '.RunReturnObj(true, nullptr);\n' + '.RunReturnObj(true, bootstrap_context.get());\n' 'if (bindvals == nullptr) {\n' ' // Use a standard error to avoid a useless stack trace.\n' ' throw std::logic_error("Error binding required Python' @@ -89,18 +90,12 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None: # Then it pulls the individual values out of the returned tuple. for i, line in enumerate(lines): - ccode += ('StoreObjCallable(ObjID::' + line[1] + - ', PyTuple_GET_ITEM(bindvals, ' + str(i) + '), true);\n') + storecmd = ('StoreObjCallable' if line[1].endswith('Class') + or line[1].endswith('Call') else 'StoreObj') + ccode += (f'{storecmd}(ObjID::{line[1]},' + f' PyTuple_GET_ITEM(bindvals, {i}), true);\n') - # Lastly it cleans up after itself. - ccode += ('result = PyRun_SimpleString("del get_binding_values");\n' - 'if (result != 0) {\n' - ' PyErr_PrintEx(0);\n' - ' // Use a standard error to avoid a useless stack trace.\n' - ' throw std::logic_error("Error cleaning up after Python' - ' binding.");\n' - '}\n' - '}\n') + ccode += 'Py_DECREF(bindvals);\n}\n' pretty_path = os.path.abspath(out_path) if pretty_path.startswith(projroot + '/'): pretty_path = pretty_path[len(projroot) + 1:]