Merge branch 'efroemling:master' into master

This commit is contained in:
Vishal 2022-09-02 21:51:09 +05:30 committed by GitHub
commit 6074709e30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 511 additions and 189 deletions

View File

@ -420,7 +420,7 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/60/ad/38269b7f1c7dc20cb9a506cd0681",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/72/85/d6fc4d16b7081d91fba2850b5b10",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/e9/ae/1d674d0c086eaa0bd1c3b1db0505",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/a3/38/05424b4cfb6e23e902b6dc20b209",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/68/22/ee4cff9f9fa011db6a2ed7092d1f",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/5b/cf/4501b151257c3d8d6ee8d0497d14",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/61/03/89070ca765e06da3a419a579f503",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/17/21/7b6371bde52392eb4a38e7c6d55a",
@ -431,12 +431,12 @@
"assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/68/93/da8e9874f41a786edf52ba4ccaad",
"assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/70/7d/6cbdaf130eaa5c58cffb1f321e3d",
"assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/4c/c7/0184b8178869d1a3827a1bfcd5bb",
"assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/c1/3c/2d45627563fbfbbbda2b7e6799e1",
"assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/8f/73/093120ae2241d8f4b899ccda2d75",
"assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/25/65/1cb03566e73811fc6e1b841d9072",
"assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/ef/e6/d4909f571d7473fd04055728490e",
"assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/a5/28/6bf6b15f8359a145cd2e599849f1",
"assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/82/eb/37ff44af76812097f9c98f05c730",
"assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/08/3b/68cea4d16f7020d932829af85323",
"assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/50/e8/837be1324c8128507b3df89b689f",
"assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/d8/f2/aa16bc336bd7660cc86c3264bfc4",
"assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/09/6c/942dd354447772a69ea5cae1d486",
"assets/build/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/91/70/05ad4a6fdbdaa0f471225f7ad317",
@ -3995,26 +3995,26 @@
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34",
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/0a/56/252de9190ee6367ccbf37174783d",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f5/30/29f5a9d9cc5c6f5c76e3058d3621",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3e/e5/037d736cacd93a4b005cc93e72ad",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/22/04/430aa3457c427f0814058c2b4483",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/93/68/307719e44199480a5ee051d993f5",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/9f/50/6f8a60e5375bf651bdebda617249",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cf/03/a5a5748fda33c876fbf3e8261b02",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/82/3e/5725b87a8cc1e90f69bec58c65d5",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/64/16/1589abfd35715bd2aa2915766148",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a8/36/584d685f3bea03753acf7344dfce",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3d/09/cbb451c2e8f856de61c0eafc5fdc",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b3/c9/9b3e221426dae6a047a893a4eb39",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/61/2e/af3b07614ea2fb60f70b3d3b442a",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/6e/fb/4b6e3e14ae9e329ae2a5c2eaab24",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/86/3b/f8fc04eefa313d673ee98d20e360",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d1/83/544e088664612666bbaa6c1ff422",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/79/13/29d322c6e8f7717ec87d5027bb20",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/94/01/19f43fe2ee530d48f31665d22ff0",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/17/e9/d6369d897f3595fbe03202887447",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/07/ff/cd46cba42a67cf31d6454b9eba7d",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/eb/f6/0fa02f0dd61fe86f030e235cb65d",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e8/5a/c49738579f58cff159f78330685e",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/77/79/d970690e3fa5d24e0cdfb5aff646",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/83/56/8203d51e88d563f373bd73304219",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/2b/7a/3eb09023c93e907472043fbccfff",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d0/4f/d139bb2f0a1e4e400dade616f5d3",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5d/5d/20bd4d3a607a8b3a5d9d9d925146",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a0/f2/0646e8a3ed1c1ae091bee9628b8e",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/08/e8/064033db53071b97422d21386e8f",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e7/e6/3cc5634143190749753a806a4792",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d1/94/513926fe5432ba722b0e4f119f0c",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/7e/884381d6bc009c804496a512beaf",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/be/06/c6f50e95926031616daf349acc64",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/16/95/4a354d9d8faa18f26be3a7c57f63",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f1/39/6da99f25127e5ec62b5586e30378",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d6/17/65c7d490b78d8ae0120e1d254b83",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/de/28/bea57ab25706df395792f06fd08b",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/4c/29/17e37dd645bb9d5fb6f85db2b1bd",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/26/22/bbd9535c97eedfc2c18c45e65f9b",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/a6/40/e9517fe39850dca141f0cc086503",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3b/0c/2f4061ab877d415a1c30e0e736db",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3c/5a/2b0714af254c64954ccfe51c70b3",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1f/ae/c8a885b1a1868b6846b606cdb456",
@ -4031,14 +4031,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/44/df/efb51d1c226eac613d48e2cbf0b8",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1c/f6/357fe951c86c9fc5b1b737cd91ae",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/04/17/e2de0ab5df6b938d828e8662ce6d",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/69/dc/6fc1614b2548c6ac76c9e891c2e2",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/c2/1b/263c5e001c6891d774d941f0bdfd",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/7b/3a/f77ffca8d7c45b859d1e48c1b468",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/88/15/1aa07f986d0bf7dac9a1f39635f2",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/4b/e1/646d3095ab442e0b18d4c0de9689",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/0f/fe/034c116781ddfe6cc89ada030056",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/91/64/10fcd883cf0d15895d72a638e2ad",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/1e/69/bf40bc8defe923cfa6d48cb5dd04",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/34/77/1986ffe869aca7b8aee6b24ea64b",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/0f/af/5430940c906f3d0c6e0983b9a2b9",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/22/84/02b9109e1449f2acca49f4f9b934",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/2b/22/b32d8e18c6a258929f091a14419d",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/a2/08/ecf905f1c6ede831e66e8d84c6f6",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/83/d7/01a034b1d9e2f028cb5f964396d3",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/81/a5/e780126b52d530cce18a64ae65e4",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/d8/39/51a851a77b6ce36073e9d190b9bf",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/7d/3e/229a581cb2454ed856f1d8b564a7",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/aa/a5/3ddc86d1789b2bf1d376b7671a3d"
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/98/12/571b2160d69d42580e8f31fa6a8d"
}

