diff --git a/.efrocachemap b/.efrocachemap
index 4b8f1691..375613f0 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -421,18 +421,18 @@
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55",
- "build/assets/ba_data/data/langdata.json": "c6f94f9c1dc833c537d16672d9018b94",
+ "build/assets/ba_data/data/langdata.json": "4a35cc51d1021fa7e525123bcf99043c",
"build/assets/ba_data/data/languages/arabic.json": "00ba700de6c672a56658a6bd1ad27523",
"build/assets/ba_data/data/languages/belarussian.json": "7fe38341815ca6ff4d95224196e7a67e",
"build/assets/ba_data/data/languages/chinese.json": "5761468d25f2bd4e79921826cebd572b",
"build/assets/ba_data/data/languages/chinesetraditional.json": "f858da49be0a5374157c627857751078",
"build/assets/ba_data/data/languages/croatian.json": "766532c67af5bd0144c2d63cab0516fa",
- "build/assets/ba_data/data/languages/czech.json": "93c5fe0d884d95435da6c675f64e30e0",
+ "build/assets/ba_data/data/languages/czech.json": "cd21ad8c6b8e9ed700284cf1e1aecbf8",
"build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e",
"build/assets/ba_data/data/languages/dutch.json": "22b44a33bf81142ba2befad14eb5746e",
"build/assets/ba_data/data/languages/english.json": "bd43b77b1ccca059573acbde148b4767",
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
- "build/assets/ba_data/data/languages/filipino.json": "afbda3adf14555e1567ee63c32e340e7",
+ "build/assets/ba_data/data/languages/filipino.json": "0f5ad7c06db70027b116dfd9324bdf67",
"build/assets/ba_data/data/languages/french.json": "49ff6d211537b8003b8241438dca661d",
"build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad",
"build/assets/ba_data/data/languages/gibberish.json": "9aae526303a22372fe9b4cf1781520ef",
@@ -445,12 +445,12 @@
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38",
"build/assets/ba_data/data/languages/persian.json": "d742f4a6d3c3555031102b21abdcbb5b",
"build/assets/ba_data/data/languages/polish.json": "b9a58b70ed5e99d8b7fa2392b2eb0cda",
- "build/assets/ba_data/data/languages/portuguese.json": "556af4e8170356ad239412e1743e20d5",
+ "build/assets/ba_data/data/languages/portuguese.json": "e3adc6c04486d21e84019a0b03ce11b1",
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
"build/assets/ba_data/data/languages/russian.json": "e120993371f52edd2d99f2236188933c",
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
"build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef",
- "build/assets/ba_data/data/languages/spanish.json": "80ea58bd3295a0252b7fdac9154aa22f",
+ "build/assets/ba_data/data/languages/spanish.json": "1d14210b4eefb48130608bd0495b7900",
"build/assets/ba_data/data/languages/swedish.json": "5142a96597d17d8344be96a603da64ac",
"build/assets/ba_data/data/languages/tamil.json": "b4de1a2851afe4869c82e9acd94cd89c",
"build/assets/ba_data/data/languages/thai.json": "77755219bbf5fb7eea0d6b226684f403",
@@ -4060,50 +4060,50 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
- "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "8561660678904509458b80bb4b62d8ea",
- "build/prefab/full/linux_arm64_gui/release/ballisticakit": "f2e7de4723ce8fb6984a5bbd9ac93aa6",
- "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "af1ade2cc275dacc7e1793383d766f3d",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "bb806faa941b6c3c7834383264cf8571",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "648bfaca6487ff13cd27a31e317878f0",
- "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "38501876d5009ecaa936994ec6fbb3a2",
- "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "f7d354aded26684974b4d0fbb8725762",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "f6ceb8135fe4fb9aa7fc13fb1b835077",
- "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "402cb0d5a0abecfa9e578f3801b15e59",
- "build/prefab/full/mac_arm64_gui/release/ballisticakit": "4eae3e6ade00be0224839bfc16351d2c",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "c6dd9b6cf876db50aa8d0cc0cc80efa4",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "79d19536ed7de918c947c5095286b8ce",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "409140e2c39941dbc66088417399d5ea",
- "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "1a25fd7e3c5b94a294ce769083b71751",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "7bfbb50681473702f286a50e56277b93",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "37ce7d3b865c0b58161c2d4ccdb54256",
- "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "728dfc083916d7199dee1e75ad12e9fc",
- "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "fbf59de05d7e121d9c5dd164939228dc",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "1eb456e1b1c4d215912809950d938c1a",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "65f507699fed78fcfa12e3e63af2bf8b",
- "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "4f5d3cafbea651078c1721684b61033a",
- "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "7436c575aee1f9d112d41f18e2ae6b22",
- "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "4f5d3cafbea651078c1721684b61033a",
- "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "7436c575aee1f9d112d41f18e2ae6b22",
- "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "3ffcb35eb71566fefb7d9ad2589b37b4",
- "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ce9de33efccb9aa1e301fe4f11e6b1c1",
- "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "3ffcb35eb71566fefb7d9ad2589b37b4",
- "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ce9de33efccb9aa1e301fe4f11e6b1c1",
- "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "9d674b5e8a8357b9462a65359611ea45",
- "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "e6d1c0b9bf27c34968e512c5db8e7de5",
- "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "9d674b5e8a8357b9462a65359611ea45",
- "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "e6d1c0b9bf27c34968e512c5db8e7de5",
- "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "910b5e39fe4ba5bb849505c578efe3ec",
- "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "2d9e14f7cfe50b1dc51e5e9eae05b5fd",
- "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "b83a67eeaed0fc99bf995767a8150e5d",
- "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "2d9e14f7cfe50b1dc51e5e9eae05b5fd",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "f6a855e83d7816e73d9859ff9e508190",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "6cd203dce2718e0eded672c83c51506a",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "a1f81d6527bba562c3626e520ab8aa2d",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "8b6a8b19f73b67413612185aa2d9ca5d",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "2534af31cf9f8a86ddb7c92b0184f4b2",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "0cf7fb12f97a3f8176462ec5d1ddb992",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4afbbfa78e25e932d9392a1bd6bacc52",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "a0ba1ed33b7a83543b51f24a96941dfa",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "6135aeb242afaf9d1114810a67c89cec",
+ "build/prefab/full/linux_arm64_gui/release/ballisticakit": "bbbbb14d42ed6eb0c5eb56867b7fb870",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cd28f9cc4652736a31c677fc4e5dbaf1",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "239c608cc52c0320210e56ad6abe57a5",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "e76d67cacf1393d33796d6b6b1bf1413",
+ "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "a7eaa8dc4d859ef7a735483b04ccec4a",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "7a2eef42da34a35ddcc2fd7c66843b1b",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "694599ac6a967b2ed383b27bf8093e5b",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "c91cbab6a07affa22e0612210f8b807c",
+ "build/prefab/full/mac_arm64_gui/release/ballisticakit": "d460f7a3909f92d5dbf752e4521a9fbc",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "0a0abfe75bc987e7b65a3cfa106e8353",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "8f21405b29f2b2ab01323d711492cca0",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "96dc73e819f41f99a1b2dbb45f79d551",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "c79ac51cd2deabb1c2d0acddeaf81c30",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "f06ec14e8c3106be9df91af7da621dc9",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "f389f9a7b1afc81f76787722340cfa9c",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "c7dab78aac11cb1430d8456d5d48107a",
+ "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "67e29852dfee2e63e179cfebf608ef26",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "9778f8faf91c9993fbf3015bd4554a87",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "73477bd15b9e3834314fd878c9e108d4",
+ "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "fb9b8443c1b4cccad749df7d6328220f",
+ "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "384fb7fd55ad5a6cdbb662da1ec402ab",
+ "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "fb9b8443c1b4cccad749df7d6328220f",
+ "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "384fb7fd55ad5a6cdbb662da1ec402ab",
+ "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "bc7d0811bcd87156ebf5292a38a1c350",
+ "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "bb32f45054b6999300bf8b41d6a4b402",
+ "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "bc7d0811bcd87156ebf5292a38a1c350",
+ "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "bb32f45054b6999300bf8b41d6a4b402",
+ "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "8d9a1505bf397f4902baabed7c1cf438",
+ "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "f4d9c115e22dd81e36d1c5baeac8d848",
+ "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "8d9a1505bf397f4902baabed7c1cf438",
+ "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "f4d9c115e22dd81e36d1c5baeac8d848",
+ "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "fb72c92ec6ec0e1c8f4ced32abd86505",
+ "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "131aab20cfe77fe89c3f452a855f1e68",
+ "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "ee10cdc9f9a861e2be0f1a208c0ca0fe",
+ "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "131aab20cfe77fe89c3f452a855f1e68",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "678fabc6dfd6f401ee8942d088ee9181",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "e092d2aed8464a61a623d79ca25308d8",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6b658f49be396ad645c5e57464739a3b",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "9d79a56403a6d806ff131a7de664dfa7",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "e831a26d2c28e862d51e24393d158c99",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "46fe1c89bcc75c781729ec9e5491c610",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "9c6278d7df3ce4db2ffe7794a0fd35b7",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "110c35a17b462864075800756b5e541a",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",
@@ -4112,7 +4112,7 @@
"src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69",
"src/ballistica/core/mgen/pyembed/env.inc": "8be46e5818f360d10b7b0224a9e91d07",
"src/ballistica/core/mgen/python_modules_monolithic.h": "fb967ed1c7db0c77d8deb4f00a7103c5",
- "src/ballistica/scene_v1/mgen/pyembed/binding_scene_v1.inc": "d80f970053099b3044204bfe29ddefce",
+ "src/ballistica/scene_v1/mgen/pyembed/binding_scene_v1.inc": "c25b263f2a31fb5ebe057db07d144879",
"src/ballistica/template_fs/mgen/pyembed/binding_template_fs.inc": "44a45492db057bf7f7158c3b0fa11f0f",
"src/ballistica/ui_v1/mgen/pyembed/binding_ui_v1.inc": "f5f054050d2b2fcd3763a4833fb32269"
}
\ No newline at end of file
diff --git a/.idea/ballisticakit.iml b/.idea/ballisticakit.iml
index 4c2844c3..6d3ca9f0 100644
--- a/.idea/ballisticakit.iml
+++ b/.idea/ballisticakit.iml
@@ -21,7 +21,6 @@
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4423000a..34c408fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
-### 1.7.31 (build 21700, api 8, 2023-12-09)
-
+### 1.7.31 (build 21707, api 8, 2023-12-13)
+- added `bascenev1.get_connection_to_host_info_2()` which is an improved
+ type-safe version of `bascenev1.get_connection_to_host_info()`.
+
### 1.7.30 (build 21697, api 8, 2023-12-08)
- Continued work on the big 1.7.28 update.
- Got the Android version back up and running. There's been lots of cleanup and
diff --git a/ballisticakit-cmake/.idea/misc.xml b/ballisticakit-cmake/.idea/misc.xml
index 17581663..f01f08b8 100644
--- a/ballisticakit-cmake/.idea/misc.xml
+++ b/ballisticakit-cmake/.idea/misc.xml
@@ -14,7 +14,6 @@
-
diff --git a/config/spinoffconfig.py b/config/spinoffconfig.py
index 21b06551..ece093bd 100644
--- a/config/spinoffconfig.py
+++ b/config/spinoffconfig.py
@@ -152,7 +152,6 @@ ctx.filter_dirs = {
'ballisticakit-cmake',
'ballisticakit-xcode/BallisticaKit.xcodeproj',
'ballisticakit-ios.xcodeproj',
- 'ballisticakit-mac.xcodeproj',
'config',
'src/assets/pdoc',
}
diff --git a/src/assets/.asset_manifest_public.json b/src/assets/.asset_manifest_public.json
index 55998cf7..8a62973f 100644
--- a/src/assets/.asset_manifest_public.json
+++ b/src/assets/.asset_manifest_public.json
@@ -20,7 +20,6 @@
"ba_data/python/babase/__pycache__/_error.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_general.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_hooks.cpython-311.opt-1.pyc",
- "ba_data/python/babase/__pycache__/_keyboard.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_language.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_login.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_math.cpython-311.opt-1.pyc",
@@ -50,7 +49,6 @@
"ba_data/python/babase/_error.py",
"ba_data/python/babase/_general.py",
"ba_data/python/babase/_hooks.py",
- "ba_data/python/babase/_keyboard.py",
"ba_data/python/babase/_language.py",
"ba_data/python/babase/_login.py",
"ba_data/python/babase/_math.py",
@@ -152,6 +150,7 @@
"ba_data/python/bascenev1/__pycache__/_messages.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_multiteamsession.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_music.cpython-311.opt-1.pyc",
+ "ba_data/python/bascenev1/__pycache__/_net.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_nodeactor.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_player.cpython-311.opt-1.pyc",
"ba_data/python/bascenev1/__pycache__/_playlist.cpython-311.opt-1.pyc",
@@ -186,6 +185,7 @@
"ba_data/python/bascenev1/_messages.py",
"ba_data/python/bascenev1/_multiteamsession.py",
"ba_data/python/bascenev1/_music.py",
+ "ba_data/python/bascenev1/_net.py",
"ba_data/python/bascenev1/_nodeactor.py",
"ba_data/python/bascenev1/_player.py",
"ba_data/python/bascenev1/_playlist.py",
@@ -352,10 +352,12 @@
"ba_data/python/bauiv1/__init__.py",
"ba_data/python/bauiv1/__pycache__/__init__.cpython-311.opt-1.pyc",
"ba_data/python/bauiv1/__pycache__/_hooks.cpython-311.opt-1.pyc",
+ "ba_data/python/bauiv1/__pycache__/_keyboard.cpython-311.opt-1.pyc",
"ba_data/python/bauiv1/__pycache__/_subsystem.cpython-311.opt-1.pyc",
"ba_data/python/bauiv1/__pycache__/_uitypes.cpython-311.opt-1.pyc",
"ba_data/python/bauiv1/__pycache__/onscreenkeyboard.cpython-311.opt-1.pyc",
"ba_data/python/bauiv1/_hooks.py",
+ "ba_data/python/bauiv1/_keyboard.py",
"ba_data/python/bauiv1/_subsystem.py",
"ba_data/python/bauiv1/_uitypes.py",
"ba_data/python/bauiv1/onscreenkeyboard.py",
diff --git a/src/assets/Makefile b/src/assets/Makefile
index a52dda49..1ee71109 100644
--- a/src/assets/Makefile
+++ b/src/assets/Makefile
@@ -178,7 +178,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/babase/_error.py \
$(BUILD_DIR)/ba_data/python/babase/_general.py \
$(BUILD_DIR)/ba_data/python/babase/_hooks.py \
- $(BUILD_DIR)/ba_data/python/babase/_keyboard.py \
$(BUILD_DIR)/ba_data/python/babase/_language.py \
$(BUILD_DIR)/ba_data/python/babase/_login.py \
$(BUILD_DIR)/ba_data/python/babase/_math.py \
@@ -237,6 +236,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bascenev1/_messages.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_multiteamsession.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_music.py \
+ $(BUILD_DIR)/ba_data/python/bascenev1/_net.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_nodeactor.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_player.py \
$(BUILD_DIR)/ba_data/python/bascenev1/_playlist.py \
@@ -326,6 +326,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/batemplatefs/_subsystem.py \
$(BUILD_DIR)/ba_data/python/bauiv1/__init__.py \
$(BUILD_DIR)/ba_data/python/bauiv1/_hooks.py \
+ $(BUILD_DIR)/ba_data/python/bauiv1/_keyboard.py \
$(BUILD_DIR)/ba_data/python/bauiv1/_subsystem.py \
$(BUILD_DIR)/ba_data/python/bauiv1/_uitypes.py \
$(BUILD_DIR)/ba_data/python/bauiv1/onscreenkeyboard.py \
@@ -452,7 +453,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_error.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_general.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_hooks.cpython-311.opt-1.pyc \
- $(BUILD_DIR)/ba_data/python/babase/__pycache__/_keyboard.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_language.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_login.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_math.cpython-311.opt-1.pyc \
@@ -511,6 +511,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_messages.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_multiteamsession.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_music.cpython-311.opt-1.pyc \
+ $(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_net.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_nodeactor.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_player.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bascenev1/__pycache__/_playlist.cpython-311.opt-1.pyc \
@@ -600,6 +601,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/batemplatefs/__pycache__/_subsystem.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/__init__.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_hooks.cpython-311.opt-1.pyc \
+ $(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_keyboard.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_subsystem.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/_uitypes.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1/__pycache__/onscreenkeyboard.cpython-311.opt-1.pyc \
diff --git a/src/assets/ba_data/python/babase/__init__.py b/src/assets/ba_data/python/babase/__init__.py
index b2a12abe..91b05f35 100644
--- a/src/assets/ba_data/python/babase/__init__.py
+++ b/src/assets/ba_data/python/babase/__init__.py
@@ -154,7 +154,6 @@ from babase._general import (
getclass,
get_type_name,
)
-from babase._keyboard import Keyboard
from babase._language import Lstr, LanguageSubsystem
from babase._login import LoginAdapter, LoginInfo
@@ -261,7 +260,6 @@ __all__ = [
'is_point_in_box',
'is_running_on_fire_tv',
'is_xcode_build',
- 'Keyboard',
'LanguageSubsystem',
'lock_all_input',
'LoginAdapter',
diff --git a/src/assets/ba_data/python/babase/_accountv2.py b/src/assets/ba_data/python/babase/_accountv2.py
index 5d5d6633..37914bce 100644
--- a/src/assets/ba_data/python/babase/_accountv2.py
+++ b/src/assets/ba_data/python/babase/_accountv2.py
@@ -19,7 +19,7 @@ if TYPE_CHECKING:
from babase._login import LoginAdapter, LoginInfo
-DEBUG_LOG = _babase.temp_testing()
+DEBUG_LOG = False
class AccountV2Subsystem:
diff --git a/src/assets/ba_data/python/babase/_appmode.py b/src/assets/ba_data/python/babase/_appmode.py
index fcaa77a9..1da43144 100644
--- a/src/assets/ba_data/python/babase/_appmode.py
+++ b/src/assets/ba_data/python/babase/_appmode.py
@@ -31,6 +31,7 @@ class AppMode:
AppExperience associated with the AppMode must be supported by
the current app and runtime environment.
"""
+ # FIXME: check AppExperience.
return cls._supports_intent(intent)
@classmethod
diff --git a/src/assets/ba_data/python/babase/_apputils.py b/src/assets/ba_data/python/babase/_apputils.py
index 655bc8b3..59d76481 100644
--- a/src/assets/ba_data/python/babase/_apputils.py
+++ b/src/assets/ba_data/python/babase/_apputils.py
@@ -325,7 +325,7 @@ def dump_app_state(
)
-def log_dumped_app_state() -> None:
+def log_dumped_app_state(from_previous_run: bool = False) -> None:
"""If an app-state dump exists, log it and clear it. No-op otherwise."""
try:
@@ -352,8 +352,13 @@ def log_dumped_app_state() -> None:
metadata = dataclass_from_json(DumpedAppStateMetadata, appstatedata)
+ header = (
+ 'Found app state dump from previous app run'
+ if from_previous_run
+ else 'App state dump'
+ )
out += (
- f'App state dump:\nReason: {metadata.reason}\n'
+ f'{header}:\nReason: {metadata.reason}\n'
f'Time: {metadata.app_time:.2f}'
)
tbpath = os.path.join(
@@ -383,7 +388,7 @@ class AppHealthMonitor(AppSubsystem):
def on_app_loading(self) -> None:
# If any traceback dumps happened last run, log and clear them.
- log_dumped_app_state()
+ log_dumped_app_state(from_previous_run=True)
def _app_monitor_thread_main(self) -> None:
_babase.set_thread_name('ballistica app-monitor')
diff --git a/src/assets/ba_data/python/babase/_login.py b/src/assets/ba_data/python/babase/_login.py
index cb7ebbd5..39cfa0ed 100644
--- a/src/assets/ba_data/python/babase/_login.py
+++ b/src/assets/ba_data/python/babase/_login.py
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
from typing import Callable
-DEBUG_LOG = _babase.temp_testing()
+DEBUG_LOG = False
@dataclass
diff --git a/src/assets/ba_data/python/babase/_meta.py b/src/assets/ba_data/python/babase/_meta.py
index 5e2a0ec5..76f88b76 100644
--- a/src/assets/ba_data/python/babase/_meta.py
+++ b/src/assets/ba_data/python/babase/_meta.py
@@ -24,6 +24,8 @@ if TYPE_CHECKING:
# instead of these or to make the meta system aware of arbitrary classes.
EXPORT_CLASS_NAME_SHORTCUTS: dict[str, str] = {
'plugin': 'babase.Plugin',
+ # DEPRECATED as of 12/2023. Currently am warning if finding these
+ # but should take this out eventually.
'keyboard': 'babase.Keyboard',
}
@@ -414,30 +416,27 @@ class DirectoryScan:
if export_class_name is not None:
classname = modulename + '.' + export_class_name
- # Since we'll soon have multiple versions of 'game'
- # classes we need to migrate people to using base
- # class names for them.
- if exporttypestr == 'game':
+ # Migrating away from the 'keyboard' name shortcut
+ # since it's specific to bauiv1; warn if we find it.
+ if exporttypestr == 'keyboard':
logging.warning(
"metascan: %s:%d: '# ba_meta export"
- " game' tag should be replaced by '# ba_meta"
- " export bascenev1.GameActivity'.",
+ " keyboard' tag should be replaced by '# ba_meta"
+ " export bauiv1.Keyboard'.",
subpath,
lindex + 1,
)
self.results.announce_errors_occurred = True
- else:
- # If export type is one of our shortcuts, sub in the
- # actual class path. Otherwise assume its a classpath
- # itself.
- exporttype = EXPORT_CLASS_NAME_SHORTCUTS.get(
- exporttypestr
- )
- if exporttype is None:
- exporttype = exporttypestr
- self.results.exports.setdefault(exporttype, []).append(
- classname
- )
+
+ # If export type is one of our shortcuts, sub in the
+ # actual class path. Otherwise assume its a classpath
+ # itself.
+ exporttype = EXPORT_CLASS_NAME_SHORTCUTS.get(exporttypestr)
+ if exporttype is None:
+ exporttype = exporttypestr
+ self.results.exports.setdefault(exporttype, []).append(
+ classname
+ )
def _get_export_class_name(
self, subpath: Path, lines: list[str], lindex: int
diff --git a/src/assets/ba_data/python/baclassic/_ads.py b/src/assets/ba_data/python/baclassic/_ads.py
index 0b520d22..fdb34de4 100644
--- a/src/assets/ba_data/python/baclassic/_ads.py
+++ b/src/assets/ba_data/python/baclassic/_ads.py
@@ -97,7 +97,7 @@ class AdsSubsystem:
show = True
# No ads without net-connections, etc.
- if not bauiv1.can_show_ad():
+ if not plus.can_show_ad():
show = False
if classic.accounts.have_pro():
show = False # Pro disables interstitials.
@@ -135,7 +135,7 @@ class AdsSubsystem:
# ad-show-threshold and see if we should *actually* show
# (we reach our threshold faster the longer we've been
# playing).
- base = 'ads' if bauiv1.has_video_ads() else 'ads2'
+ base = 'ads' if plus.has_video_ads() else 'ads2'
min_lc = plus.get_v1_account_misc_read_val(base + '.minLC', 0.0)
max_lc = plus.get_v1_account_misc_read_val(base + '.maxLC', 5.0)
min_lc_scale = plus.get_v1_account_misc_read_val(
diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py
index ba47b57e..35416b81 100644
--- a/src/assets/ba_data/python/baenv.py
+++ b/src/assets/ba_data/python/baenv.py
@@ -52,7 +52,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
-TARGET_BALLISTICA_BUILD = 21700
+TARGET_BALLISTICA_BUILD = 21707
TARGET_BALLISTICA_VERSION = '1.7.31'
diff --git a/src/assets/ba_data/python/baplus/_subsystem.py b/src/assets/ba_data/python/baplus/_subsystem.py
index 97a9cdb5..0b77c20f 100644
--- a/src/assets/ba_data/python/baplus/_subsystem.py
+++ b/src/assets/ba_data/python/baplus/_subsystem.py
@@ -249,3 +249,18 @@ class PlusSubsystem(AppSubsystem):
) -> None:
"""(internal)"""
return _baplus.tournament_query(callback, args)
+
+ @staticmethod
+ def have_incentivized_ad() -> bool:
+ """Is an incentivized ad available?"""
+ return _baplus.have_incentivized_ad()
+
+ @staticmethod
+ def has_video_ads() -> bool:
+ """Are video ads available?"""
+ return _baplus.has_video_ads()
+
+ @staticmethod
+ def can_show_ad() -> bool:
+ """Can we show an ad?"""
+ return _baplus.can_show_ad()
diff --git a/src/assets/ba_data/python/bascenev1/__init__.py b/src/assets/ba_data/python/bascenev1/__init__.py
index 605b363c..e6cc6a5a 100644
--- a/src/assets/ba_data/python/bascenev1/__init__.py
+++ b/src/assets/ba_data/python/bascenev1/__init__.py
@@ -78,6 +78,7 @@ from _bascenev1 import (
end_host_scanning,
get_chat_messages,
get_connection_to_host_info,
+ get_connection_to_host_info_2,
get_foreground_host_activity,
get_foreground_host_session,
get_game_port,
@@ -202,6 +203,7 @@ from bascenev1._multiteamsession import (
DEFAULT_TEAM_NAMES,
)
from bascenev1._music import MusicType, setmusic
+from bascenev1._net import HostInfo
from bascenev1._nodeactor import NodeActor
from bascenev1._powerup import get_default_powerup_distribution
from bascenev1._profile import (
@@ -303,6 +305,7 @@ __all__ = [
'GameTip',
'get_chat_messages',
'get_connection_to_host_info',
+ 'get_connection_to_host_info_2',
'get_default_free_for_all_playlist',
'get_default_teams_playlist',
'get_default_powerup_distribution',
@@ -338,6 +341,7 @@ __all__ = [
'have_connected_clients',
'have_touchscreen_input',
'HitMessage',
+ 'HostInfo',
'host_scan_cycle',
'ImpactDamageMessage',
'increment_analytics_count',
diff --git a/src/assets/ba_data/python/bascenev1/_net.py b/src/assets/ba_data/python/bascenev1/_net.py
new file mode 100644
index 00000000..279c329d
--- /dev/null
+++ b/src/assets/ba_data/python/bascenev1/_net.py
@@ -0,0 +1,24 @@
+# Released under the MIT License. See LICENSE for details.
+#
+"""Functionality related to net play."""
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from dataclasses import dataclass
+
+if TYPE_CHECKING:
+ pass
+
+
+@dataclass
+class HostInfo:
+ """Info about a host."""
+
+ name: str
+ build_number: int
+
+ # Note this can be None for non-ip hosts such as bluetooth.
+ address: str | None
+
+ # Note this can be None for non-ip hosts such as bluetooth.
+ port: int | None
diff --git a/src/assets/ba_data/python/bauiv1/__init__.py b/src/assets/ba_data/python/bauiv1/__init__.py
index fa97bb6f..5e738646 100644
--- a/src/assets/ba_data/python/bauiv1/__init__.py
+++ b/src/assets/ba_data/python/bauiv1/__init__.py
@@ -62,7 +62,6 @@ from babase import (
is_browser_likely_available,
is_running_on_fire_tv,
is_xcode_build,
- Keyboard,
lock_all_input,
LoginAdapter,
LoginInfo,
@@ -94,7 +93,6 @@ from babase import (
from _bauiv1 import (
buttonwidget,
- can_show_ad,
checkboxwidget,
columnwidget,
containerwidget,
@@ -103,8 +101,6 @@ from _bauiv1 import (
getmesh,
getsound,
gettexture,
- has_video_ads,
- have_incentivized_ad,
hscrollwidget,
imagewidget,
is_party_icon_visible,
@@ -125,6 +121,7 @@ from _bauiv1 import (
Widget,
widget,
)
+from bauiv1._keyboard import Keyboard
from bauiv1._uitypes import Window, uicleanupcheck
from bauiv1._subsystem import UIV1Subsystem
@@ -144,7 +141,6 @@ __all__ = [
'AppTimer',
'buttonwidget',
'Call',
- 'can_show_ad',
'fullscreen_control_available',
'fullscreen_control_get',
'fullscreen_control_key_shortcut',
@@ -178,8 +174,6 @@ __all__ = [
'getmesh',
'getsound',
'gettexture',
- 'has_video_ads',
- 'have_incentivized_ad',
'have_permission',
'hscrollwidget',
'imagewidget',
diff --git a/src/assets/ba_data/python/babase/_keyboard.py b/src/assets/ba_data/python/bauiv1/_keyboard.py
similarity index 100%
rename from src/assets/ba_data/python/babase/_keyboard.py
rename to src/assets/ba_data/python/bauiv1/_keyboard.py
diff --git a/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py b/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py
index 7dc42b0e..425a78e6 100644
--- a/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py
+++ b/src/assets/ba_data/python/bauiv1/onscreenkeyboard.py
@@ -12,6 +12,7 @@ from typing import TYPE_CHECKING
import babase
import _bauiv1
+from bauiv1._keyboard import Keyboard
from bauiv1._uitypes import Window
if TYPE_CHECKING:
@@ -252,9 +253,7 @@ class OnScreenKeyboardWindow(Window):
# Show change instructions only if we have more than one
# keyboard option.
keyboards = (
- babase.app.meta.scanresults.exports_of_class(
- babase.Keyboard
- )
+ babase.app.meta.scanresults.exports_of_class(Keyboard)
if babase.app.meta.scanresults is not None
else []
)
@@ -286,10 +285,10 @@ class OnScreenKeyboardWindow(Window):
def _get_keyboard(self) -> bui.Keyboard:
assert babase.app.meta.scanresults is not None
- classname = babase.app.meta.scanresults.exports_of_class(
- babase.Keyboard
- )[self._keyboard_index]
- kbclass = babase.getclass(classname, babase.Keyboard)
+ classname = babase.app.meta.scanresults.exports_of_class(Keyboard)[
+ self._keyboard_index
+ ]
+ kbclass = babase.getclass(classname, Keyboard)
return kbclass()
def _refresh(self) -> None:
@@ -384,9 +383,7 @@ class OnScreenKeyboardWindow(Window):
def _next_keyboard(self) -> None:
assert babase.app.meta.scanresults is not None
- kbexports = babase.app.meta.scanresults.exports_of_class(
- babase.Keyboard
- )
+ kbexports = babase.app.meta.scanresults.exports_of_class(Keyboard)
self._keyboard_index = (self._keyboard_index + 1) % len(kbexports)
self._load_keyboard()
diff --git a/src/assets/ba_data/python/bauiv1lib/coop/browser.py b/src/assets/ba_data/python/bauiv1lib/coop/browser.py
index 256e6771..c46c552d 100644
--- a/src/assets/ba_data/python/bauiv1lib/coop/browser.py
+++ b/src/assets/ba_data/python/bauiv1lib/coop/browser.py
@@ -415,7 +415,7 @@ class CoopBrowserWindow(bui.Window):
)
# Decrement time on our tournament buttons.
- ads_enabled = bui.have_incentivized_ad()
+ ads_enabled = plus.have_incentivized_ad()
for tbtn in self._tournament_buttons:
tbtn.time_remaining = max(0, tbtn.time_remaining - 1)
if tbtn.time_remaining_value_text is not None:
@@ -430,7 +430,7 @@ class CoopBrowserWindow(bui.Window):
)
# Also adjust the ad icon visibility.
- if tbtn.allow_ads and bui.has_video_ads():
+ if tbtn.allow_ads and plus.has_video_ads():
bui.imagewidget(
edit=tbtn.entry_fee_ad_image,
opacity=1.0 if ads_enabled else 0.25,
diff --git a/src/assets/ba_data/python/bauiv1lib/coop/tournamentbutton.py b/src/assets/ba_data/python/bauiv1lib/coop/tournamentbutton.py
index 72d604e6..4b766379 100644
--- a/src/assets/ba_data/python/bauiv1lib/coop/tournamentbutton.py
+++ b/src/assets/ba_data/python/bauiv1lib/coop/tournamentbutton.py
@@ -638,8 +638,8 @@ class TournamentButton:
# Now, if this fee allows ads and we support video ads, show
# the 'or ad' version.
- if allow_ads and bui.has_video_ads():
- ads_enabled = bui.have_incentivized_ad()
+ if allow_ads and plus.has_video_ads():
+ ads_enabled = plus.have_incentivized_ad()
bui.imagewidget(
edit=self.entry_fee_ad_image,
opacity=1.0 if ads_enabled else 0.25,
diff --git a/src/assets/ba_data/python/bauiv1lib/getcurrency.py b/src/assets/ba_data/python/bauiv1lib/getcurrency.py
index 78ab9af1..6e355d42 100644
--- a/src/assets/ba_data/python/bauiv1lib/getcurrency.py
+++ b/src/assets/ba_data/python/bauiv1lib/getcurrency.py
@@ -334,7 +334,7 @@ class GetCurrencyWindow(bui.Window):
tex_scale=1.2,
) # 19.99-ish
- self._enable_ad_button = bui.has_video_ads()
+ self._enable_ad_button = plus.has_video_ads()
h = self._width * 0.5 + 110.0
v = self._height - b_size[1] - 115.0
@@ -561,7 +561,7 @@ class GetCurrencyWindow(bui.Window):
next_reward_ad_time
)
now = datetime.datetime.utcnow()
- if bui.have_incentivized_ad() and (
+ if plus.have_incentivized_ad() and (
next_reward_ad_time is None or next_reward_ad_time <= now
):
self._ad_button_greyed = False
diff --git a/src/assets/ba_data/python/bauiv1lib/keyboard/englishkeyboard.py b/src/assets/ba_data/python/bauiv1lib/keyboard/englishkeyboard.py
index ae94d938..3de74f87 100644
--- a/src/assets/ba_data/python/bauiv1lib/keyboard/englishkeyboard.py
+++ b/src/assets/ba_data/python/bauiv1lib/keyboard/englishkeyboard.py
@@ -9,7 +9,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
-import babase
+import bauiv1 as bui
if TYPE_CHECKING:
from typing import Iterable
@@ -33,15 +33,15 @@ def split(chars: Iterable[str], maxlen: int) -> list[list[str]]:
def generate_emojis(maxlen: int) -> list[list[str]]:
- """Generates a lot of UTF8 emojis prepared for babase.Keyboard pages"""
+ """Generates a lot of UTF8 emojis prepared for bui.Keyboard pages"""
all_emojis = split([chr(i) for i in range(0x1F601, 0x1F650)], maxlen)
all_emojis += split([chr(i) for i in range(0x2702, 0x27B1)], maxlen)
all_emojis += split([chr(i) for i in range(0x1F680, 0x1F6C1)], maxlen)
return all_emojis
-# ba_meta export keyboard
-class EnglishKeyboard(babase.Keyboard):
+# ba_meta export bauiv1.Keyboard
+class EnglishKeyboard(bui.Keyboard):
"""Default English keyboard."""
name = 'English'
diff --git a/src/assets/ba_data/python/bauiv1lib/party.py b/src/assets/ba_data/python/bauiv1lib/party.py
index 5920c2d8..f9250334 100644
--- a/src/assets/ba_data/python/bauiv1lib/party.py
+++ b/src/assets/ba_data/python/bauiv1lib/party.py
@@ -92,9 +92,10 @@ class PartyWindow(bui.Window):
iconscale=1.2,
)
- info = bs.get_connection_to_host_info()
- if info.get('name', '') != '':
- title = bui.Lstr(value=info['name'])
+ info = bs.get_connection_to_host_info_2()
+
+ if info is not None and info.name != '':
+ title = bui.Lstr(value=info.name)
else:
title = bui.Lstr(resource=self._r + '.titleText')
@@ -481,7 +482,8 @@ class PartyWindow(bui.Window):
kick_str = bui.Lstr(resource='kickText')
else:
# kick-votes appeared in build 14248
- if bs.get_connection_to_host_info().get('build_number', 0) < 14248:
+ info = bs.get_connection_to_host_info_2()
+ if info is None or info.build_number < 14248:
return
kick_str = bui.Lstr(resource='kickVoteText')
assert bui.app.classic is not None
diff --git a/src/assets/ba_data/python/bauiv1lib/tournamententry.py b/src/assets/ba_data/python/bauiv1lib/tournamententry.py
index fcba3e99..d00c37dd 100644
--- a/src/assets/ba_data/python/bauiv1lib/tournamententry.py
+++ b/src/assets/ba_data/python/bauiv1lib/tournamententry.py
@@ -34,6 +34,7 @@ class TournamentEntryWindow(PopupWindow):
# pylint: disable=too-many-statements
assert bui.app.classic is not None
+ assert bui.app.plus
bui.set_analytics_screen('Tournament Entry Window')
self._tournament_id = tournament_id
@@ -100,7 +101,7 @@ class TournamentEntryWindow(PopupWindow):
self._launched = False
# Show the ad button only if we support ads *and* it has a level 1 fee.
- self._do_ad_btn = bui.has_video_ads() and self._allow_ads
+ self._do_ad_btn = bui.app.plus.has_video_ads() and self._allow_ads
x_offs = 0 if self._do_ad_btn else 85
@@ -477,7 +478,7 @@ class TournamentEntryWindow(PopupWindow):
)
if self._do_ad_btn:
- enabled = bui.have_incentivized_ad()
+ enabled = plus.have_incentivized_ad()
have_ad_tries_remaining = (
self._tournament_info['adTriesRemaining'] is not None
and self._tournament_info['adTriesRemaining'] > 0
diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc
index 45d6c2c7..a174c575 100644
--- a/src/ballistica/base/base.cc
+++ b/src/ballistica/base/base.cc
@@ -195,6 +195,12 @@ void BaseFeatureSet::OnAssetsAvailable() {
}
void BaseFeatureSet::StartApp() {
+ // {
+ // // TEST - recreate the ID python dumps in its thread tracebacks.
+ // auto val = PyThread_get_thread_ident();
+ // printf("MAIN THREAD IS %#018lx\n", val);
+ // }
+
BA_PRECONDITION(g_core->InMainThread());
BA_PRECONDITION(g_base);
diff --git a/src/ballistica/base/networking/networking.cc b/src/ballistica/base/networking/networking.cc
index 696eb7a1..3b83f698 100644
--- a/src/ballistica/base/networking/networking.cc
+++ b/src/ballistica/base/networking/networking.cc
@@ -50,7 +50,7 @@ void Networking::SendTo(const std::vector& buffer,
if (sd != -1) {
sendto(sd, (const char*)&buffer[0],
static_cast_check_fit(buffer.size()), 0,
- addr.GetSockAddr(), addr.GetSockAddrLen());
+ addr.AsSockAddr(), addr.GetSockAddrLen());
}
}
diff --git a/src/ballistica/base/platform/base_platform.cc b/src/ballistica/base/platform/base_platform.cc
index 0a1d4949..f05d2b26 100644
--- a/src/ballistica/base/platform/base_platform.cc
+++ b/src/ballistica/base/platform/base_platform.cc
@@ -58,8 +58,8 @@ auto BasePlatform::GetPublicDeviceUUID() -> std::string {
// We used to plug version in directly here, but that caused uuids to
// shuffle too rapidly during periods of rapid development. This
// keeps it more constant.
- // __last_rand_uuid_component_shuffle_date__ 2023 6 15
- auto rand_uuid_component{"JVRWZ82D4WMBO110OA0IFJV7JKMQV8W3"};
+ // __last_rand_uuid_component_shuffle_date__ 2023 12 13
+ auto rand_uuid_component{"7YM96RZHN6ZCPZGTQONULZO1JU5NMMC7"};
inputs.emplace_back(rand_uuid_component);
auto gil{Python::ScopedInterpreterLock()};
diff --git a/src/ballistica/core/core.cc b/src/ballistica/core/core.cc
index 002a4388..f7bdc1ca 100644
--- a/src/ballistica/core/core.cc
+++ b/src/ballistica/core/core.cc
@@ -368,12 +368,64 @@ void CoreFeatureSet::StartSuicideTimer(const std::string& action,
}
}
-// auto CoreFeatureSet::InMainThread() -> bool {
-// return std::this_thread::get_id() == main_thread_id;
-// // if (main_event_loop_) {
-// // return main_event_loop_->ThreadIsCurrent();
-// // }
-// // return false;
-// }
+void CoreFeatureSet::RegisterThread(const std::string& name) {
+ {
+ std::scoped_lock lock(thread_info_map_mutex_);
+
+ // Should be registering each thread just once.
+ assert(thread_info_map_.find(std::this_thread::get_id())
+ == thread_info_map_.end());
+ thread_info_map_[std::this_thread::get_id()] = name;
+ }
+
+ // Also set the name at the OS leve when possible. Prepend 'ballistica'
+ // since there's generally lots of other random threads in the mix.
+ //
+ // Note that we currently don't do this for our main thread because (on
+ // Linux at least) that changes the process name we see in top/etc. On
+ // other platforms we could reconsider, but its generally clear what the
+ // main thread is anyway in most scenarios.
+ if (!InMainThread()) {
+ g_core->platform->SetCurrentThreadName("ballistica " + name);
+ }
+}
+
+void CoreFeatureSet::UnregisterThread() {
+ std::scoped_lock lock(thread_info_map_mutex_);
+ auto i = thread_info_map_.find(std::this_thread::get_id());
+ assert(i != thread_info_map_.end());
+ if (i != thread_info_map_.end()) {
+ thread_info_map_.erase(i);
+ }
+}
+
+auto CoreFeatureSet::CurrentThreadName() -> std::string {
+ if (g_core == nullptr) {
+ return "unknown(not-yet-inited)";
+ }
+ {
+ std::scoped_lock lock(g_core->thread_info_map_mutex_);
+ auto i = g_core->thread_info_map_.find(std::this_thread::get_id());
+ if (i != g_core->thread_info_map_.end()) {
+ return i->second;
+ }
+ }
+
+ // Ask pthread for the thread name if we don't have one.
+ // FIXME - move this to platform.
+#if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS || BA_OSTYPE_LINUX
+ std::string name = "unknown (sys-name=";
+ char buffer[256];
+ int result = pthread_getname_np(pthread_self(), buffer, sizeof(buffer));
+ if (result == 0) {
+ name += std::string("\"") + buffer + "\")";
+ } else {
+ name += "";
+ }
+ return name;
+#else
+ return "unknown";
+#endif
+}
} // namespace ballistica::core
diff --git a/src/ballistica/core/core.h b/src/ballistica/core/core.h
index be60774f..d31d0e8d 100644
--- a/src/ballistica/core/core.h
+++ b/src/ballistica/core/core.h
@@ -144,6 +144,12 @@ class CoreFeatureSet {
return using_custom_app_python_dir_;
}
+ /// Register various info about the current thread.
+ void RegisterThread(const std::string& name);
+
+ /// Should be called by a thread before it exits.
+ void UnregisterThread();
+
// Subsystems.
CorePython* const python;
CorePlatform* const platform;
@@ -158,8 +164,6 @@ class CoreFeatureSet {
bool v1_cloud_log_full{};
int master_server_source{};
std::vector suspendable_event_loops;
- std::mutex thread_name_map_mutex;
- std::unordered_map thread_name_map;
std::mutex v1_cloud_log_mutex;
std::string v1_cloud_log;
@@ -173,6 +177,7 @@ class CoreFeatureSet {
auto vr_mode() const { return vr_mode_; }
auto event_loops_suspended() const { return event_loops_suspended_; }
void set_event_loops_suspended(bool val) { event_loops_suspended_ = val; }
+ static auto CurrentThreadName() -> std::string;
private:
explicit CoreFeatureSet(CoreConfig config);
@@ -204,6 +209,8 @@ class CoreFeatureSet {
std::optional ba_env_user_python_dir_;
std::optional ba_env_site_python_dir_;
std::string ba_env_data_dir_;
+ std::mutex thread_info_map_mutex_;
+ std::unordered_map thread_info_map_;
};
} // namespace ballistica::core
diff --git a/src/ballistica/core/platform/apple/core_platform_apple.cc b/src/ballistica/core/platform/apple/core_platform_apple.cc
index aa9f72d0..df8281f6 100644
--- a/src/ballistica/core/platform/apple/core_platform_apple.cc
+++ b/src/ballistica/core/platform/apple/core_platform_apple.cc
@@ -466,7 +466,7 @@ auto CorePlatformApple::CanShowBlockingFatalErrorDialog() -> bool {
if (g_buildconfig.xcode_build() && g_buildconfig.ostype_macos()) {
return true;
}
- return false;
+ return CorePlatform::CanShowBlockingFatalErrorDialog();
}
void CorePlatformApple::BlockingFatalErrorDialog(const std::string& message) {
diff --git a/src/ballistica/core/platform/core_platform.cc b/src/ballistica/core/platform/core_platform.cc
index 0abf2ad0..274c0996 100644
--- a/src/ballistica/core/platform/core_platform.cc
+++ b/src/ballistica/core/platform/core_platform.cc
@@ -826,11 +826,9 @@ auto CorePlatform::MacMusicAppGetPlaylists() -> std::list {
}
void CorePlatform::SetCurrentThreadName(const std::string& name) {
- // Currently we leave the main thread alone, otherwise we show up as
- // "BallisticaMainThread" under "top" on linux (should check other platforms).
- if (g_core->InMainThread()) {
- return;
- }
+ // We should never be doing this for the main thread.
+ BA_PRECONDITION_FATAL(!g_core->InMainThread());
+
#if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS
pthread_setname_np(name.c_str());
#elif BA_OSTYPE_LINUX || BA_OSTYPE_ANDROID
diff --git a/src/ballistica/scene_v1/connection/connection_to_host_udp.cc b/src/ballistica/scene_v1/connection/connection_to_host_udp.cc
index 97bf9271..3ae741eb 100644
--- a/src/ballistica/scene_v1/connection/connection_to_host_udp.cc
+++ b/src/ballistica/scene_v1/connection/connection_to_host_udp.cc
@@ -18,7 +18,7 @@ auto ConnectionToHostUDP::SwitchProtocol() -> bool {
// Need a new request id so we ignore further responses to our previous
// requests.
- GetRequestID();
+ GetRequestID_();
return true;
}
return false;
@@ -32,7 +32,7 @@ ConnectionToHostUDP::ConnectionToHostUDP(const SockAddr& addr)
did_die_(false),
last_host_response_time_millisecs_(
static_cast(g_base->logic->display_time() * 1000.0)) {
- GetRequestID();
+ GetRequestID_();
if (auto* appmode = SceneV1AppMode::GetActiveOrWarn()) {
if (appmode->connections()->GetPrintUDPConnectProgress()) {
ScreenMessage(g_base->assets->GetResourceString("connectingToPartyText"));
@@ -46,11 +46,11 @@ ConnectionToHostUDP::~ConnectionToHostUDP() {
set_connection_dying(true);
}
-void ConnectionToHostUDP::GetRequestID() {
+void ConnectionToHostUDP::GetRequestID_() {
// We store a unique-ish request ID to minimize the chance that data for
- // previous connections/etc will muck with us.
- // Try to start this value at something that won't be common in packets to
- // minimize chance of garbage packets causing trouble.
+ // previous connections/etc will muck with us. Try to start this value at
+ // something that won't be common in packets to minimize chance of garbage
+ // packets causing trouble.
static auto next_request_id =
static_cast(71 + (rand() % 151)); // NOLINT
request_id_ = next_request_id++;
@@ -95,13 +95,14 @@ void ConnectionToHostUDP::Update() {
{1, 0, 0});
}
- // Die immediately in this case; no use trying to wait for a disconnect-ack
- // since we've already given up hope of hearing from them.
+ // Die immediately in this case; no use trying to wait for a
+ // disconnect-ack since we've already given up hope of hearing from
+ // them.
Die();
return;
} else if (errored()) {
- // If we've errored, keep sending disconnect-requests periodically.
- // Once we get a response (or time out in the above code) we'll die.
+ // If we've errored, keep sending disconnect-requests periodically. Once
+ // we get a response (or time out in the above code) we'll die.
if (current_time_millisecs - last_disconnect_request_time_ > 1000) {
last_disconnect_request_time_ = current_time_millisecs;
@@ -189,8 +190,8 @@ void ConnectionToHostUDP::Error(const std::string& msg) {
auto ConnectionToHostUDP::GetAsUDP() -> ConnectionToHostUDP* { return this; }
void ConnectionToHostUDP::RequestDisconnect() {
- // Mark us as errored so all future communication results in more disconnect
- // requests.
+ // Mark us as errored so all future communication results in more
+ // disconnect requests.
set_errored(true);
if (client_id_ != -1) {
SendDisconnectRequest();
diff --git a/src/ballistica/scene_v1/connection/connection_to_host_udp.h b/src/ballistica/scene_v1/connection/connection_to_host_udp.h
index b085b759..58cb4a04 100644
--- a/src/ballistica/scene_v1/connection/connection_to_host_udp.h
+++ b/src/ballistica/scene_v1/connection/connection_to_host_udp.h
@@ -23,8 +23,8 @@ class ConnectionToHostUDP : public ConnectionToHost {
void set_client_id(int val) { client_id_ = val; }
auto client_id() const -> int { return client_id_; }
- // Attempt connecting via a different protocol. If none are left to try,
- // returns false.
+ /// Attempt connecting via a different protocol. If none are left to try,
+ /// returns false.
auto SwitchProtocol() -> bool;
void RequestDisconnect() override;
@@ -32,16 +32,18 @@ class ConnectionToHostUDP : public ConnectionToHost {
void Error(const std::string& error_msg) override;
void Die();
void SendDisconnectRequest();
+ const auto& addr() const { return *addr_; }
private:
- void GetRequestID();
- uint8_t request_id_{};
- std::unique_ptr addr_;
+ void GetRequestID_();
+
bool did_die_{};
+ uint8_t request_id_{};
+ int client_id_{};
millisecs_t last_client_id_request_time_{};
millisecs_t last_disconnect_request_time_{};
- int client_id_{};
millisecs_t last_host_response_time_millisecs_{};
+ std::unique_ptr addr_;
};
} // namespace ballistica::scene_v1
diff --git a/src/ballistica/scene_v1/python/methods/python_methods_networking.cc b/src/ballistica/scene_v1/python/methods/python_methods_networking.cc
index b254752b..72b824d1 100644
--- a/src/ballistica/scene_v1/python/methods/python_methods_networking.cc
+++ b/src/ballistica/scene_v1/python/methods/python_methods_networking.cc
@@ -5,13 +5,17 @@
#include "ballistica/base/assets/assets.h"
#include "ballistica/base/networking/network_reader.h"
#include "ballistica/base/python/base_python.h"
+#include "ballistica/core/python/core_python.h"
#include "ballistica/scene_v1/connection/connection_set.h"
#include "ballistica/scene_v1/connection/connection_to_client.h"
#include "ballistica/scene_v1/connection/connection_to_host.h"
+#include "ballistica/scene_v1/connection/connection_to_host_udp.h"
+#include "ballistica/scene_v1/python/scene_v1_python.h"
#include "ballistica/scene_v1/support/scene_v1_app_mode.h"
#include "ballistica/shared/math/vector3f.h"
#include "ballistica/shared/networking/sockaddr.h"
#include "ballistica/shared/python/python.h"
+#include "ballistica/shared/python/python_ref.h"
#include "ballistica/shared/python/python_sys.h"
namespace ballistica::scene_v1 {
@@ -20,8 +24,7 @@ namespace ballistica::scene_v1 {
#pragma clang diagnostic push
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
-// ------------------------- get_public_party_enabled
-// ---------------------------
+// ----------------------- get_public_party_enabled ---------------------------
static auto PyGetPublicPartyEnabled(PyObject* self, PyObject* args,
PyObject* keywds) -> PyObject* {
@@ -411,7 +414,10 @@ static auto PyGetConnectionToHostInfo(PyObject* self, PyObject* args,
const_cast(kwlist))) {
return nullptr;
}
- // Error if we're not in our app-mode.
+ BA_LOG_ONCE(LogLevel::kWarning,
+ "bascenev1.get_connection_to_host_info() is deprecated; use "
+ "bascenev1.get_connection_to_host_info_2().");
+ BA_PRECONDITION(g_base->InLogicThread());
auto* appmode = SceneV1AppMode::GetActiveOrThrow();
ConnectionToHost* hc = appmode->connections()->connection_to_host();
@@ -435,6 +441,57 @@ static PyMethodDef PyGetConnectionToHostInfoDef = {
"(internal)",
};
+// --------------------- get_connection_to_host_info_2 -------------------------
+
+static auto PyGetConnectionToHostInfo2(PyObject* self, PyObject* args,
+ PyObject* keywds) -> PyObject* {
+ BA_PYTHON_TRY;
+ static const char* kwlist[] = {nullptr};
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "",
+ const_cast(kwlist))) {
+ return nullptr;
+ }
+ BA_PRECONDITION(g_base->InLogicThread());
+ auto* appmode = SceneV1AppMode::GetActiveOrThrow();
+
+ ConnectionToHost* hc = appmode->connections()->connection_to_host();
+ if (hc) {
+ PythonRef addr_obj;
+ PythonRef port_obj;
+ if (ConnectionToHostUDP* hcu = dynamic_cast(hc)) {
+ addr_obj.Steal(PyUnicode_FromString(hcu->addr().AddressString().c_str()));
+ port_obj.Steal(PyLong_FromLong(hcu->addr().Port()));
+ } else {
+ addr_obj.Acquire(Py_None);
+ port_obj.Acquire(Py_None);
+ }
+ auto args =
+ g_core->python->objs().Get(core::CorePython::ObjID::kEmptyTuple);
+ auto keywds = PythonRef::Stolen(Py_BuildValue(
+ "{sssisOsO}", "name", hc->party_name().c_str(), "build_number",
+ hc->build_number(), "address", addr_obj.Get(), "port", port_obj.Get()));
+ auto result = g_scene_v1->python->objs()
+ .Get(SceneV1Python::ObjID::kHostInfoClass)
+ .Call(args, keywds);
+ if (!result.Exists()) {
+ throw Exception("Failed to instantiate HostInfo.", PyExcType::kRuntime);
+ }
+ return result.HandOver();
+ }
+ Py_RETURN_NONE;
+ BA_PYTHON_CATCH;
+}
+
+static PyMethodDef PyGetConnectionToHostInfo2Def = {
+ "get_connection_to_host_info_2", // name
+ (PyCFunction)PyGetConnectionToHostInfo2, // method
+ METH_VARARGS | METH_KEYWORDS, // flags
+
+ "get_connection_to_host_info_2() -> bascenev1.HostInfo | None\n"
+ "\n"
+ "Return info about the host we are currently connected to.",
+};
+
// --------------------------- disconnect_from_host ----------------------------
static auto PyDisconnectFromHost(PyObject* self, PyObject* args,
@@ -701,6 +758,7 @@ static auto PyChatMessage(PyObject* self, PyObject* args, PyObject* keywds)
&clients_obj, &sender_override_obj)) {
return nullptr;
}
+ BA_PRECONDITION(g_base->InLogicThread());
auto* appmode = SceneV1AppMode::GetActiveOrThrow();
message = g_base->python->GetPyLString(message_obj);
@@ -775,6 +833,7 @@ auto PythonMethodsNetworking::GetMethods() -> std::vector {
PyDisconnectClientDef,
PyGetClientPublicDeviceUUIDDef,
PyGetConnectionToHostInfoDef,
+ PyGetConnectionToHostInfo2Def,
PyClientInfoQueryResponseDef,
PyConnectToPartyDef,
PySetAuthenticateClientsDef,
diff --git a/src/ballistica/scene_v1/python/scene_v1_python.h b/src/ballistica/scene_v1/python/scene_v1_python.h
index 404e72f7..afa67832 100644
--- a/src/ballistica/scene_v1/python/scene_v1_python.h
+++ b/src/ballistica/scene_v1/python/scene_v1_python.h
@@ -91,6 +91,7 @@ class SceneV1Python {
kGetPlayerIconCall,
kFilterChatMessageCall,
kHandleLocalChatMessageCall,
+ kHostInfoClass,
kLast // Sentinel; must be at end.
};
diff --git a/src/ballistica/scene_v1/support/client_session_net.h b/src/ballistica/scene_v1/support/client_session_net.h
index 075dda57..7a6a213f 100644
--- a/src/ballistica/scene_v1/support/client_session_net.h
+++ b/src/ballistica/scene_v1/support/client_session_net.h
@@ -37,21 +37,16 @@ class ClientSessionNet : public ClientSession {
auto GetBucketNum() -> int;
bool writing_replay_{};
+ int delay_sample_counter_{};
+ float max_delay_smoothed_{};
+ float last_bucket_max_delay_{};
+ float current_delay_{};
millisecs_t base_time_received_{};
millisecs_t last_base_time_receive_time_{};
millisecs_t leading_base_time_received_{};
millisecs_t leading_base_time_receive_time_{};
Object::WeakRef connection_to_host_;
std::vector buckets_{5};
-
- // float bucket_max_smoothed_{};
- // float bucket_min_smoothed_{};
- float max_delay_smoothed_{};
- float last_bucket_max_delay_{};
- float current_delay_{};
-
- int delay_sample_counter_{};
- // int adjust_counter_{};
};
} // namespace ballistica::scene_v1
diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc
index 2dec51aa..27f3b924 100644
--- a/src/ballistica/shared/ballistica.cc
+++ b/src/ballistica/shared/ballistica.cc
@@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica {
// These are set automatically via script; don't modify them here.
-const int kEngineBuildNumber = 21700;
+const int kEngineBuildNumber = 21707;
const char* kEngineVersion = "1.7.31";
const int kEngineApiVersion = 8;
@@ -290,7 +290,7 @@ void ScreenMessage(const std::string& msg) {
auto CurrentThreadName() -> std::string {
// Currently just ask event-loop for this but perhaps should be talking
// more directly to the OS/etc. to cover more cases.
- return EventLoop::CurrentThreadName();
+ return core::CoreFeatureSet::CurrentThreadName();
}
} // namespace ballistica
diff --git a/src/ballistica/shared/foundation/event_loop.cc b/src/ballistica/shared/foundation/event_loop.cc
index 7ed704a6..b3711495 100644
--- a/src/ballistica/shared/foundation/event_loop.cc
+++ b/src/ballistica/shared/foundation/event_loop.cc
@@ -98,21 +98,6 @@ EventLoop::EventLoop(EventLoopID identifier_in, ThreadSource source)
}
}
-void EventLoop::SetInternalThreadName_(const std::string& name) {
- assert(g_core);
- std::scoped_lock lock(g_core->thread_name_map_mutex);
- g_core->thread_name_map[std::this_thread::get_id()] = name;
-}
-
-void EventLoop::ClearCurrentThreadName() {
- assert(g_core);
- std::scoped_lock lock(g_core->thread_name_map_mutex);
- auto i = g_core->thread_name_map.find(std::this_thread::get_id());
- if (i != g_core->thread_name_map.end()) {
- g_core->thread_name_map.erase(i);
- }
-}
-
// These are all exactly the same; its just a way to try and clarify
// in stack traces which thread is running in case it is not otherwise
// evident.
@@ -341,53 +326,40 @@ void EventLoop::GetThreadMessages_(std::list* messages) {
void EventLoop::BootstrapThread_() {
assert(!bootstrapped_);
+ assert(g_core);
thread_id_ = std::this_thread::get_id();
const char* id_string;
switch (identifier_) {
case EventLoopID::kLogic:
name_ = "logic";
- id_string = "ballistica logic";
break;
case EventLoopID::kStdin:
name_ = "stdin";
- id_string = "ballistica stdin";
break;
case EventLoopID::kAssets:
name_ = "assets";
- id_string = "ballistica assets";
break;
case EventLoopID::kFileOut:
name_ = "fileout";
- id_string = "ballistica file-out";
break;
case EventLoopID::kMain:
name_ = "main";
- id_string = "ballistica main";
break;
case EventLoopID::kAudio:
name_ = "audio";
- id_string = "ballistica audio";
break;
case EventLoopID::kBGDynamics:
name_ = "bgdynamics";
- id_string = "ballistica bg-dynamics";
break;
case EventLoopID::kNetworkWrite:
name_ = "networkwrite";
- id_string = "ballistica network-write";
break;
default:
throw Exception();
}
- assert(!name_.empty() && id_string);
- SetInternalThreadName_(name_);
-
- // Note: we currently don't do this for our main thread because it
- // changes the process name we see in top/etc. Should look into that.
- if (identifier_ != EventLoopID::kMain) {
- g_core->platform->SetCurrentThreadName(id_string);
- }
+ assert(!name_.empty());
+ g_core->RegisterThread(name_);
bootstrapped_ = true;
}
@@ -410,7 +382,7 @@ auto EventLoop::ThreadMain_() -> int {
RunToCompletion();
- ClearCurrentThreadName();
+ g_core->UnregisterThread();
return 0;
} catch (const std::exception& e) {
auto error_msg = std::string("Unhandled exception in ")
@@ -622,35 +594,6 @@ void EventLoop::DeleteTimer(int id) {
timers_.DeleteTimer(id);
}
-auto EventLoop::CurrentThreadName() -> std::string {
- if (g_core == nullptr) {
- return "unknown(not-yet-inited)";
- }
- {
- std::scoped_lock lock(g_core->thread_name_map_mutex);
- auto i = g_core->thread_name_map.find(std::this_thread::get_id());
- if (i != g_core->thread_name_map.end()) {
- return i->second;
- }
- }
-
- // Ask pthread for the thread name if we don't have one.
- // FIXME - move this to platform.
-#if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS || BA_OSTYPE_LINUX
- std::string name = "unknown (sys-name=";
- char buffer[256];
- int result = pthread_getname_np(pthread_self(), buffer, sizeof(buffer));
- if (result == 0) {
- name += std::string("\"") + buffer + "\")";
- } else {
- name += "";
- }
- return name;
-#else
- return "unknown";
-#endif
-}
-
void EventLoop::RunPendingRunnables_() {
// Pull all runnables off the list first (its possible for one of these
// runnables to add more) and then process them.
diff --git a/src/ballistica/shared/foundation/event_loop.h b/src/ballistica/shared/foundation/event_loop.h
index 186c52de..8bb761a6 100644
--- a/src/ballistica/shared/foundation/event_loop.h
+++ b/src/ballistica/shared/foundation/event_loop.h
@@ -25,10 +25,6 @@ class EventLoop {
ThreadSource source = ThreadSource::kCreate);
virtual ~EventLoop();
- void ClearCurrentThreadName();
-
- static auto CurrentThreadName() -> std::string;
-
static void SetEventLoopsSuspended(bool enable);
static auto AreEventLoopsSuspended() -> bool;
@@ -113,7 +109,6 @@ class EventLoop {
: type(type), runnable(runnable), completion_flag{completion_flag} {}
};
auto CheckPushRunnableSafety_() -> bool;
- void SetInternalThreadName_(const std::string& name);
void WaitForNextEvent_(bool single_cycle);
void LogThreadMessageTally_(
std::vector>* log_entries);
diff --git a/src/ballistica/shared/foundation/fatal_error.cc b/src/ballistica/shared/foundation/fatal_error.cc
index be39d384..4bed5b6a 100644
--- a/src/ballistica/shared/foundation/fatal_error.cc
+++ b/src/ballistica/shared/foundation/fatal_error.cc
@@ -91,12 +91,16 @@ void FatalError::ReportFatalError(const std::string& message,
if (trace) {
std::string tracestr = trace->FormatForDisplay();
if (!tracestr.empty()) {
- logmsg += ("\nCPP-STACK-TRACE-BEGIN:\n" + tracestr
- + "\nCPP-STACK-TRACE-END");
+ logmsg +=
+ (("\n----------------------- BALLISTICA-NATIVE-STACK-TRACE-BEGIN "
+ "--------------------\n")
+ + tracestr
+ + ("\n----------------------- BALLISTICA-NATIVE-STACK-TRACE-END "
+ "----------------------"));
}
delete trace;
} else {
- logmsg += "\n(CPP-STACK-TRACE-UNAVAILABLE)";
+ logmsg += "\n(BALLISTICA-NATIVE-STACK-TRACE-UNAVAILABLE)";
}
}
}
@@ -155,12 +159,10 @@ void FatalError::DoBlockingFatalErrorDialog(const std::string& message) {
bool* startedptr{&started};
bool* finishedptr{&finished};
- // If our thread is holding the GIL, release it to give the main thread
- // a better chance of getting to the point of displaying the fatal
- // error.
- if (Python::HaveGIL()) {
- Python::PermanentlyReleaseGIL();
- }
+ // If our thread is holding the GIL, release it while we spin; otherwise
+ // we can wind up in deadlock if the main thread wants it.
+ Python::ScopedInterpreterLockRelease gil_release;
+
g_base_soft->PushMainThreadRunnable(
NewLambdaRunnableUnmanaged([message, startedptr, finishedptr] {
*startedptr = true;
diff --git a/src/ballistica/shared/networking/sockaddr.cc b/src/ballistica/shared/networking/sockaddr.cc
index 68fdf993..6dd5b190 100644
--- a/src/ballistica/shared/networking/sockaddr.cc
+++ b/src/ballistica/shared/networking/sockaddr.cc
@@ -14,22 +14,48 @@ SockAddr::SockAddr(const std::string& addr, int port) {
if (result == 1) {
auto* a = reinterpret_cast(&addr_);
a->sin_family = AF_INET;
- a->sin_port = htons(port); // NOLINT
+ a->sin_port = htons(port);
a->sin_addr = addr_out;
return;
} else {
- struct in6_addr addr6Out {};
- result = inet_pton(AF_INET6, addr.c_str(), &addr6Out);
+ struct in6_addr addr6_out {};
+ result = inet_pton(AF_INET6, addr.c_str(), &addr6_out);
if (result == 1) {
auto* a = reinterpret_cast(&addr_);
a->sin6_family = AF_INET6;
- a->sin6_port = htons(port); // NOLINT
- a->sin6_addr = addr6Out;
+ a->sin6_port = htons(port);
+ a->sin6_addr = addr6_out;
return;
}
}
}
- throw Exception("Invalid address: '" + addr + "'.");
+ throw Exception("Invalid address: '" + addr + "'.", PyExcType::kValue);
+}
+
+auto SockAddr::AddressString() const -> std::string {
+ if (IsV6()) {
+ char ip_str[INET6_ADDRSTRLEN];
+ if (inet_ntop(AF_INET6, &(AsSockAddrIn6()->sin6_addr), ip_str,
+ INET6_ADDRSTRLEN)
+ == nullptr) {
+ throw Exception("inet_ntop failed for v6 addr", PyExcType::kValue);
+ }
+ return ip_str;
+ }
+ char ip_str[INET_ADDRSTRLEN];
+ if (inet_ntop(AF_INET, &(AsSockAddrIn()->sin_addr), ip_str, INET_ADDRSTRLEN)
+ == nullptr) {
+ throw Exception("inet_ntop failed for v4 addr", PyExcType::kValue);
+ }
+ return ip_str;
+}
+
+auto SockAddr::Port() const -> int {
+ if (IsV6()) {
+ return ntohs(AsSockAddrIn6()->sin6_port);
+ } else {
+ return ntohs(AsSockAddrIn()->sin_port);
+ }
}
} // namespace ballistica
diff --git a/src/ballistica/shared/networking/sockaddr.h b/src/ballistica/shared/networking/sockaddr.h
index f372bf70..3e880c56 100644
--- a/src/ballistica/shared/networking/sockaddr.h
+++ b/src/ballistica/shared/networking/sockaddr.h
@@ -15,16 +15,33 @@ class SockAddr {
public:
SockAddr() { memset(&addr_, 0, sizeof(addr_)); }
- // Creates from an ipv4 or ipv6 address string;
- // throws an exception on error.
+ // Creates from an ipv4 or ipv6 address string; throws an exception on
+ // error.
SockAddr(const std::string& addr, int port);
+
explicit SockAddr(const sockaddr_storage& addr_in) {
addr_ = addr_in;
assert(addr_.ss_family == AF_INET || addr_.ss_family == AF_INET6);
}
- auto GetSockAddr() const -> const sockaddr* {
+
+ auto AsSockAddr() const -> const sockaddr* {
return reinterpret_cast(&addr_);
}
+
+ auto AsSockAddrIn() const -> const sockaddr_in* {
+ assert(!IsV6());
+ return reinterpret_cast(&addr_);
+ }
+
+ auto AsSockAddrIn6() const -> const sockaddr_in6* {
+ assert(IsV6());
+ return reinterpret_cast(&addr_);
+ }
+
+ auto AddressString() const -> std::string;
+
+ auto Port() const -> int;
+
auto GetSockAddrLen() const -> socklen_t {
switch (addr_.ss_family) {
case AF_INET:
@@ -32,9 +49,10 @@ class SockAddr {
case AF_INET6:
return sizeof(sockaddr_in6);
default:
- throw Exception();
+ throw Exception(PyExcType::kValue);
}
}
+
auto IsV6() const -> bool {
switch (addr_.ss_family) {
case AF_INET:
@@ -45,25 +63,22 @@ class SockAddr {
throw Exception();
}
}
+
auto operator==(const SockAddr& other) const -> bool {
if (addr_.ss_family != other.addr_.ss_family) return false;
if (addr_.ss_family == AF_INET) {
- return (reinterpret_cast(addr_).sin_addr.s_addr
- == reinterpret_cast(other.addr_)
- .sin_addr.s_addr)
- && (reinterpret_cast(addr_).sin_port
- == reinterpret_cast(other.addr_).sin_port);
+ auto* a1 = AsSockAddrIn();
+ auto* a2 = other.AsSockAddrIn();
+ return !memcmp(&(a1->sin_addr), &(a2->sin_addr), sizeof(in_addr))
+ && a1->sin_port == a2->sin_port;
}
if (addr_.ss_family == AF_INET6) {
- return !memcmp(&(reinterpret_cast(addr_).sin6_addr),
- &(reinterpret_cast(other.addr_)
- .sin6_addr),
- sizeof(in6_addr))
- && (reinterpret_cast(addr_).sin6_port
- == reinterpret_cast(other.addr_)
- .sin6_port);
+ auto* a1 = AsSockAddrIn6();
+ auto* a2 = other.AsSockAddrIn6();
+ return !memcmp(&(a1->sin6_addr), &(a2->sin6_addr), sizeof(in6_addr))
+ && a1->sin6_port == a2->sin6_port;
}
- throw Exception();
+ throw Exception(PyExcType::kValue);
}
private:
diff --git a/src/ballistica/shared/python/python.cc b/src/ballistica/shared/python/python.cc
index 567a9831..0068077b 100644
--- a/src/ballistica/shared/python/python.cc
+++ b/src/ballistica/shared/python/python.cc
@@ -416,9 +416,7 @@ class Python::ScopedInterpreterLock::Impl {
};
Python::ScopedInterpreterLock::ScopedInterpreterLock()
- : impl_{new Python::ScopedInterpreterLock::Impl()}
-// impl_{std::make_unique()}
-{}
+ : impl_{new Python::ScopedInterpreterLock::Impl()} {}
Python::ScopedInterpreterLock::~ScopedInterpreterLock() { delete impl_; }
diff --git a/src/ballistica/shared/python/python.h b/src/ballistica/shared/python/python.h
index 2a94c9e3..4d2e1615 100644
--- a/src/ballistica/shared/python/python.h
+++ b/src/ballistica/shared/python/python.h
@@ -41,7 +41,8 @@ class Python {
/// Use this to protect Python code that may be run in cases where we
/// don't hold the Global Interpreter Lock (GIL). (Basically anything
- /// outside of the logic thread).
+ /// outside of the logic thread). This will release and then restore
+ /// the GIL if it is held initially; otherwise it is a no-op.
class ScopedInterpreterLock {
public:
ScopedInterpreterLock();
@@ -49,9 +50,6 @@ class Python {
private:
class Impl;
- // Note: should use unique_ptr for this, but build fails on raspberry pi
- // (gcc 8.3.0). Works on Ubuntu 9.3 so should try again later.
- // std::unique_ptr impl_{};
Impl* impl_{};
};
@@ -64,9 +62,6 @@ class Python {
private:
class Impl;
- // Note: should use unique_ptr for this, but build fails on raspberry pi
- // (gcc 8.3.0). Works on Ubuntu 9.3 so should try again later.
- // std::unique_ptr impl_{};
Impl* impl_{};
};
diff --git a/src/ballistica/shared/python/python_ref.h b/src/ballistica/shared/python/python_ref.h
index 141aeebf..b2455d26 100644
--- a/src/ballistica/shared/python/python_ref.h
+++ b/src/ballistica/shared/python/python_ref.h
@@ -118,7 +118,9 @@ class PythonRef {
/// Release the held reference (if one is held).
void Release();
- /// Clear the ref without decrementing its count and return the raw PyObject*
+ /// Clear the ref without decrementing its count and return the raw
+ /// PyObject*. Useful for functions that are expected to return a new
+ /// Python ref.
auto HandOver() -> PyObject* {
assert(obj_);
PyObject* obj = obj_;
@@ -151,8 +153,9 @@ class PythonRef {
/// Throws Exception if an error occurs.
auto DictGetItem(const char* name) const -> PythonRef;
- /// The equivalent of calling Python str() on the contained PyObject.
- /// Gracefully handles invalid refs.
+ /// The equivalent of calling Python str() on the contained PyObject, and
+ /// gracefully handles invalid refs. To throw exceptions on invalid refs,
+ /// use ValueAsString();
auto Str() const -> std::string;
/// The equivalent of calling repr() on the contained PyObject.
diff --git a/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc b/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc
index 0fd6279f..8c0aefe5 100644
--- a/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc
+++ b/src/ballistica/ui_v1/python/methods/python_methods_ui_v1.cc
@@ -2589,90 +2589,6 @@ static PyMethodDef PyGetSpecialWidgetDef = {
"(internal)",
};
-// -------------------------- have_incentivized_ad -----------------------------
-
-// returns an extra hash value that can be incorporated into security checks;
-// this contains things like whether console commands have been run, etc.
-static auto PyHaveIncentivizedAd(PyObject* self, PyObject* args,
- PyObject* keywds) -> PyObject* {
- BA_PYTHON_TRY;
- static const char* kwlist[] = {nullptr};
- if (!PyArg_ParseTupleAndKeywords(args, keywds, "",
- const_cast(kwlist))) {
- return nullptr;
- }
- if (g_core->have_incentivized_ad) {
- Py_RETURN_TRUE;
- } else {
- Py_RETURN_FALSE;
- }
- BA_PYTHON_CATCH;
-}
-
-static PyMethodDef PyHaveIncentivizedAdDef = {
- "have_incentivized_ad", // name
- (PyCFunction)PyHaveIncentivizedAd, // method
- METH_VARARGS | METH_KEYWORDS, // flags
-
- "have_incentivized_ad() -> bool\n"
- "\n"
- "(internal)",
-};
-
-// ----------------------------- can_show_ad -----------------------------------
-
-// this returns whether it makes sense to show an currently
-static auto PyCanShowAd(PyObject* self, PyObject* args, PyObject* keywds)
- -> PyObject* {
- BA_PYTHON_TRY;
-
- BA_PRECONDITION(g_base->InLogicThread());
- // if we've got any network connections, no ads.
- // (don't want to make someone on the other end wait or risk disconnecting
- // them or whatnot). Also disallow ads if remote apps are connected; at least
- // on Android, ads pause our activity which disconnects the remote app.
- // (need to fix this).
- if (g_base->app_mode()->HasConnectionToHost()
- || g_base->app_mode()->HasConnectionToClients()
- || g_base->input->HaveRemoteAppController()) {
- Py_RETURN_FALSE;
- }
- Py_RETURN_TRUE; // all systems go..
- BA_PYTHON_CATCH;
-}
-
-static PyMethodDef PyCanShowAdDef = {
- "can_show_ad", // name
- (PyCFunction)PyCanShowAd, // method
- METH_VARARGS | METH_KEYWORDS, // flags
- "can_show_ad() -> bool\n"
- "\n"
- "(internal)",
-};
-
-// ---------------------------- has_video_ads ----------------------------------
-
-static auto PyHasVideoAds(PyObject* self, PyObject* args, PyObject* keywds)
- -> PyObject* {
- BA_PYTHON_TRY;
- if (g_core->platform->GetHasVideoAds()) {
- Py_RETURN_TRUE;
- } else {
- Py_RETURN_FALSE;
- }
- BA_PYTHON_CATCH;
-}
-
-static PyMethodDef PyHasVideoAdsDef = {
- "has_video_ads", // name
- (PyCFunction)PyHasVideoAds, // method
- METH_VARARGS | METH_KEYWORDS, // flags
-
- "has_video_ads() -> bool\n"
- "\n"
- "(internal)",
-};
-
// ------------------------------ back_press -----------------------------------
static auto PyBackPress(PyObject* self, PyObject* args, PyObject* keywds)
@@ -2893,9 +2809,6 @@ auto PythonMethodsUIV1::GetMethods() -> std::vector {
PyOpenFileExternallyDef,
PyOpenURLDef,
PyBackPressDef,
- PyHasVideoAdsDef,
- PyCanShowAdDef,
- PyHaveIncentivizedAdDef,
PyGetSpecialWidgetDef,
PySetPartyWindowOpenDef,
PySetPartyIconAlwaysVisibleDef,
diff --git a/src/meta/bascenev1meta/pyembed/binding_scene_v1.py b/src/meta/bascenev1meta/pyembed/binding_scene_v1.py
index c4a19066..4757e713 100644
--- a/src/meta/bascenev1meta/pyembed/binding_scene_v1.py
+++ b/src/meta/bascenev1meta/pyembed/binding_scene_v1.py
@@ -10,6 +10,7 @@ from bascenev1._player import Player
from bascenev1._dependency import AssetPackage
from bascenev1._activity import Activity
from bascenev1._session import Session
+from bascenev1._net import HostInfo
import _bascenev1
# The C++ layer looks for this variable:
@@ -30,4 +31,5 @@ values = [
AssetPackage, # kAssetPackageClass
Activity, # kActivityClass
Session, # kSceneV1SessionClass
+ HostInfo, # kHostInfoClass
]
diff --git a/tools/bacommon/app.py b/tools/bacommon/app.py
index d1b50fda..2b8abdc6 100644
--- a/tools/bacommon/app.py
+++ b/tools/bacommon/app.py
@@ -5,22 +5,49 @@
from __future__ import annotations
from enum import Enum
-from typing import TYPE_CHECKING
+from dataclasses import dataclass
+from typing import TYPE_CHECKING, Annotated
+
+from efro.dataclassio import ioprepped, IOAttrs
if TYPE_CHECKING:
pass
-class AppExperience(Enum):
- """Overall experience that can be provided by a Ballistica app.
+class AppInterfaceIdiom(Enum):
+ """A general form-factor or way of experiencing a Ballistica app.
- This corresponds generally, but not exactly, to distinct apps built
- with Ballistica. However, a single app may support multiple experiences,
- or there may be multiple apps targeting one experience. Cloud components
- such as leagues are generally associated with an AppExperience.
+ Note that it is possible for a running app to switch idioms (for
+ instance if a mobile device or computer is connected to a TV).
"""
- # A special experience category that is supported everywhere. Used
+ PHONE = 'phone'
+ TABLET = 'tablet'
+ DESKTOP = 'desktop'
+ TV = 'tv'
+ XR = 'xr'
+
+
+class AppExperience(Enum):
+ """A particular experience that can be provided by a Ballistica app.
+
+ This is one metric used to isolate different playerbases from
+ eachother where there might be no technical barriers doing so.
+ For example, a casual one-hand-playable phone game and an augmented
+ reality tabletop game may both use the same scene-versions and
+ networking-protocols and whatnot, but it would make no sense to
+ allow players of one join servers for the other. AppExperience can
+ be used to keep these player bases separate.
+
+ Generally a single Ballistica app targets a single AppExperience.
+ This is not a technical requirement, however. A single app may
+ support multiple experiences, or there may be multiple apps
+ targeting one experience. Cloud components such as leagues are
+ generally associated with an AppExperience so that they are only
+ visible to client apps designed for that play style.
+ """
+
+ # An experience that is supported everywhere. Used
# for the default empty AppMode when starting the app, etc.
EMPTY = 'empty'
@@ -33,3 +60,79 @@ class AppExperience(Enum):
# touch-screen allowing a mobile device to be used as a game
# controller.
REMOTE = 'remote'
+
+
+class AppArchitecture(Enum):
+ """Processor architecture the App is running on."""
+
+ ARM = 'arm'
+ ARM64 = 'arm64'
+ X86 = 'x86'
+ X86_64 = 'x86_64'
+
+
+class AppPlatform(Enum):
+ """Overall platform a Ballistica build can be targeting.
+
+ Each distinct flavor of an app has a unique combination
+ of AppPlatform and AppVariant. Generally platform describes
+ a set of hardware, while variant describes a destination or
+ purpose for the build.
+ """
+
+ MAC = 'mac'
+ WINDOWS = 'windows'
+ LINUX = 'linux'
+ ANDROID = 'android'
+ IOS = 'ios'
+ TVOS = 'tvos'
+
+
+class AppVariant(Enum):
+ """A unique Ballistica build type within a single platform.
+
+ Each distinct flavor of an app has a unique combination
+ of AppPlatform and AppVariant. Generally platform describes
+ a set of hardware, while variant describes a destination or
+ purpose for the build.
+ """
+
+ # Default builds.
+ GENERIC = 'generic'
+
+ # Builds intended for public testing (may have some extra checks
+ # or logging enabled).
+ TEST = 'test'
+
+ # Various stores.
+ AMAZON_APPSTORE = 'amazon_appstore'
+ GOOGLE_PLAY = 'google_play'
+ APP_STORE = 'app_store'
+ WINDOWS_STORE = 'windows_store'
+ STEAM = 'steam'
+ META = 'meta'
+ EPIC_GAMES_STORE = 'epic_games_store'
+
+ # Other.
+ ARCADE = 'arcade'
+ DEMO = 'demo'
+
+
+@ioprepped
+@dataclass
+class AppInstanceInfo:
+ """General info about an individual running app."""
+
+ name = Annotated[str, IOAttrs('n')]
+ version = Annotated[str, IOAttrs('v')]
+ build = Annotated[int, IOAttrs('b')]
+
+ platform = Annotated[AppPlatform, IOAttrs('p')]
+ variant = Annotated[AppVariant, IOAttrs('va')]
+ architecture = Annotated[AppArchitecture, IOAttrs('a')]
+ os_version = Annotated[str | None, IOAttrs('o')]
+
+ interface_idiom: Annotated[AppInterfaceIdiom, IOAttrs('i')]
+ locale: Annotated[str, IOAttrs('l')]
+
+ device: Annotated[str | None, IOAttrs('d')]
diff --git a/tools/batools/dummymodule.py b/tools/batools/dummymodule.py
index 02bd6746..7fbf618e 100755
--- a/tools/batools/dummymodule.py
+++ b/tools/batools/dummymodule.py
@@ -216,6 +216,12 @@ def _writefuncs(
'import bascenev1 # pylint: disable=cyclic-import\n'
'return bascenev1.Time(0.0)'
)
+ elif returns == 'bascenev1.HostInfo | None':
+ returnstr = (
+ 'import bascenev1 # pylint: disable=cyclic-import\n'
+ 'return bascenev1.HostInfo(\'dummyname\', -1,'
+ ' \'dummy_addr\', -1)'
+ )
elif returns == 'babase.DisplayTime':
returnstr = (
'import babase # pylint: disable=cyclic-import\n'
diff --git a/tools/batools/project/_updater.py b/tools/batools/project/_updater.py
index db616708..5d820d92 100755
--- a/tools/batools/project/_updater.py
+++ b/tools/batools/project/_updater.py
@@ -426,7 +426,6 @@ class ProjectUpdater:
# from batools.xcode import update_xcode_project
for projpath in [
- # 'ballisticakit-mac.xcodeproj/project.pbxproj',
# 'ballisticakit-ios.xcodeproj/project.pbxproj',
'ballisticakit-xcode/BallisticaKit.xcodeproj/project.pbxproj',
]:
diff --git a/tools/efrotools/openalbuild.py b/tools/efrotools/openalbuild.py
index 8ccb2b4a..a53555b3 100644
--- a/tools/efrotools/openalbuild.py
+++ b/tools/efrotools/openalbuild.py
@@ -62,7 +62,7 @@ def build_openal(arch: str, mode: str) -> None:
)
# subprocess.run(['git', 'checkout', '1.23.1'], check=True, cwd=builddir)
subprocess.run(
- ['git', 'checkout', '5b5b948516f7340810ebbfdd5e46eb40f85d2e56'],
+ ['git', 'checkout', 'b81a270f6c1e795ca70d7684e0ccf35a19f247e2'],
check=True,
cwd=builddir,
)