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, )