View File

@ -337,6 +337,7 @@
<w>capturetheflag</w>
<w>carentity</w>
<w>cashregistersound</w>
<w>cbegin</w>
<w>cbgn</w>
<w>cbits</w>
<w>cbot</w>
@ -699,6 +700,7 @@
<w>eachother</w>
<w>eaddrnotavail</w>
<w>easteregghunt</w>
<w>echofile</w>
<w>edcc</w>
<w>editcontroller</w>
<w>editgame</w>
@ -891,6 +893,7 @@
<w>floofcls</w>
<w>floooff</w>
<w>floop</w>
<w>flushhhhh</w>
<w>flycheck</w>
<w>fmod</w>
<w>fname</w>
@ -1391,6 +1394,7 @@
<w>listvalidconfigs</w>
<w>lival</w>
<w>llzma</w>
<w>lmap</w>
<w>lmerged</w>
<w>lmod</w>
<w>lmodfile</w>

View File

@ -1,4 +1,4 @@
### 1.7.7 (build 20723, api 7, 2022-08-26)
### 1.7.7 (build 20730, api 7, 2022-09-02)
- Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread.
- Improved logging of missing playlist game types.
- Some ba.Lstr functionality can now be used in background threads.
@ -8,6 +8,7 @@
- Added support for the console tool in the new devices section on ballistica.net.
- Increased timeouts in net-testing gui and a few other places to be able to better diagnose/handle places with very poor connectivity.
- Removed `Platform::SetLastPyCall()` which was just for debugging and which has not been useful in a while.
- Moved some app bootstrapping from the C++ layer to the ba._bootstrap module.
### 1.7.6 (build 20687, api 7, 2022-08-11)
- Cleaned up da MetaSubsystem code.

View File

@ -17,6 +17,7 @@
"ba_data/python/ba/__pycache__/_assetmanager.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_asyncio.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_benchmark.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_bootstrap.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_campaign.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_cloud.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_collision.cpython-310.opt-1.pyc",
@ -82,6 +83,7 @@
"ba_data/python/ba/_assetmanager.py",
"ba_data/python/ba/_asyncio.py",
"ba_data/python/ba/_benchmark.py",
"ba_data/python/ba/_bootstrap.py",
"ba_data/python/ba/_campaign.py",
"ba_data/python/ba/_cloud.py",
"ba_data/python/ba/_collision.py",
@ -511,6 +513,7 @@
"ba_data/python/efro/__pycache__/__init__.cpython-310.opt-1.pyc",
"ba_data/python/efro/__pycache__/call.cpython-310.opt-1.pyc",
"ba_data/python/efro/__pycache__/error.cpython-310.opt-1.pyc",
"ba_data/python/efro/__pycache__/log.cpython-310.opt-1.pyc",
"ba_data/python/efro/__pycache__/rpc.cpython-310.opt-1.pyc",
"ba_data/python/efro/__pycache__/terminal.cpython-310.opt-1.pyc",
"ba_data/python/efro/__pycache__/util.cpython-310.opt-1.pyc",
@ -532,6 +535,7 @@
"ba_data/python/efro/dataclassio/_prep.py",
"ba_data/python/efro/dataclassio/extras.py",
"ba_data/python/efro/error.py",
"ba_data/python/efro/log.py",
"ba_data/python/efro/message/__init__.py",
"ba_data/python/efro/message/__pycache__/__init__.cpython-310.opt-1.pyc",
"ba_data/python/efro/message/__pycache__/_message.cpython-310.opt-1.pyc",

View File

@ -150,6 +150,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/ba/_assetmanager.py \
build/ba_data/python/ba/_asyncio.py \
build/ba_data/python/ba/_benchmark.py \
build/ba_data/python/ba/_bootstrap.py \
build/ba_data/python/ba/_campaign.py \
build/ba_data/python/ba/_cloud.py \
build/ba_data/python/ba/_collision.py \
@ -399,6 +400,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/ba/__pycache__/_assetmanager.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_asyncio.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_benchmark.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_bootstrap.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_campaign.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_cloud.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_collision.cpython-310.opt-1.pyc \
@ -664,6 +666,7 @@ SCRIPT_TARGETS_PY_PUBLIC_TOOLS = \
build/ba_data/python/efro/dataclassio/_prep.py \
build/ba_data/python/efro/dataclassio/extras.py \
build/ba_data/python/efro/error.py \
build/ba_data/python/efro/log.py \
build/ba_data/python/efro/message/__init__.py \
build/ba_data/python/efro/message/_message.py \
build/ba_data/python/efro/message/_module.py \
@ -694,6 +697,7 @@ SCRIPT_TARGETS_PYC_PUBLIC_TOOLS = \
build/ba_data/python/efro/dataclassio/__pycache__/_prep.cpython-310.opt-1.pyc \
build/ba_data/python/efro/dataclassio/__pycache__/extras.cpython-310.opt-1.pyc \
build/ba_data/python/efro/__pycache__/error.cpython-310.opt-1.pyc \
build/ba_data/python/efro/__pycache__/log.cpython-310.opt-1.pyc \
build/ba_data/python/efro/message/__pycache__/__init__.cpython-310.opt-1.pyc \
build/ba_data/python/efro/message/__pycache__/_message.cpython-310.opt-1.pyc \
build/ba_data/python/efro/message/__pycache__/_module.cpython-310.opt-1.pyc \

View File

@ -0,0 +1,185 @@
# Released under the MIT License. See LICENSE for details.
#
"""Bootstrapping."""
from __future__ import annotations
import threading
from typing import TYPE_CHECKING
import _ba
if TYPE_CHECKING:
from typing import Any, TextIO, Callable
def bootstrap() -> None:
"""Run bootstrapping logic.
This is the very first userland code that runs.
It sets up low level environment bits and creates the app instance.
"""
import os
import sys
import signal
# The first thing we set up is capturing/redirecting Python
# stdout/stderr so we can at least debug problems on systems where
# native stdout/stderr is not easily accessible (looking at you, Android).
sys.stdout = _Redirect(sys.stdout, _ba.print_stdout) # type: ignore
sys.stderr = _Redirect(sys.stderr, _ba.print_stderr) # type: ignore
env = _ba.env()
# Give a soft warning if we're being used with a different binary
# version than we expect.
expected_build = 20730
running_build: int = env['build_number']
if running_build != expected_build:
print(
f'WARNING: These script files are meant to be used with'
f' Ballistica build {expected_build}.\n'
f' You are running build {running_build}.'
f' This might cause the app to error or misbehave.',
file=sys.stderr)
# Tell Python to not handle SIGINT itself (it normally generates
# KeyboardInterrupts which make a mess; we want to intercept them
# for simple clean exit). We capture interrupts per-platform in
# the C++ layer.
# Note: 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.',
file=sys.stderr)
debug_build = env['debug_build']
# We expect dev_mode on in debug builds and off otherwise.
if debug_build != sys.flags.dev_mode:
print(
f'WARNING: Mismatch in debug_build {debug_build}'
f' and sys.flags.dev_mode {sys.flags.dev_mode}',
file=sys.stderr)
# In embedded situations (when we're providing our own Python) let's
# also provide our own root certs so ssl works. We can consider overriding
# this in particular embedded cases if we can verify that system certs
# are working.
# (We also allow forcing this via an env var if the user desires)
if (_ba.contains_python_dist()
or os.environ.get('BA_USE_BUNDLED_ROOT_CERTS') == '1'):
import certifi
# Let both OpenSSL and requests (if present) know to use this.
os.environ['SSL_CERT_FILE'] = os.environ['REQUESTS_CA_BUNDLE'] = (
certifi.where())
# 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 debug_build:
# noinspection PyUnresolvedReferences
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
# Clear out the standard quit/exit messages since they don't work for us.
# pylint: disable=c-extension-no-member
if not TYPE_CHECKING:
import __main__
del __main__.__builtins__.quit
del __main__.__builtins__.exit
# Now spin up our App instance and store it on both _ba and ba.
from ba._app import App
import ba
_ba.app = ba.app = App()
class _Redirect:
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 write call."""
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)
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)
def flush(self) -> None:
"""Flush the file."""
self._original.flush()
def isatty(self) -> bool:
"""Are we a terminal?"""
return self._original.isatty()

View File

@ -106,8 +106,8 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
],
default=1.0,
),
ba.BoolSetting('Epic Mode', default=False),
]
default_music = ba.MusicType.FOOTBALL
@classmethod
def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool:
@ -143,6 +143,10 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
self._flag_respawn_light: ba.NodeActor | None = None
self._score_to_win = int(settings['Score to Win'])
self._time_limit = float(settings['Time Limit'])
self._epic_mode = bool(settings['Epic Mode'])
self.slow_motion = self._epic_mode
self.default_music = (ba.MusicType.EPIC
if self._epic_mode else ba.MusicType.FOOTBALL)
def get_instance_description(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
@ -330,7 +334,6 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
tips = ['Use the pick-up button to grab the flag < ${PICKUP} >']
scoreconfig = ba.ScoreConfig(scoretype=ba.ScoreType.MILLISECONDS,
version='B')
default_music = ba.MusicType.FOOTBALL
# FIXME: Need to update co-op games to use getscoreconfig.
def get_score_type(self) -> str:

View File

@ -137,8 +137,8 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
],
default=1.0,
),
ba.BoolSetting('Epic Mode', default=False),
]
default_music = ba.MusicType.HOCKEY
@classmethod
def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool:
@ -203,6 +203,10 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
self._puck: Puck | None = None
self._score_to_win = int(settings['Score to Win'])
self._time_limit = float(settings['Time Limit'])
self._epic_mode = bool(settings['Epic Mode'])
self.slow_motion = self._epic_mode
self.default_music = (ba.MusicType.EPIC
if self._epic_mode else ba.MusicType.HOCKEY)
def get_instance_description(self) -> str | Sequence:
if self._score_to_win == 1:

View File

@ -76,9 +76,9 @@ class KeepAwayGame(ba.TeamGameActivity[Player, Team]):
],
default=1.0,
),
ba.BoolSetting('Epic Mode', default=False),
]
scoreconfig = ba.ScoreConfig(label='Time Held')
default_music = ba.MusicType.KEEP_AWAY
@classmethod
def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool:
@ -115,6 +115,10 @@ class KeepAwayGame(ba.TeamGameActivity[Player, Team]):
self._flag: Flag | None = None
self._hold_time = int(settings['Hold Time'])
self._time_limit = float(settings['Time Limit'])
self._epic_mode = bool(settings['Epic Mode'])
self.slow_motion = self._epic_mode
self.default_music = (ba.MusicType.EPIC
if self._epic_mode else ba.MusicType.KEEP_AWAY)
def get_instance_description(self) -> str | Sequence:
return 'Carry the flag for ${ARG1} seconds.', self._hold_time

View File

@ -79,6 +79,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
],
default=1.0,
),
ba.BoolSetting('Epic Mode', default=False),
]
scoreconfig = ba.ScoreConfig(label='Time Held')
@ -115,6 +116,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
self._scoring_team: weakref.ref[Team] | None = None
self._hold_time = int(settings['Hold Time'])
self._time_limit = float(settings['Time Limit'])
self._epic_mode = bool(settings['Epic Mode'])
self._flag_region_material = ba.Material()
self._flag_region_material.add_actions(
conditions=('they_have_material', shared.player_material),
@ -128,7 +130,9 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
))
# Base class overrides.
self.default_music = ba.MusicType.SCARY
self.slow_motion = self._epic_mode
self.default_music = (ba.MusicType.EPIC
if self._epic_mode else ba.MusicType.SCARY)
def get_instance_description(self) -> str | Sequence:
return 'Secure the flag for ${ARG1} seconds.', self._hold_time

View File

@ -188,6 +188,7 @@
<w>cancelbtn</w>
<w>capitan</w>
<w>cargs</w>
<w>cbegin</w>
<w>cbgn</w>
<w>cbresults</w>
<w>cbtnoffs</w>
@ -362,6 +363,7 @@
<w>dxgi</w>
<w>dynamicdata</w>
<w>echidna</w>
<w>echofile</w>
<w>edef</w>
<w>effmult</w>
<w>efro</w>
@ -460,6 +462,7 @@
<w>floooff</w>
<w>floop</w>
<w>flopsy</w>
<w>flushhhhh</w>
<w>fname</w>
<w>fnode</w>
<w>fnumc</w>
@ -703,6 +706,7 @@
<w>linkstoryboards</w>
<w>listobj</w>
<w>llock</w>
<w>lmap</w>
<w>localmodlibs</w>
<w>localns</w>
<w>lockpath</w>

View File

@ -21,7 +21,7 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20723;
const int kAppBuildNumber = 20730;
const char* kAppVersion = "1.7.7";
// Our standalone globals.

View File

@ -1,78 +1,20 @@
# Released under the MIT License. See LICENSE for details.
#
"""Initial ballistica bootstrapping."""
from __future__ import annotations
import os
import sys
import signal
import threading
from typing import TYPE_CHECKING
import _ba
if TYPE_CHECKING:
from typing import Any, TextIO, Callable
pass
# All we do here is make our script files accessible and then hand it off
# to them.
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 write(self, sval: Any) -> None:
"""Override standard write call."""
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)
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)
def flush(self) -> None:
"""Flush the file."""
self._original.flush()
def isatty(self) -> bool:
"""Are we a terminal?"""
return self._original.isatty()
# The very first thing we set up is redirecting Python stdout/stderr so
# we can at least debug problems on systems where native stdout/stderr
# is not easily accessible (looking at you, Android).
sys.stdout = _BAConsoleRedirect(sys.stdout, _ba.print_stdout) # type: ignore
sys.stderr = _BAConsoleRedirect(sys.stderr, _ba.print_stderr) # type: ignore
# Now get access to our various script files.
# Let's lookup mods first (so users can do whatever they want).
# and then our bundled scripts last (don't want bundled site-package
# stuff overwriting system versions)
@ -80,93 +22,9 @@ 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 intercept them
# for simple clean exit). We capture interrupts per-platform in
# the C++ layer.
# Note: 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.')
debug_build = _ba.env()['debug_build']
# We expect dev_mode on in debug builds and off otherwise.
if debug_build != sys.flags.dev_mode:
print(f'WARNING: Mismatch in debug_build {debug_build}'
f' and sys.flags.dev_mode {sys.flags.dev_mode}')
# In embedded situations (when we're providing our own Python) let's
# also provide our own root certs so ssl works. We can consider overriding
# this in particular embedded cases if we can verify that system certs
# are working.
# (We also allow forcing this via an env var if the user desires)
# pylint: disable=wrong-import-position
if (_ba.contains_python_dist()
or os.environ.get('BA_USE_BUNDLED_ROOT_CERTS') == '1'):
import certifi
# Let both OpenSSL and requests (if present) know to use this.
os.environ['SSL_CERT_FILE'] = os.environ['REQUESTS_CA_BUNDLE'] = (
certifi.where())
# 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 debug_build:
# noinspection PyUnresolvedReferences
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
# Clear out the standard quit/exit messages since they don't work for us.
# pylint: disable=c-extension-no-member
if not TYPE_CHECKING:
import __main__
del __main__.__builtins__.quit
del __main__.__builtins__.exit
# Now spin up our App instance, store it on both _ba and ba,
# and return it to the C++ layer.
# The import is down here since it won't work until we muck with paths.
# noinspection PyProtectedMember
from ba._app import App
import ba
# pylint: disable=wrong-import-position
from ba._bootstrap import bootstrap
_ba.app = ba.app = App()
bootstrap()

247
tools/efro/log.py Normal file
View File

@ -0,0 +1,247 @@
# Released under the MIT License. See LICENSE for details.
#
"""Logging functionality."""
from __future__ import annotations
import sys
import time
import logging
import datetime
import threading
from enum import Enum
from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated
from efro.util import utc_now
from efro.terminal import TerminalColor
from efro.dataclassio import ioprepped, IOAttrs, dataclass_to_json
if TYPE_CHECKING:
from pathlib import Path
from typing import Any, Callable
class LogLevel(Enum):
"""Severity level for a log entry.
These enums have numeric values so they can be compared in severity.
Note that these values are not currently interchangeable with the
logging.ERROR, logging.DEBUG, etc. values.
"""
DEBUG = 0
INFO = 1
WARNING = 2
ERROR = 3
CRITICAL = 4
@ioprepped
@dataclass
class LogEntry:
"""Single logged message."""
name: Annotated[str,
IOAttrs('n', soft_default='root', store_default=False)]
message: Annotated[str, IOAttrs('m')]
level: Annotated[LogLevel, IOAttrs('l')]
time: Annotated[datetime.datetime, IOAttrs('t')]
class LogHandler(logging.Handler):
"""Fancy-pants handler for logging output.
Writes logs to disk in structured json format and echoes them
to stdout/stderr with pretty colors.
"""
def __init__(self,
path: str | Path | None,
echofile: Any,
suppress_non_root_debug: bool = False):
super().__init__()
# pylint: disable=consider-using-with
self._file = (None
if path is None else open(path, 'w', encoding='utf-8'))
self._echofile = echofile
self._callbacks: list[Callable[[LogEntry], None]] = []
self._suppress_non_root_debug = suppress_non_root_debug
def emit(self, record: logging.LogRecord) -> None:
# Special case - filter out this common extra-chatty category.
# TODO - should use a standard logging.Filter for this.
if (self._suppress_non_root_debug and record.name != 'root'
and record.levelname == 'DEBUG'):
return
# Bake down all log formatting into a simple string.
msg = self.format(record)
# Translate Python log levels to our own.
level = {
'DEBUG': LogLevel.DEBUG,
'INFO': LogLevel.INFO,
'WARNING': LogLevel.WARNING,
'ERROR': LogLevel.ERROR,
'CRITICAL': LogLevel.CRITICAL
}[record.levelname]
entry = LogEntry(message=msg,
name=record.name,
level=level,
time=datetime.datetime.fromtimestamp(
record.created, datetime.timezone.utc))
for call in self._callbacks:
call(entry)
# Also route log entries to the echo file (generally stdout/stderr)
# with pretty colors.
if self._echofile is not None:
cbegin: str
cend: str
cbegin, cend = {
LogLevel.DEBUG:
(TerminalColor.CYAN.value, TerminalColor.RESET.value),
LogLevel.INFO: ('', ''),
LogLevel.WARNING:
(TerminalColor.YELLOW.value, TerminalColor.RESET.value),
LogLevel.ERROR:
(TerminalColor.RED.value, TerminalColor.RESET.value),
LogLevel.CRITICAL:
(TerminalColor.STRONG_MAGENTA.value +
TerminalColor.BOLD.value + TerminalColor.BG_BLACK.value,
TerminalColor.RESET.value),
}[level]
self._echofile.write(f'{cbegin}{msg}{cend}\n')
# Note to self: it sounds like logging wraps calls to us
# in a lock so we shouldn't have to worry about garbled
# json output due to multiple threads writing at once,
# but may be good to find out for sure?
if self._file is not None:
entry_s = dataclass_to_json(entry)
assert '\n' not in entry_s # make sure its a single line
print(entry_s, file=self._file, flush=True)
def emit_custom(self, name: str, message: str, level: LogLevel) -> None:
"""Custom emit call for our stdout/stderr redirection."""
entry = LogEntry(name=name,
message=message,
level=level,
time=utc_now())
for call in self._callbacks:
call(entry)
if self._file is not None:
entry_s = dataclass_to_json(entry)
assert '\n' not in entry_s # Make sure its a single line.
print(entry_s, file=self._file, flush=True)
def add_callback(self, call: Callable[[LogEntry], None]) -> None:
"""Add a callback to be run for each added entry."""
self._callbacks.append(call)
class LogRedirect:
"""A file-like object for redirecting stdout/stderr to our log."""
def __init__(self, name: str, orig_out: Any, log_handler: LogHandler,
log_level: LogLevel):
self._name = name
self._orig_out = orig_out
self._log_handler = log_handler
self._log_level = log_level
self._chunk = ''
self._chunk_start_time = 0.0
self._lock = threading.Lock()
def write(self, s: str) -> None:
"""Write something to output."""
assert isinstance(s, str)
# First, ship it off to the original destination.
self._orig_out.write(s)
# Now add this to our chunk and ship completed chunks
# off to the logger.
# Let's consider a chunk completed when we're passed
# a single '\n' by itself. (print() statement will do
# this at the end by default).
# We may get some false positives/negatives this way
# but it should result in *most* big multi-line print
# statements being wrapped into a single log entry.
# Also, flush with only_old=True can be called periodically
# to dump any pending chunks that don't happen to fit
# this pattern.
with self._lock:
if s == '\n':
self._log_handler.emit_custom(name=self._name,
message=self._chunk,
level=self._log_level)
self._chunk = ''
else:
if self._chunk == '':
self._chunk_start_time = time.time()
self._chunk += s
def flush(self, only_old: bool = False) -> None:
"""Flushhhhh!"""
self._orig_out.flush()
if only_old and time.time() - self._chunk_start_time < 0.5:
return
with self._lock:
if self._chunk != '':
chunk = self._chunk
if chunk.endswith('\n'):
chunk = chunk[:-1]
self._log_handler.emit_custom(name=self._name,
message=chunk,
level=self._log_level)
self._chunk = ''
def setup_logging(log_path: str | Path | None,
level: LogLevel,
suppress_non_root_debug: bool = False) -> LogHandler:
"""Set up our logging environment.
Returns the custom handler which can be used to fetch information
about logs that have passed through it. (worst log-levels, etc.).
"""
lmap = {
LogLevel.DEBUG: logging.DEBUG,
LogLevel.INFO: logging.INFO,
LogLevel.WARNING: logging.WARNING,
LogLevel.ERROR: logging.ERROR,
LogLevel.CRITICAL: logging.CRITICAL,
}
# Wire logger output to go to a structured log file.
# Also echo it to stderr IF we're running in a terminal.
loghandler = LogHandler(
log_path,
echofile=sys.stderr if sys.stderr.isatty() else None,
suppress_non_root_debug=suppress_non_root_debug)
logging.basicConfig(level=lmap[level],
format='%(message)s',
handlers=[loghandler])
# DISABLING THIS BIT FOR NOW - want to keep things as pure as possible.
if bool(False):
# Now wire Python stdout/stderr output to generate log entries
# in addition to its regular routing. Make sure to do this *after* we
# tell the log-handler to write to stderr, otherwise we get an infinite
# loop.
# NOTE: remember that this won't capture subcommands or other
# non-python stdout/stderr output.
sys.stdout = LogRedirect( # type: ignore
'stdout', sys.stdout, loghandler, LogLevel.INFO)
sys.stderr = LogRedirect( # type: ignore
'stderr', sys.stderr, loghandler, LogLevel.INFO)
return loghandler