mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-29 02:23:22 +08:00
Merge branch 'efroemling:master' into master
This commit is contained in:
commit
eac3418972
216
.efrocachemap
generated
216
.efrocachemap
generated
@ -1,5 +1,5 @@
|
||||
{
|
||||
"ballisticakit-windows/Generic/BallisticaKit.ico": "be1b956dcd7f7a261b1afe5bce2a0336",
|
||||
"ballisticakit-windows/Generic/BallisticaKit.ico": "6f33e74cb282f070871413f092983fcd",
|
||||
"build/assets/ba_data/audio/achievement.ogg": "079a366ce183b25a63550ef7072af605",
|
||||
"build/assets/ba_data/audio/actionHero1.ogg": "f0f986f268f036a5ac2f940e07f2f27e",
|
||||
"build/assets/ba_data/audio/actionHero2.ogg": "204a6735dc655f0975cf8308b585f2fd",
|
||||
@ -421,42 +421,42 @@
|
||||
"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": "992c5c5ce292132c4f011f39e0d13de8",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "d1f900ab5aa2433d402bd46ed1149cc7",
|
||||
"build/assets/ba_data/data/languages/belarussian.json": "e151808b6b4f6dc159cf55ee62adad3c",
|
||||
"build/assets/ba_data/data/languages/chinese.json": "8d889accdd49334591209bdaf6eaf02f",
|
||||
"build/assets/ba_data/data/langdata.json": "fae88cbb2a5b9c24096f2e43452114a2",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "0db32e21b6d5337ccca478381744aa88",
|
||||
"build/assets/ba_data/data/languages/belarussian.json": "a112dfca3e188387516788bd8229c5b0",
|
||||
"build/assets/ba_data/data/languages/chinese.json": "ff9a595726f0aff42a39be576d0ff037",
|
||||
"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": "c9d518a324870066b987b8f412881dd3",
|
||||
"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": "b38d54aecf3ac47b8d8ca97d8bab3006",
|
||||
"build/assets/ba_data/data/languages/english.json": "2434e127b6788e3128d3523fcb1b8994",
|
||||
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "347f38524816691170d266708fe25894",
|
||||
"build/assets/ba_data/data/languages/french.json": "d8527da977a563185de25ef02bacf826",
|
||||
"build/assets/ba_data/data/languages/german.json": "549754d2a530d825200c6126be56df5c",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "837423db378b3e7679683805826aa26e",
|
||||
"build/assets/ba_data/data/languages/greek.json": "a65d78f912e9a89f98de004405167a6a",
|
||||
"build/assets/ba_data/data/languages/hindi.json": "88ee0cda537bab9ac827def5e236fe1a",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "e750fb1a95e4c5611115f9ece9ecab53",
|
||||
"build/assets/ba_data/data/languages/french.json": "163362f7b33866ef069cae62d0387551",
|
||||
"build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "e24d391c9fd12f9afa92f7ff65a06d23",
|
||||
"build/assets/ba_data/data/languages/greek.json": "287c0ec437b38772284ef9d3e4fb2fc3",
|
||||
"build/assets/ba_data/data/languages/hindi.json": "8848f6b0caec0fcf9d85bc6e683809ec",
|
||||
"build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e",
|
||||
"build/assets/ba_data/data/languages/indonesian.json": "bff88ce57744a639810b93a1d1dd79f4",
|
||||
"build/assets/ba_data/data/languages/italian.json": "338e7a03dff47f4eefc0ca3a995cd4f4",
|
||||
"build/assets/ba_data/data/languages/korean.json": "ca1122a9ee551da3f75ae632012bd0e2",
|
||||
"build/assets/ba_data/data/languages/indonesian.json": "408fb026e84c24a8dd7a43cb2b794541",
|
||||
"build/assets/ba_data/data/languages/italian.json": "61c5308638bed44194f0ec24f19bf3cb",
|
||||
"build/assets/ba_data/data/languages/korean.json": "03fd99d5e1155e81053fc028f69df982",
|
||||
"build/assets/ba_data/data/languages/malay.json": "832562ce997fc70704b9234c95fb2e38",
|
||||
"build/assets/ba_data/data/languages/persian.json": "71cc5b33abda0f285b970b8cc4a014a8",
|
||||
"build/assets/ba_data/data/languages/polish.json": "e1a1a801851924748ad38fa68216439a",
|
||||
"build/assets/ba_data/data/languages/portuguese.json": "9fcd6b4da9e5d0dc0e337ab00b5debe2",
|
||||
"build/assets/ba_data/data/languages/persian.json": "4c3394f6662bb6dcf55728cfe213d750",
|
||||
"build/assets/ba_data/data/languages/polish.json": "3a90b2d9e2c59305580c96f8098fc839",
|
||||
"build/assets/ba_data/data/languages/portuguese.json": "0274cb9a4b7d2bd49c8eb8120144a1bf",
|
||||
"build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826",
|
||||
"build/assets/ba_data/data/languages/russian.json": "910cf653497654a16d5c4f067d6def22",
|
||||
"build/assets/ba_data/data/languages/russian.json": "9d0b40586301a82e532c4a250d5f6d58",
|
||||
"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": "0122b0b24aa111ab259af02bbae9b7b6",
|
||||
"build/assets/ba_data/data/languages/swedish.json": "77d671f10613291ebf9c71da66f18a18",
|
||||
"build/assets/ba_data/data/languages/tamil.json": "b9d4b4e107456ea6420ee0f9d9d7a03e",
|
||||
"build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc",
|
||||
"build/assets/ba_data/data/languages/turkish.json": "9d7e58c9062dc517c3779c255a9b3142",
|
||||
"build/assets/ba_data/data/languages/ukrainian.json": "f72eb51abfbbb56e27866895d7e947d2",
|
||||
"build/assets/ba_data/data/languages/venetian.json": "88595b7ee696b4094d7874c3c4188852",
|
||||
"build/assets/ba_data/data/languages/spanish.json": "42f857c40dbd4b637e3866849489f7d1",
|
||||
"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",
|
||||
"build/assets/ba_data/data/languages/turkish.json": "ab149ebbd57cf4daa3cf8f310d91519a",
|
||||
"build/assets/ba_data/data/languages/ukrainian.json": "e5c861187c4c6db37d1a033f4ef3dd5a",
|
||||
"build/assets/ba_data/data/languages/venetian.json": "a559a5608d2e0b4708f7a4dee42ff354",
|
||||
"build/assets/ba_data/data/languages/vietnamese.json": "921cd1e50f60fe3e101f246e172750ba",
|
||||
"build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054",
|
||||
"build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422",
|
||||
@ -946,11 +946,11 @@
|
||||
"build/assets/ba_data/meshes/zoeUpperArm.bob": "a8a881010ac1ee9ec5ca872d5c5e853a",
|
||||
"build/assets/ba_data/meshes/zoeUpperLeg.bob": "95b2502f74c70f934927f67cd505c3ad",
|
||||
"build/assets/ba_data/python-site-packages/_yaml/__init__.py": "b09d1968d73a04d6cf20e4e79657a6e7",
|
||||
"build/assets/ba_data/python-site-packages/certifi/__init__.py": "6337efa17f5b457b793332df33904162",
|
||||
"build/assets/ba_data/python-site-packages/certifi/__init__.py": "b1fb6436db400125ecbb288262d00f0f",
|
||||
"build/assets/ba_data/python-site-packages/certifi/__main__.py": "ef02e73f8581609df189a9f61aca365b",
|
||||
"build/assets/ba_data/python-site-packages/certifi/cacert.pem": "6ac29a6bccca11cd2ed7e16e27dfccec",
|
||||
"build/assets/ba_data/python-site-packages/certifi/cacert.pem": "4422aed09ab445f7290df7d72a301a47",
|
||||
"build/assets/ba_data/python-site-packages/certifi/core.py": "1b505388f1475fabd1b60031f985271c",
|
||||
"build/assets/ba_data/python-site-packages/typing_extensions.py": "084d93bb609d798a3930dfb5e25eba59",
|
||||
"build/assets/ba_data/python-site-packages/typing_extensions.py": "2d974cad17a71505d86513d1322976a5",
|
||||
"build/assets/ba_data/python-site-packages/yaml/__init__.py": "2b747e5772c203377222afc888ac6b71",
|
||||
"build/assets/ba_data/python-site-packages/yaml/composer.py": "cef871e1f5f99ba2a7c44941b70afb06",
|
||||
"build/assets/ba_data/python-site-packages/yaml/constructor.py": "8a15e361e34b79491c81553bb3534062",
|
||||
@ -1444,6 +1444,10 @@
|
||||
"build/assets/ba_data/textures/discordLogo.ktx": "d56ab6389e2770e0601d87f99375d7a5",
|
||||
"build/assets/ba_data/textures/discordLogo.pvr": "bd39785cf2cbf9bc41fdde8b86ab3310",
|
||||
"build/assets/ba_data/textures/discordLogo_preview.png": "90efd54d3abd371c0150a363f3f673e7",
|
||||
"build/assets/ba_data/textures/discordServer.dds": "782f63ce8bcf0de2c338ee1fbfb5df2e",
|
||||
"build/assets/ba_data/textures/discordServer.ktx": "09c969fd0a278b5c426344845ef07c43",
|
||||
"build/assets/ba_data/textures/discordServer.pvr": "54216467f9527c3fc2d8c777811e97a7",
|
||||
"build/assets/ba_data/textures/discordServer_preview.png": "99cf0a7f77b909e9079d423a687e399f",
|
||||
"build/assets/ba_data/textures/doomShroomBGColor.dds": "628100f564789f57e22f2ce0dcf53b76",
|
||||
"build/assets/ba_data/textures/doomShroomBGColor.ktx": "ffbb38af60ab59e6030b01cd129bfaf9",
|
||||
"build/assets/ba_data/textures/doomShroomBGColor.pvr": "77d103392813e302cdaf631cef88ba36",
|
||||
@ -1524,9 +1528,9 @@
|
||||
"build/assets/ba_data/textures/fontBig.ktx": "94b56c2488d6c9ebabfbbb740eca07dd",
|
||||
"build/assets/ba_data/textures/fontBig.pvr": "dff3f6c04a8c7b0bb937001640b42c8d",
|
||||
"build/assets/ba_data/textures/fontBig_preview.png": "f8b15cb04f0deca7774def335a72f053",
|
||||
"build/assets/ba_data/textures/fontExtras.dds": "7ab11df1b3a3daa651dfad34219b89f5",
|
||||
"build/assets/ba_data/textures/fontExtras.ktx": "30c3c8ca2cdf1209ff177017bb10f0a8",
|
||||
"build/assets/ba_data/textures/fontExtras.pvr": "fd3b0bd902c30e4b7aa5fe00e1eec4be",
|
||||
"build/assets/ba_data/textures/fontExtras.dds": "0a5a39028853c443cd88bc2492cb6ad9",
|
||||
"build/assets/ba_data/textures/fontExtras.ktx": "5b14075ce3d1d29c6d5635602e2176d8",
|
||||
"build/assets/ba_data/textures/fontExtras.pvr": "8cc68ca85ba327c20c45bad73b000d8c",
|
||||
"build/assets/ba_data/textures/fontExtras2.dds": "18063a12912dadc9528afd90d1cf2369",
|
||||
"build/assets/ba_data/textures/fontExtras2.ktx": "36da7f6cfbfb8d32fb14371de0a8f660",
|
||||
"build/assets/ba_data/textures/fontExtras2.pvr": "7a4e8e64ac05313b1782fb5b958150d0",
|
||||
@ -1539,7 +1543,7 @@
|
||||
"build/assets/ba_data/textures/fontExtras4.ktx": "6d872ac15e2e874c1252f63b4584722b",
|
||||
"build/assets/ba_data/textures/fontExtras4.pvr": "6a0a0a1a8bbbc3ee9d6b8b914e7aa697",
|
||||
"build/assets/ba_data/textures/fontExtras4_preview.png": "363e2647621917b3821c9068267d2516",
|
||||
"build/assets/ba_data/textures/fontExtras_preview.png": "9c9c58aff612e7b6386f3522c0b4f1f6",
|
||||
"build/assets/ba_data/textures/fontExtras_preview.png": "b6503267cc15e9e2524f41fabd94e773",
|
||||
"build/assets/ba_data/textures/fontSmall0.dds": "b30bfe5f9e436be7be8b5eae6e8490c3",
|
||||
"build/assets/ba_data/textures/fontSmall0.ktx": "7e6058f37e6c5a4ea628f35b5f92c227",
|
||||
"build/assets/ba_data/textures/fontSmall0.pvr": "c66e3d6aa1f7def83aaacd8a6c9185e5",
|
||||
@ -2586,21 +2590,21 @@
|
||||
"build/assets/pylib-android/_pyio.py": "a6e88d66fbca88b13213cdd2177390b8",
|
||||
"build/assets/pylib-android/_sitebuiltins.py": "8b5e3f6e73917962fa014ad2c4a55e61",
|
||||
"build/assets/pylib-android/_strptime.py": "ff699c3f7647db7621bb88c43cc282d3",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_aarch64-linux-android.py": "cb9a77b04173c8776365999b57186e36",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_arm-linux-androideabi.py": "6d50596ec7f4858a0c6a5edefde21f7a",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_i686-linux-android.py": "bf9358a2243aa7884b8e80d85c969fa5",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_x86_64-linux-android.py": "45eee0efbc2441535b94a8ad5acf4d2e",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_aarch64-linux-android.py": "f8ff271cf6df0b5b4d46d9c548abb84e",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_arm-linux-androideabi.py": "da171b290c06a34d6a5cfbb296c22c34",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_i686-linux-android.py": "dde2516b5ac29412dfbebaa7b3de0d0d",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_x86_64-linux-android.py": "7df452144c6630afb96951487c1257a0",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_aarch64-linux-android.py": "b1a9ca985ff6a159aa5ef94abd287f46",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_arm-linux-androideabi.py": "21a5842f39c86fccaaa0a30e0e4ab347",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_i686-linux-android.py": "9349023049d7599da61456b3f9a9687b",
|
||||
"build/assets/pylib-android/_sysconfigdata__linux_x86_64-linux-android.py": "4151fa62c11c32cddf538e5cc7647160",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_aarch64-linux-android.py": "d9f7f1d3f5b89b08150dfa00cf243901",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_arm-linux-androideabi.py": "f4b99d4501a1cf1eb20fbc8973fa0040",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_i686-linux-android.py": "454094da5fe52a969b53bb46d360da84",
|
||||
"build/assets/pylib-android/_sysconfigdata_d_linux_x86_64-linux-android.py": "1e23f45f4243c1aacc83f23ad5852390",
|
||||
"build/assets/pylib-android/_threading_local.py": "4a9688e3987d7d692db46feb9214945e",
|
||||
"build/assets/pylib-android/_weakrefset.py": "e4fa8532ace46dfbc35149c41ea497f7",
|
||||
"build/assets/pylib-android/abc.py": "a0daa1ed187eee8690c1e8438b97da90",
|
||||
"build/assets/pylib-android/aifc.py": "1b9134c72b1e542417bee5bf345a1d0a",
|
||||
"build/assets/pylib-android/antigravity.py": "6d56bedf73be574cb6d7117caf5d334c",
|
||||
"build/assets/pylib-android/argparse.py": "e22cac9b12c09592929d57eb982fc554",
|
||||
"build/assets/pylib-android/ast.py": "3aaa1b0e56b21b28155707c54bc225a8",
|
||||
"build/assets/pylib-android/ast.py": "f287ccaa1cd7cb0ea256e3984fd4ce4d",
|
||||
"build/assets/pylib-android/asynchat.py": "2ef3a0ce322332fabbf8fad4e133c6a3",
|
||||
"build/assets/pylib-android/asyncio/__init__.py": "edf0e79e2b8b85c08f09fd14668e4822",
|
||||
"build/assets/pylib-android/asyncio/__main__.py": "8e391b47f448ad922dc2614dbd93011e",
|
||||
@ -2624,10 +2628,10 @@
|
||||
"build/assets/pylib-android/asyncio/selector_events.py": "a108fbd3a49f967da245f39cebf7694e",
|
||||
"build/assets/pylib-android/asyncio/sslproto.py": "2ec1b21e523055147d94c8c634154aab",
|
||||
"build/assets/pylib-android/asyncio/staggered.py": "f5056f0a56b73b477a9fa65e71145366",
|
||||
"build/assets/pylib-android/asyncio/streams.py": "f00ddd2b2fd74554ae1d3088bd9d2bfd",
|
||||
"build/assets/pylib-android/asyncio/subprocess.py": "edb8d98278300b6c99f36cd08643c743",
|
||||
"build/assets/pylib-android/asyncio/streams.py": "8cc026c067fc9245568199ea659167df",
|
||||
"build/assets/pylib-android/asyncio/subprocess.py": "46e8b0ba32b4ac7bb5f840c49c89c85a",
|
||||
"build/assets/pylib-android/asyncio/taskgroups.py": "5162e5b1806d9b647383d34ba1b21b56",
|
||||
"build/assets/pylib-android/asyncio/tasks.py": "234550593cd4928e6ee2c9591b6928ca",
|
||||
"build/assets/pylib-android/asyncio/tasks.py": "c1bc59c01792bac43b79b425bb61e10e",
|
||||
"build/assets/pylib-android/asyncio/threads.py": "7bbf81d424901524510e07b5d20e4a50",
|
||||
"build/assets/pylib-android/asyncio/timeouts.py": "c7cb81c7ee938bc47ff75342befc872a",
|
||||
"build/assets/pylib-android/asyncio/transports.py": "04598090d813bb363cea9bf714b97c3f",
|
||||
@ -2641,13 +2645,13 @@
|
||||
"build/assets/pylib-android/bisect.py": "9b70437e327d5176da41192567ad0064",
|
||||
"build/assets/pylib-android/bz2.py": "cd6a5f2491bc52afd8fc180097371473",
|
||||
"build/assets/pylib-android/cProfile.py": "9e9c07ac3b9e4195a62b74e4f2b9489f",
|
||||
"build/assets/pylib-android/calendar.py": "4ef3d6d85d44e36212e5d784051c80b6",
|
||||
"build/assets/pylib-android/calendar.py": "18df862e8e3c3fcbe4ab8a0b0348e339",
|
||||
"build/assets/pylib-android/cgi.py": "090c5cfc8b4b92a730beec975159bd2a",
|
||||
"build/assets/pylib-android/cgitb.py": "2bcff1cec7f3a3a9c96de7a55ebb4ea3",
|
||||
"build/assets/pylib-android/chunk.py": "13d7633b1ff28f5aed4eb043c65c99c5",
|
||||
"build/assets/pylib-android/cmd.py": "8befee2654b0954af7886e24e2e7871f",
|
||||
"build/assets/pylib-android/code.py": "5d47099984013b933c96b02ef16981b8",
|
||||
"build/assets/pylib-android/codecs.py": "6fac5e2969e98ceaba92d3b8e42cb2ec",
|
||||
"build/assets/pylib-android/codecs.py": "e11eabe4824899dea4b26a89a568a361",
|
||||
"build/assets/pylib-android/codeop.py": "d375467fb29fccd43ab94d15a2e63085",
|
||||
"build/assets/pylib-android/collections/__init__.py": "dcffbb6ee2cadd0c05ad22f2ef41f89b",
|
||||
"build/assets/pylib-android/collections/abc.py": "15f410d3821352033a90a04539c99060",
|
||||
@ -2656,7 +2660,7 @@
|
||||
"build/assets/pylib-android/concurrent/__init__.py": "aa990702e8f3a7af205efb5ae23a7c85",
|
||||
"build/assets/pylib-android/concurrent/futures/__init__.py": "3e46fadb9de9c995c37dca4311641d6a",
|
||||
"build/assets/pylib-android/concurrent/futures/_base.py": "a1cd37aea6fe0efff1bc00a39543609e",
|
||||
"build/assets/pylib-android/concurrent/futures/process.py": "a44e8618e158f8f351dafcb566a02544",
|
||||
"build/assets/pylib-android/concurrent/futures/process.py": "1d1bb7b14e3999b383ba8bd11aa8951c",
|
||||
"build/assets/pylib-android/concurrent/futures/thread.py": "e63753b8201f1392dbebc84a15054a13",
|
||||
"build/assets/pylib-android/configparser.py": "914afd2b2cec90bbca0b94fd176b5176",
|
||||
"build/assets/pylib-android/contextlib.py": "6f52eac914e438ef54407760def8305f",
|
||||
@ -2680,7 +2684,7 @@
|
||||
"build/assets/pylib-android/curses/panel.py": "8f36fdade9588f8a4362d2cc057a6eff",
|
||||
"build/assets/pylib-android/curses/textpad.py": "94aa9ebc47a6068d4461652346646dbb",
|
||||
"build/assets/pylib-android/dataclasses.py": "febeea138bff21dbed88762be772514e",
|
||||
"build/assets/pylib-android/datetime.py": "5dcfd7f3b1a4db8214c1442164ac999c",
|
||||
"build/assets/pylib-android/datetime.py": "521d6767afcfef887ac4c3719386b8fd",
|
||||
"build/assets/pylib-android/decimal.py": "f57d255d45b5d1d7d8e13c41a283c3e4",
|
||||
"build/assets/pylib-android/difflib.py": "6b3c8fd541b2b8d0320727025cd25275",
|
||||
"build/assets/pylib-android/dis.py": "cecdc0c02aa3d70a7f550e60ebc9b3ba",
|
||||
@ -2836,7 +2840,7 @@
|
||||
"build/assets/pylib-android/encodings/utf_8_sig.py": "8f3542863ef311d8b970a37c0d66b0de",
|
||||
"build/assets/pylib-android/encodings/uu_codec.py": "4ef8a65413574c017a96b97fc1638ba6",
|
||||
"build/assets/pylib-android/encodings/zlib_codec.py": "1388fb103fdf395451bfc8a2d60933a9",
|
||||
"build/assets/pylib-android/enum.py": "73b214a43ceef88aff7098b83623ed09",
|
||||
"build/assets/pylib-android/enum.py": "e2a5734675e418870d7b379b5dba1ed3",
|
||||
"build/assets/pylib-android/filecmp.py": "7648fdc6d0fc8bae7429d5e4081cf353",
|
||||
"build/assets/pylib-android/fileinput.py": "c3def1041e6b12dd5f1906c9dbbd1101",
|
||||
"build/assets/pylib-android/fnmatch.py": "a1bc67633695d4defd4c0886428c5363",
|
||||
@ -2916,7 +2920,7 @@
|
||||
"build/assets/pylib-android/optparse.py": "5f65f891612b68c71a2846da86254285",
|
||||
"build/assets/pylib-android/os.py": "36f9692131ffb9ba4db510de31afc651",
|
||||
"build/assets/pylib-android/pathlib.py": "095ec821fec243124d0a286b4de3848a",
|
||||
"build/assets/pylib-android/pdb.py": "117b0d24ccb89edc5f183c94f6722f70",
|
||||
"build/assets/pylib-android/pdb.py": "c44527d9e905ca3b1b45d3c158df730a",
|
||||
"build/assets/pylib-android/pickle.py": "e6f9f53d29988454690ccde3279c7c38",
|
||||
"build/assets/pylib-android/pickletools.py": "85b30fba86d32dfc4a588300dedf5f01",
|
||||
"build/assets/pylib-android/pipes.py": "2dd796bdbb87982034234fec50d4526c",
|
||||
@ -2945,10 +2949,10 @@
|
||||
"build/assets/pylib-android/runpy.py": "3a2dd98314791c7e36b6bd3585f6ad82",
|
||||
"build/assets/pylib-android/sched.py": "f5579c8c711dd3e89da70ec9e1788c9c",
|
||||
"build/assets/pylib-android/secrets.py": "bbf9ed672044ef3ab4b83ca2aea1644e",
|
||||
"build/assets/pylib-android/selectors.py": "98e0d83849452cbc2cc1381555bd5024",
|
||||
"build/assets/pylib-android/selectors.py": "3c94b3b678c473543cdc7f1d2b20a6f6",
|
||||
"build/assets/pylib-android/shelve.py": "3e569c07c863ecbd7f35a6c382d1785a",
|
||||
"build/assets/pylib-android/shlex.py": "0873fac90a491702950816ead0e59dd0",
|
||||
"build/assets/pylib-android/shutil.py": "a5d0ee9f28244b42a06e682312d0e3fa",
|
||||
"build/assets/pylib-android/shutil.py": "aa636d67785c2e92d34c7c5c81f9e8c5",
|
||||
"build/assets/pylib-android/signal.py": "114ef47b1798fca6f56ac8a250974b3e",
|
||||
"build/assets/pylib-android/site.py": "2a99f7de2702aa8411d35acbb91fe926",
|
||||
"build/assets/pylib-android/smtpd.py": "0602b6a39c4e37133303bee16c3e28a4",
|
||||
@ -2958,7 +2962,7 @@
|
||||
"build/assets/pylib-android/socketserver.py": "98e33643181a54765e6d0b9e01b03d53",
|
||||
"build/assets/pylib-android/sqlite3/__init__.py": "8838d75ad0e465e25bb0c8dfeab7a9ab",
|
||||
"build/assets/pylib-android/sqlite3/dbapi2.py": "c85f3ff9ddbd56683a8c801885dc5e53",
|
||||
"build/assets/pylib-android/sqlite3/dump.py": "8364bd18be01acf7e56e168db98c0e6f",
|
||||
"build/assets/pylib-android/sqlite3/dump.py": "8d2085ec40031d544694759608e53178",
|
||||
"build/assets/pylib-android/sre_compile.py": "a1784e9ccbea7d9963cab75b536b40c8",
|
||||
"build/assets/pylib-android/sre_constants.py": "5c5be32a5334d9b0a848dad520746a63",
|
||||
"build/assets/pylib-android/sre_parse.py": "cca15b9ab31509e6642f9d2fd4fb9d91",
|
||||
@ -2978,8 +2982,8 @@
|
||||
"build/assets/pylib-android/tempfile.py": "436007fbe6821c864a53861bd73b4d43",
|
||||
"build/assets/pylib-android/textwrap.py": "3eb16a40553205dc96be5cb9039f3c8c",
|
||||
"build/assets/pylib-android/this.py": "8b0a9a1fa0a45a37e6c656eca1922277",
|
||||
"build/assets/pylib-android/threading.py": "dda98a9e1169adb496655300454ecc09",
|
||||
"build/assets/pylib-android/timeit.py": "8dc6f4245abf1d44814745e22a2f78b1",
|
||||
"build/assets/pylib-android/threading.py": "3354bf0cad72286a0532b0754de78704",
|
||||
"build/assets/pylib-android/timeit.py": "c918c7dee7538ff6e5a92288f55b4327",
|
||||
"build/assets/pylib-android/token.py": "d8ff4e6c8eb59896891d01148f481e27",
|
||||
"build/assets/pylib-android/tokenize.py": "3056f048c07e6c5a6442a5ef4f38e54c",
|
||||
"build/assets/pylib-android/tomllib/__init__.py": "253ecf9dd67cb81a3e19911a4a39f930",
|
||||
@ -2987,7 +2991,7 @@
|
||||
"build/assets/pylib-android/tomllib/_re.py": "0e509117e16c41c491615e06bb98861d",
|
||||
"build/assets/pylib-android/tomllib/_types.py": "07be9616d6f5e401fd31fbeea619fc97",
|
||||
"build/assets/pylib-android/trace.py": "3d8698a2c3ec03dc0f394a2f48c2ffbc",
|
||||
"build/assets/pylib-android/traceback.py": "91f67818e621e3b2f5bf583ed6863ef8",
|
||||
"build/assets/pylib-android/traceback.py": "668bd36fc103a89554d2f9202a07f56d",
|
||||
"build/assets/pylib-android/tracemalloc.py": "e4d10d2bee7773566e46797a939e5cbf",
|
||||
"build/assets/pylib-android/tty.py": "271c7d61005a0a3c2c0952efc60dcb6d",
|
||||
"build/assets/pylib-android/types.py": "78f8942c08dbfc9c582f1bb8d5206639",
|
||||
@ -4056,59 +4060,59 @@
|
||||
"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": "43a2b924e39a8ec1c1c4dc29c4dd82a1",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "870c3973e3f84ca977496f79706ceab1",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "49251d9bf7583a51e7e28cf6b62ff357",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "23068b129ac54e4f24c96aa154f0bb4d",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "045814b1c6e365ca5ac73883bbd9f22d",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "52d8664a3ed40b0715fa5097005f58da",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "4f364467c2f636a83a04f4f2930b34ee",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "9b2afa754ae00d687750b243ee977312",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "182c54fa13364049339aedfa67cbfcba",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "341976d8d665a94bf20a685aace37be5",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "b60fe7cb522e9aa3969c7e92af06d14e",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "e03943051877ae0105c7526b5094c67f",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "bfc7e631e7319b0749912e40b4bdbe88",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "d30e8266191cfd80f108129086f83b87",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "bcf27351436cb58df186612b82be983b",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "39f8ce569f1faed09bd4189e291a91bd",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "71b1c542f48d5abae5fb790b521a156a",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "553f71787d119cb01046f247711424b5",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "12cd08021ae0ed1ce4238c1adead61dc",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "b7e08e6b9f76def9b61e4839b2682497",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "58946f3534363d88f713c54d3d643d6d",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "be356d05ecccd68043258d87b1892805",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "58946f3534363d88f713c54d3d643d6d",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "be356d05ecccd68043258d87b1892805",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "bf7d793d62416db7273590a796001cb6",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "b309e0cc3ec04024712c4ca938efdb92",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "bf7d793d62416db7273590a796001cb6",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "b309e0cc3ec04024712c4ca938efdb92",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "a86b09c31abf0b5ec934ef28c8bd9fa3",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "87be7a2f6e83c495f99024bb68660e17",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "a86b09c31abf0b5ec934ef28c8bd9fa3",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "87be7a2f6e83c495f99024bb68660e17",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "9fb5d3cb36dd53bd18c7ca831e7c73ee",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "88332859e6e9ee70848f5252e5ee6ce0",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "55b6db8700acfc573cc3db31c6b210f7",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "88332859e6e9ee70848f5252e5ee6ce0",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "cf40ba3bce2391e82978b08785405a5e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "a8a74156e04932a2a5cc6d2d4b202acf",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "a135f9210a1c3be6b5d5d8228c8f6184",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "e412e20e4a0ac33b9f83c7750cde7109",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "cfcae11dab1c6752f821f0816706fa47",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "16daa37287a6d9d3404461da8565aadb",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "b3faf8b8925145f121b09e67d6114fb8",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "ed7ec02978df94f92168c5990cb6c78c",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "5f7e668a6a904ba8c0fe391654a8211b",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "77cd63dbc1760a3d1bb55753794701b0",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "9f674a5b767f798b350ef30d84968d44",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "84546f0452561d3f6519d296a9e9b2ca",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "3122dbce63fb248c9ebe66d545be8480",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4004c01a3ea1a7b62544f08f14e76f9e",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "cd9b5b9ae925cef7417d2b86fd0f0489",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "35c2b307e5d228b105315b8d022d31d2",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "90993c873a2d1ca7b593992e52b1865a",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "46119cb9ff99629ca16471f51032bac7",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "8cac6eff8eb4f9e453a669b6f239b8a5",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "3e20c91bf8948354a0e167faa5c184e9",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "52ff216e1b0394a07bf178b8eea8c269",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "32d85cc58ade8e7a63dfb604ef175553",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "fdf3da5251bc0d3b8f4e5a5b2e0d4b94",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "c03afc18b7aa72ab065381be985f48ae",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "67a58d483ac028f9fc59112b59463a99",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "0800d71a41320664bacba49822e8b442",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "9019d9b48cefa45a7a16bae6bd696896",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "90eca6cc81b0a2c39fd1d41d0d029f04",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "db535f0ca1e01af825f75f204fbc8928",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "97d51afca996ae15b61fd9f409a00459",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "db535f0ca1e01af825f75f204fbc8928",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "97d51afca996ae15b61fd9f409a00459",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "43794f4973b09588367261faf46b652a",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "9bc71c9874596dd708841d84dff69b55",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "43794f4973b09588367261faf46b652a",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "9bc71c9874596dd708841d84dff69b55",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "87f6bf3ea1196d91cd6486785702a5b2",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "60336f9d664825ca08236dda311276ca",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "87f6bf3ea1196d91cd6486785702a5b2",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "60336f9d664825ca08236dda311276ca",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "4399c87b58fbe58fa67096cfa878f86a",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "452623f0495dd4375e5b5d9b80d643d5",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "ca49b32ed573feea11613d62cd89840c",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "452623f0495dd4375e5b5d9b80d643d5",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "ff81e6eeea861f59e71db628bc64918b",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "ba89e5949d1cdf2b857089feb901285c",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "5e063f8acc0e0e9f35f82480d7dbf143",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "1f0d14fcc16dd0d4896d91c75e32be25",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "251d3dd0bc9a6418eb1cb5176bb5509c",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "96c003edb87b3f506d1b15af461487c3",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "274ebd634f05b23653719ef973119cf5",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "796edace73f874ebf46054b2a1ff0ba1",
|
||||
"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": "ba8ce3ca3858b4c2d20db68f99b788b2",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base_app.inc": "00f81f9bd92386ec12a6e60170678a98",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base_app.inc": "97efb93f4bfd8e8b09f2db24398e29fc",
|
||||
"src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3",
|
||||
"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": "8f4c2070174bdc2fbf735180394d7b3a"
|
||||
"src/ballistica/ui_v1/mgen/pyembed/binding_ui_v1.inc": "f5f054050d2b2fcd3763a4833fb32269"
|
||||
}
|
||||
178
.github/workflows/cd.yml
vendored
Normal file
178
.github/workflows/cd.yml
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
name: CD
|
||||
|
||||
on:
|
||||
# Run on pushes and pull-requests
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
make_linux_x86_64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_x86_64_gui_(debug)
|
||||
path: build/prefab/full/linux_x86_64_gui
|
||||
make_linux_x86_64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_x86_64_server_(debug)
|
||||
path: build/prefab/full/linux_x86_64_server
|
||||
make_linux_arm64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-linux-arm64-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_arm64_gui_(debug)
|
||||
path: build/prefab/full/linux_arm64_gui
|
||||
make_linux_arm64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-linux-arm64-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux_arm64_server_(debug)
|
||||
path: build/prefab/full/linux_arm64_server
|
||||
make_mac_x86_64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-x86-64-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_x86_64_gui_(debug)
|
||||
path: build/prefab/full/mac_x86_64_gui
|
||||
make_mac_x86_64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-x86-64-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_x86_64_server_(debug)
|
||||
path: build/prefab/full/mac_x86_64_server
|
||||
make_mac_arm64_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-arm64-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_arm64_gui_(debug)
|
||||
path: build/prefab/full/mac_arm64_gui
|
||||
make_mac_arm64_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-mac-arm64-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mac_arm64_server_(debug)
|
||||
path: build/prefab/full/mac_arm64_server
|
||||
make_windows_x86_gui_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-windows-x86-gui-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows_x86_gui_(debug)
|
||||
path: build/prefab/full/windows_x86_gui
|
||||
make_windows_x86_server_debug_build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install pip requirements
|
||||
run: tools/pcommand install_pip_reqs
|
||||
- name: Make the build
|
||||
run: make prefab-windows-x86-server-debug-build
|
||||
- name: Upload the build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows_x86_server_(debug)
|
||||
path: build/prefab/full/windows_x86_server
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -120,10 +120,10 @@ xcuserdata/
|
||||
/ballisticakit-android/BallisticaKit/src/main/res/mipmap-*/ic_launcher*.png
|
||||
/ballisticakit-android/BallisticaKit/src/cardboard/res/mipmap-*/ic_launcher*.png
|
||||
BallisticaKit.ico
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/Cursor macOS.appiconset/cursor_*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon iOS.appiconset/icon_*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon macOS.appiconset/icon_*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Layer*.imagestacklayer/Content.imageset/*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Layer*.imagestacklayer/Content.imageset/*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/Cursor macOS.imageset/cursor_*.png
|
||||
|
||||
3
.idea/ballisticakit.iml
generated
3
.idea/ballisticakit.iml
generated
@ -21,7 +21,6 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-android" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-cmake" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-ios.xcodeproj" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-mac.xcodeproj" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-windows" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-windows-oculus" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/ballisticakit-xcode" />
|
||||
@ -74,4 +73,4 @@
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="analyzeDoctest" value="false" />
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
|
||||
4
.idea/misc.xml
generated
4
.idea/misc.xml
generated
@ -4,10 +4,10 @@
|
||||
<option name="cmdArguments" value="--line-length 80 --skip-string-normalization" />
|
||||
<option name="enabledOnReformat" value="true" />
|
||||
<option name="pathToExecutable" value="/opt/homebrew/bin/black" />
|
||||
<option name="sdkUUID" value="1b270adb-5261-4492-85e8-d79b3894255d" />
|
||||
<option name="sdkName" value="Python 3.11" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
201
CHANGELOG.md
201
CHANGELOG.md
@ -1,19 +1,128 @@
|
||||
### 1.7.28 (build 21445, api 8, 2023-10-11)
|
||||
### 1.7.33 (build 21743, api 8, 2023-12-21)
|
||||
|
||||
### 1.7.32 (build 21741, api 8, 2023-12-20)
|
||||
- Fixed a screen message that no one will ever see (Thanks vishal332008?...)
|
||||
- Plugins window now displays 'No Plugins Installed' when no plugins are present (Thanks vishal332008!)
|
||||
- Old messages are now displayed as soon as you press 'Unmute Chat' (Thanks vishal332008!)
|
||||
- Added an 'Add to Favorites' entry to the party menu (Thanks vishal332008!)
|
||||
- Now displays 'No Parties Added' in favorites tab if no favorites are present (Thanks vishal332008!)
|
||||
- Now shows character icons in the profiles list window (Thanks vishal332008!)
|
||||
- Added a Random button for names in the Player Profiles window (Thanks vishal332008!)
|
||||
- Fixed a bug where no server is selected by default in the favorites tab (Thanks vishal332008!)
|
||||
- Fixed a bug where no replay is selected by default in the watch tab (Thanks vishal332008!)
|
||||
- Fixed a bug where no profile is selected by default in the profile tab (Thanks vishal332008!)
|
||||
- Fixed a number of UI screens so that ugly window edges are no longer visible
|
||||
in corners on modern ultra wide phone displays.
|
||||
- Added a `player_rejoin_cooldown` server config option. This defaults to 10
|
||||
seconds for servers but 0 for normal gui clients. This mechanism had been
|
||||
introduced recently to combat multiplayer fast-rejoin exploits and was set to
|
||||
10 seconds everywhere, but it could tend to be annoying for local single
|
||||
player play, dev testing, etc. Hopefully this strikes a good balance now.
|
||||
- Removed the player-rejoin-cooldown mechanism from the C++ layer since it was
|
||||
redundant with the Python level one and didn't cover as many cases.
|
||||
- Restored the behavior from before 1.7.28 where backgrounding the app would
|
||||
bring up the main menu and pause the action. Now it is implemented more
|
||||
cleanly however (an `on_app_active_changed()` call in the `AppMode` class).
|
||||
This means that it also applies to other platforms when the app reaches the
|
||||
'inactive' state; for instance when minimizing the window on the SDL build.
|
||||
|
||||
### 1.7.31 (build 21727, api 8, 2023-12-17)
|
||||
- Added `bascenev1.get_connection_to_host_info_2()` which is an improved
|
||||
type-safe version of `bascenev1.get_connection_to_host_info()`.
|
||||
- There is now a link to the official Discord server in the About section
|
||||
(thanks EraOSBeta!).
|
||||
- Native stack traces now work on Android; woohoo! Should be very helpful for
|
||||
debugging.
|
||||
- Added the concept of 'ui-operations' in the native layer to hopefully clear
|
||||
out the remaining double-window bugs. Basically, widgets used to schedule
|
||||
their payload commands to a future cycle of the event loop, meaning it was
|
||||
possible for commands that switched the main window to get scheduled twice
|
||||
before the first one ran (due to 2 key presses, etc), which could lead to all
|
||||
sorts of weirdness happening such as multiple windows popping up when one was
|
||||
intended. Now, however, such commands get scheduled to a current
|
||||
'ui-operation' and then run *almost* immediately, which should prevent such
|
||||
situations. Please holler if you run into any UI weirdness at this point.
|
||||
|
||||
### 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
|
||||
simplification on the Android layer, cleaning out years of cruft. This should
|
||||
put things in a better more maintainable place, but there will probably be
|
||||
some bugs to iron out, so please holler if you run into any.
|
||||
- Minimum supported Android version has been bumped from 5.0 to 6.0. Some
|
||||
upcoming tech such as ASTC textures will likely not be well supported on such
|
||||
old devices, so I think it is better to leave them running an older version
|
||||
that performs decently instead of a newer version that performs poorly. And
|
||||
letting go of old Android versions lets us better support new ones.
|
||||
- Android version now uses the 'Oboe' library as an audio back-end instead of
|
||||
OpenSL. This should result in better behaving audio in general. Please holler
|
||||
if you experience otherwise.
|
||||
- Bundled Android Python has been bumped to version 3.11.6.
|
||||
- Android app suspend behavior has been revamped. The app should stay running
|
||||
more often and be quicker to respond when dialogs or other activities
|
||||
temporarily pop up in front of it. This also allows it to continue playing
|
||||
music over other activities such as Google Play Games
|
||||
Achievements/Leaderboards screens. Please holler if you run into strange side
|
||||
effects such as the app continuing to play audio when it should not be.
|
||||
- Modernized the Android fullscreen setup code when running in Android 11 or
|
||||
newer. The game should now use the whole screen area, including the area
|
||||
around notches or camera cutouts. Please holler if you are seeing any problems
|
||||
related to this.
|
||||
- (build 21626) Fixed a bug where click/tap locations were incorrect on some
|
||||
builds when tv-border was on (Thanks for the heads-up Loup(Dliwk's fan)!).
|
||||
- (build 21631) Fixes an issue where '^^^^^^^^^^^^^' lines in stack traces could
|
||||
get chopped into tiny bits each on their own line in the dev console.
|
||||
- Hopefully finally fixed a longstanding issue where obscure cases such as
|
||||
multiple key presses simultaneously could cause multiple main menu windows to
|
||||
pop up. Please holler if you still see this problem happening anywhere. Also
|
||||
added a few related safety checks and warnings to help ensure UI code is free
|
||||
from such problems going forward. To make sure your custom UIs are behaving
|
||||
well in this system, do the following two things: 1) any time you call
|
||||
`set_main_menu_window()`, pass your existing main menu window root widget as
|
||||
`from_window`. 2) In any call that can lead to you switching the main menu
|
||||
window, check if your root widget is dead or transitioning out first and abort
|
||||
if it is. See any window in `ui_v1_lib` for examples.
|
||||
- (build 21691) Fixed a bug causing touches to not register in some cases on
|
||||
newer Android devices. (Huge thanks to JESWIN A J for helping me track that
|
||||
down!).
|
||||
- Temporarily removed the pause-the-game-when-backgrounded behavior for locally
|
||||
hosted games, mainly due to the code being hacky. Will try to restore this
|
||||
functionality in a cleaner way soon.
|
||||
|
||||
### 1.7.29 (build 21619, api 8, 2023-11-21)
|
||||
|
||||
- Simply continued work on the big 1.7.28 update. I was able to finally start
|
||||
updating the Mac App Store version of the game again (it had been stuck at
|
||||
1.4!), and it turns out that Apple AppStore submissions require the version
|
||||
number to increase each time and not just the build number, so we may start
|
||||
seeing more minor version number bumps for that reason.
|
||||
- Windows builds should now die with a clear error when the OpenGL version is
|
||||
too old (OpenGL 3.0 or newer is required). Previously they could die with more
|
||||
cryptic error messages such as "OpenGL function 'glActiveTexture2D' not
|
||||
found".
|
||||
|
||||
### 1.7.28 (build 21599, api 8, 2023-11-16)
|
||||
|
||||
- Turning off ticket continues on all platforms. I'll be moving the game towards
|
||||
a new monetization scheme mostly based on cosmetics and this has always felt a
|
||||
bit ugly pay-to-win to me, so it's time for it to go. Note that the
|
||||
functionality is still in there if anyone wants to support it in mods.
|
||||
- Massively cleaned up code related to rendering and window systems (OpenGL,
|
||||
SDL, etc). This code had been growing into a nasty tangle for 15 years
|
||||
attempting to support various old/hacked versions of SDL, etc. I ripped out
|
||||
huge chunks of it and put back still-relevant pieces in a much more cleanly
|
||||
designed way. This should put us in a much better place for supporting various
|
||||
platforms and making graphical improvements going forward. See
|
||||
`ballistica/base/app_adapter/app_adapter_sdl.cc` for an example of the now
|
||||
platforms and making graphical improvements going forward.
|
||||
`ballistica/base/app_adapter/app_adapter_sdl.cc` is an example of the now
|
||||
nicely implemented system.
|
||||
- The engine now requires OpenGL 3.0 or newer on desktop and OpenGL ES 3.0 or
|
||||
newer on mobile. This means we're cutting off a few percent of old devices on
|
||||
Android that only support ES 2, but ES 3 has been out for 10 years now so I
|
||||
feel it is time. As mentioned above, this allows massively cleaning up the
|
||||
graphics code which means we can start to improve it.
|
||||
- Removed gamma controls. These were only active on the old Mac version anyway
|
||||
graphics code which means we can start to improve it. Ideally now the GL
|
||||
renderer can be abstracted a bit more which will make the process of writing
|
||||
other renderers easier.
|
||||
- Removed gamma controls. These were only active on the old Mac builds anyway
|
||||
and are being removed from the upcoming SDL3, so if we want this sort of thing
|
||||
we should do it through shading in the renderer now.
|
||||
- Implemented both vsync and max-fps for the SDL build of the game. This means
|
||||
@ -129,7 +238,87 @@
|
||||
before. It also takes a `confirm` bool arg which allows it to be used to bring
|
||||
up a confirm dialog.
|
||||
- Clicking on a window close button to quit no longer brings up a confirm dialog
|
||||
and instead quits immediately (though with a proper graceful shutdown).
|
||||
and instead quits immediately (though with a proper graceful shutdown and a
|
||||
lovely little fade).
|
||||
- Camera shake is now supported in network games and replays. Somehow I didn't
|
||||
notice that was missing for years. The downside is this requires a server to
|
||||
be hosting protocol 35, which cuts off support for 1.4 clients. So for now I
|
||||
am keeping the default at 33. Once there a fewer 1.4 clients around we can
|
||||
consider changing this (if everything hasn't moved to SceneV2 by then).
|
||||
- Added a server option to set the hosting protocol for servers who might want
|
||||
to allow camera shake (or other minor features/fixes) that don't work in the
|
||||
default protocol 33. See `protocol_version` in `config.yaml`. Just remember
|
||||
that you will be cutting off support for older clients if you use 35.
|
||||
- Fixed a bug with screen-messages animating off screen too fast when frame
|
||||
rates are high.
|
||||
- Added a proper graceful shutdown process for the audio server. This should
|
||||
result in fewer ugly pops and warning messages when the app is quit.
|
||||
- Tidied up some keyboard shortcuts to be more platform-appropriate. For
|
||||
example, toggling fullscreen on Windows is now Alt+Enter or F11.
|
||||
- Fancy rebuilt Mac build should now automatically sync its frame rate to the
|
||||
display its running on (using CVDisplayLinks, not VSync).
|
||||
- Mac build is now relying solely on Apple's Game Controller Framework, which
|
||||
seems pretty awesome these days. It should support most stuff SDL does and
|
||||
with less configuring involved. Please holler if you come across something
|
||||
that doesn't work.
|
||||
- Mac build is also now using the Game Controller Framework to handle keyboard
|
||||
events. This should better handle things like modifier keys and also will
|
||||
allow us to use that exact same code on the iPad/iPhone version.
|
||||
- OS key repeat events are no longer passed through the engine. This means that
|
||||
any time we want repeating behavior, such as holding an arrow key to move
|
||||
through UI elements, we will need to wire it up ourselves. We already do this
|
||||
for things like game controllers however, so this is more consistent in a way.
|
||||
- Dev console no longer claims key events unless the Python tab is showing and
|
||||
there is a hardware keyboard attached. This allows showing dev console tabs
|
||||
above gameplay without interfering with it.
|
||||
- Added clipboard paste support to the dev console python terminal.
|
||||
- Added various text editing functionality to the dev console python terminal
|
||||
(cursor movement, deleting chars and words, etc.)
|
||||
- Internal on-screen-keyboard now has a cancel button (thanks vishal332008!)
|
||||
- Public servers list now shows 'No servers found' if there are no servers to
|
||||
show instead of just remaining mysteriously blank (thanks vishal332008!)
|
||||
- Players are now prevented from rejoining a session for 10 seconds after they
|
||||
leave to prevent game exploits. Note this is different than the existing
|
||||
system that prevents joining a *party* for 10 seconds; this covers people
|
||||
who never leave the party (Thanks EraOSBeta!).
|
||||
- Fixes an issue where servers could be crashed by flooding them with join
|
||||
requests (Thanks for the heads-up Era!).
|
||||
- The engine will now ignore empty device config dicts and fall back to
|
||||
defaults; these could theoretically happen if device config code fails
|
||||
somewhere and it previously would leave the device mysteriously inoperable.
|
||||
- The game will now show <unset> for controls with no bindings in the in-game
|
||||
guide and controller/keyboard config screens.
|
||||
- Fixed a crash that could occur if SDL couldn't find a name for connected
|
||||
joystick.
|
||||
- Simplified the app's handling of broken config files. Previously it would do
|
||||
various complex things such as offering to edit the broken config on desktop
|
||||
builds, avoiding overwriting broken configs, and automatically loading
|
||||
previous configs. Now, if it finds a broken config, it will simply back it up
|
||||
to a .broken file, log an error message, and then start up normally with a
|
||||
default config. This way, things are more consistent across platforms, and
|
||||
technical users can still fix and restore their old configs. Note that the app
|
||||
still also writes .prev configs for extra security, though it no longer uses
|
||||
them for anything itself.
|
||||
- Converted more internal engine time values from milliseconds to microseconds,
|
||||
including things like the internal EventLoop timeline. Please holler if you
|
||||
notice anything running 1000x too fast or slow. In general my strategy going
|
||||
forward is to use microseconds for exact internal time values but to mostly
|
||||
expose float seconds to the user, especially on the Python layer. There were
|
||||
starting to be a few cases were integer milliseconds was not enough precision
|
||||
for internal values. For instance, if we run with unclamped framerates and hit
|
||||
several hundred FPS, milliseconds per frame would drop to 0 which caused some
|
||||
problems. Note that scenev1 will be remaining on milliseconds internally for
|
||||
compatibility reasons. Scenev2 should move to microseconds though.
|
||||
- The V2 account id for the signed in account is now available at
|
||||
`ba*.app.plus.accounts.primary.accountid` (alongside some other existing
|
||||
account info).
|
||||
- (build 21585) Fixed an issue where some navigation key presses were getting
|
||||
incorrectly absorbed by text widgets. (Thanks for the heads-up Temp!)
|
||||
- (build 21585) Fixed an issue where texture quality changes would not take
|
||||
effect until next launch.
|
||||
- Added a 'glow_type' arg to `bauiv1.textwidget()` to adjust the glow used when
|
||||
the text is selected. The default is 'gradient' but there is now a 'uniform'
|
||||
option which may look better in some circumstances.
|
||||
|
||||
### 1.7.27 (build 21282, api 8, 2023-08-30)
|
||||
|
||||
|
||||
@ -41,8 +41,12 @@
|
||||
- Modder
|
||||
|
||||
### Era0S
|
||||
- Community Suggestions Implementer
|
||||
- Bug Fixer
|
||||
- Modder
|
||||
|
||||
### VinniTR
|
||||
- Fixes
|
||||
|
||||
### Rikko
|
||||
- Created the original "reject_recently_left_players" plugin
|
||||
|
||||
9
Makefile
9
Makefile
@ -779,13 +779,12 @@ check-full: py_check_prereqs
|
||||
|
||||
# Same as 'check' plus optional/slow extra checks.
|
||||
check2: py_check_prereqs
|
||||
@$(DMAKE) -j$(CPUS) update-check cpplint pylint mypy pycharm
|
||||
@$(DMAKE) -j$(CPUS) update-check cpplint pylint mypy
|
||||
@$(PCOMMANDBATCH) echo SGRN BLD ALL CHECKS PASSED!
|
||||
|
||||
# Same as check2 but no caching (all files are checked).
|
||||
check2-full: py_check_prereqs
|
||||
@$(DMAKE) -j$(CPUS) update-check cpplint-full pylint-full mypy-full \
|
||||
pycharm-full
|
||||
@$(DMAKE) -j$(CPUS) update-check cpplint-full pylint-full mypy-full
|
||||
@$(PCOMMANDBATCH) echo SGRN BLD ALL CHECKS PASSED!
|
||||
|
||||
# Run Cpplint checks on all C/C++ code.
|
||||
@ -924,14 +923,14 @@ preflight-full:
|
||||
preflight2:
|
||||
@$(MAKE) format
|
||||
@$(MAKE) update
|
||||
@$(MAKE) -j$(CPUS) cpplint pylint mypy pycharm test
|
||||
@$(MAKE) -j$(CPUS) cpplint pylint mypy test
|
||||
@$(PCOMMANDBATCH) echo SGRN BLD PREFLIGHT SUCCESSFUL!
|
||||
|
||||
# Same as 'preflight2' but without caching (all files visited).
|
||||
preflight2-full:
|
||||
@$(MAKE) format-full
|
||||
@$(MAKE) update
|
||||
@$(MAKE) -j$(CPUS) cpplint-full pylint-full mypy-full pycharm-full test-full
|
||||
@$(MAKE) -j$(CPUS) cpplint-full pylint-full mypy-full test-full
|
||||
@$(PCOMMANDBATCH) echo SGRN BLD PREFLIGHT SUCCESSFUL!
|
||||
|
||||
# Tell make which of these targets don't represent files.
|
||||
|
||||
@ -6,7 +6,7 @@ height="50" alt="logo">
|
||||
|
||||
***-ica***: collection of things relating to a specific theme.
|
||||
|
||||
[](https://github.com/efroemling/ballistica/actions/workflows/ci.yml)
|
||||
[](https://github.com/efroemling/ballistica/actions/workflows/ci.yml) [](https://github.com/efroemling/ballistica/actions/workflows/cd.yml)
|
||||
|
||||
The Ballistica project is the foundation for
|
||||
[BombSquad](https://www.froemling.net/apps/bombsquad) and potentially other
|
||||
@ -52,7 +52,7 @@ want to keep that spirit alive as the Ballistica project moves forward. Whether
|
||||
this means making it easier to share mods, organize tournaments, join up with
|
||||
friends, teach each other some Python, or whatever else. Life is short; let's
|
||||
play some games. Or make them. Maybe both.
|
||||
|
||||
|
||||
### Frequently Asked Questions
|
||||
|
||||
* **Q: What's with this name? Is it BombSquad or Ballistica?**
|
||||
@ -86,4 +86,4 @@ Playstation / My Toaster??**
|
||||
for more details or the [Ballistica
|
||||
Downloads](https://ballistica.net/downloads) page for early test builds on
|
||||
some platforms.
|
||||
|
||||
|
||||
|
||||
1
ballisticakit-cmake/.idea/misc.xml
generated
1
ballisticakit-cmake/.idea/misc.xml
generated
@ -14,7 +14,6 @@
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-android" />
|
||||
<file path="$PROJECT_DIR$/.idea" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-ios.xcodeproj" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-mac.xcodeproj" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-windows" />
|
||||
<file path="$PROJECT_DIR$/../ballisticakit-xcode" />
|
||||
<file path="$PROJECT_DIR$/../build" />
|
||||
|
||||
@ -353,9 +353,15 @@ set(BALLISTICA_SOURCES
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/camera.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/frame_def.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/frame_def.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_client_context.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_client_context.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_settings.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/graphics_settings.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/net_graph.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/render_command_buffer.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/screen_messages.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/support/screen_messages.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/text/font_page_map_data.h
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/text/text_graphics.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/graphics/text/text_graphics.h
|
||||
@ -432,16 +438,19 @@ set(BALLISTICA_SOURCES
|
||||
${BA_SRC_ROOT}/ballistica/base/support/app_config.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/app_config.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/app_timer.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/base_build_switches.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/base_build_switches.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/classic_soft.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/context.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/context.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/display_timer.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/huffman.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/huffman.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/plus_soft.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/repeater.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/repeater.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/stdio_console.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/stdio_console.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/stress_test.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/stress_test.h
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/dev_console.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/dev_console.h
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/ui.cc
|
||||
@ -454,6 +463,8 @@ set(BALLISTICA_SOURCES
|
||||
${BA_SRC_ROOT}/ballistica/classic/python/classic_python.h
|
||||
${BA_SRC_ROOT}/ballistica/classic/python/methods/python_methods_classic.cc
|
||||
${BA_SRC_ROOT}/ballistica/classic/python/methods/python_methods_classic.h
|
||||
${BA_SRC_ROOT}/ballistica/classic/support/stress_test.cc
|
||||
${BA_SRC_ROOT}/ballistica/classic/support/stress_test.h
|
||||
${BA_SRC_ROOT}/ballistica/classic/support/v1_account.cc
|
||||
${BA_SRC_ROOT}/ballistica/classic/support/v1_account.h
|
||||
${BA_SRC_ROOT}/ballistica/core/core.cc
|
||||
@ -686,8 +697,10 @@ set(BALLISTICA_SOURCES
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/json.cc
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/json.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/lambda_runnable.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/native_stack_trace.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/runnable.cc
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/runnable.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/snapshot.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.cc
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/timer_list.h
|
||||
${BA_SRC_ROOT}/ballistica/shared/generic/utf8.cc
|
||||
|
||||
@ -345,9 +345,15 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\net_graph.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\screen_messages.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\screen_messages.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\text\font_page_map_data.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\text\text_graphics.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\text\text_graphics.h" />
|
||||
@ -424,16 +430,19 @@
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\app_config.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\app_config.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\base_build_switches.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\base_build_switches.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\context.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\context.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\display_timer.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\huffman.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc" />
|
||||
@ -446,6 +455,8 @@
|
||||
<ClInclude Include="..\..\src\ballistica\classic\python\classic_python.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\python\methods\python_methods_classic.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\python\methods\python_methods_classic.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\stress_test.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\support\stress_test.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\v1_account.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\support\v1_account.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\core\core.cc" />
|
||||
@ -678,8 +689,10 @@
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\json.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\json.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\native_stack_trace.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />
|
||||
|
||||
@ -469,6 +469,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
@ -478,6 +490,12 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\screen_messages.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\screen_messages.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\text\font_page_map_data.h">
|
||||
<Filter>ballistica\base\graphics\text</Filter>
|
||||
</ClInclude>
|
||||
@ -706,6 +724,12 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\base_build_switches.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\base_build_switches.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
@ -715,6 +739,9 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\context.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\display_timer.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
@ -724,18 +751,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClCompile>
|
||||
@ -772,6 +799,12 @@
|
||||
<ClInclude Include="..\..\src\ballistica\classic\python\methods\python_methods_classic.h">
|
||||
<Filter>ballistica\classic\python\methods</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\stress_test.cc">
|
||||
<Filter>ballistica\classic\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\classic\support\stress_test.h">
|
||||
<Filter>ballistica\classic\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\v1_account.cc">
|
||||
<Filter>ballistica\classic\support</Filter>
|
||||
</ClCompile>
|
||||
@ -1468,12 +1501,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\native_stack_trace.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@ -340,9 +340,15 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\camera.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\frame_def.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\net_graph.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\screen_messages.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\screen_messages.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\text\font_page_map_data.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\text\text_graphics.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\text\text_graphics.h" />
|
||||
@ -419,16 +425,19 @@
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\app_config.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\app_config.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\base_build_switches.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\base_build_switches.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\context.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\context.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\display_timer.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\huffman.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc" />
|
||||
@ -441,6 +450,8 @@
|
||||
<ClInclude Include="..\..\src\ballistica\classic\python\classic_python.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\python\methods\python_methods_classic.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\python\methods\python_methods_classic.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\stress_test.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\support\stress_test.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\v1_account.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\support\v1_account.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\core\core.cc" />
|
||||
@ -673,8 +684,10 @@
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\json.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\json.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\native_stack_trace.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\timer_list.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\utf8.cc" />
|
||||
|
||||
@ -469,6 +469,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\frame_def.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_client_context.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\graphics_settings.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\graphics_settings.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\net_graph.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
@ -478,6 +490,12 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\render_command_buffer.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\graphics\support\screen_messages.cc">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\support\screen_messages.h">
|
||||
<Filter>ballistica\base\graphics\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\graphics\text\font_page_map_data.h">
|
||||
<Filter>ballistica\base\graphics\text</Filter>
|
||||
</ClInclude>
|
||||
@ -706,6 +724,12 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\app_timer.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\base_build_switches.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\base_build_switches.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\classic_soft.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
@ -715,6 +739,9 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\context.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\display_timer.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
@ -724,18 +751,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClCompile>
|
||||
@ -772,6 +799,12 @@
|
||||
<ClInclude Include="..\..\src\ballistica\classic\python\methods\python_methods_classic.h">
|
||||
<Filter>ballistica\classic\python\methods</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\stress_test.cc">
|
||||
<Filter>ballistica\classic\support</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\classic\support\stress_test.h">
|
||||
<Filter>ballistica\classic\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\classic\support\v1_account.cc">
|
||||
<Filter>ballistica\classic\support</Filter>
|
||||
</ClCompile>
|
||||
@ -1468,12 +1501,18 @@
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\lambda_runnable.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\native_stack_trace.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\runnable.cc">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\runnable.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\shared\generic\snapshot.h">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\shared\generic\timer_list.cc">
|
||||
<Filter>ballistica\shared\generic</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@ -10,11 +10,11 @@
|
||||
"src/ballistica/base/graphics/texture/ktx.cc",
|
||||
"src/ballistica/core/platform/android/android_gl3.h",
|
||||
"src/ballistica/base/platform/apple/app_delegate.h",
|
||||
"src/ballistica/base/platform/apple/scripting_bridge_music.h",
|
||||
"src/ballistica/base/platform/apple/MacMusicApp.h",
|
||||
"src/ballistica/base/platform/apple/MacMusicAppScriptingBridge.h",
|
||||
"src/ballistica/core/platform/android/utf8/checked.h",
|
||||
"src/ballistica/core/platform/android/utf8/unchecked.h",
|
||||
"src/ballistica/core/platform/android/utf8/core.h",
|
||||
"src/ballistica/base/platform/apple/sdl_main_mac.h",
|
||||
"src/ballistica/base/platform/oculus/main_rift.cc",
|
||||
"src/ballistica/core/platform/android/android_gl3.c"
|
||||
],
|
||||
|
||||
@ -53,47 +53,60 @@ ctx.src_omit_paths = {
|
||||
'src/assets/workspace',
|
||||
}
|
||||
|
||||
# Use this to 'carve out' directories or exact file paths which will be
|
||||
# git-managed on dst. By default, spinoff will consider dirs containing
|
||||
# the files it syncs from src as 'spinoff-managed'; it will set them as
|
||||
# git-ignored and will complain if any files appear in them that it does
|
||||
# not manage itself (to prevent accidentally doing work in such places).
|
||||
# Note that adding a dir to src_write_paths does not prevent files
|
||||
# within it from being synced by spinoff; it just means that each of
|
||||
# those individual spinoff-managed files will have their own gitignore
|
||||
# entry since there is no longer one covering the whole dir. So to keep
|
||||
# things tidy, carve out the minimal set of exact file/dir paths that
|
||||
# you need.
|
||||
# Use this to 'carve out' files or directories which will be git-managed
|
||||
# on dst.
|
||||
#
|
||||
# By default, spinoff will consider dirs containing the files it syncs
|
||||
# from src as 'spinoff-managed'; it will set them as git-ignored and
|
||||
# will complain if any files appear in them that it does not manage
|
||||
# itself (to prevent accidentally doing work in such places). Note that
|
||||
# adding a dir to src_write_paths does not prevent files within it from
|
||||
# being synced by spinoff; it just means that each of those individual
|
||||
# spinoff-managed files will have their own gitignore entry since there
|
||||
# can't be a single one covering the whole dir. So to keep things tidy,
|
||||
# carve out the minimal set of exact file/dir paths that you need.
|
||||
ctx.src_write_paths = {
|
||||
'tools/spinoff',
|
||||
'config/spinoffconfig.py',
|
||||
}
|
||||
|
||||
# Normally spinoff errors if it finds any files in its managed dirs that
|
||||
# it did not put there. This is to prevent accidentally working in these
|
||||
# parts of a dst project; since these sections are git-ignored, git
|
||||
# itself won't raise any warnings in such cases and it would be easy to
|
||||
# accidentally lose work otherwise.
|
||||
# Use this to 'carve out' files or directories under spinoff managed
|
||||
# dirs which will be completely ignored by spinoff (but *not* placed
|
||||
# under git control).
|
||||
#
|
||||
# This list can be used to suppress spinoff's errors for specific
|
||||
# locations. This is generally used to allow build output or other
|
||||
# dynamically generated files to exist within spinoff-managed
|
||||
# directories. It is possible to use src_write_paths for such purposes,
|
||||
# but this has the side-effect of greatly complicating the dst project's
|
||||
# gitignore list; selectively marking a few dirs as unchecked makes for
|
||||
# a cleaner setup. Just be careful to not set excessively broad regions
|
||||
# as unchecked; you don't want to mask actual useful error messages.
|
||||
# Normally spinoff will error if it finds any files under its managed
|
||||
# dirs that it did not put there. This is to prevent accidentally
|
||||
# working in these parts of a dst project; since spinoff-controlled
|
||||
# stuff is git-ignored, git itself won't raise any warnings in such
|
||||
# cases and it would be easy to accidentally blow away changes if
|
||||
# spinoff didn't raise a stink.
|
||||
#
|
||||
# This list is used to suppress raising of said stink for specific
|
||||
# locations. This allows build output or other dynamically generated
|
||||
# files to exist under spinoff-managed directories. It is also possible
|
||||
# to use src_write_paths for such carve-outs, but that can have the
|
||||
# negative side-effect of greatly complicating the dst project's
|
||||
# .gitignore file. Selectively marking a few specific files or dirs as
|
||||
# unchecked instead can keep things tidier and more understandable.
|
||||
#
|
||||
# Note that files and paths marked as unchecked cannot be the
|
||||
# destination for synced files, as that would be ambiguous (We can
|
||||
# either sync the file ourself or expect someone else to write it, but
|
||||
# not both).
|
||||
ctx.src_unchecked_paths = {
|
||||
'src/ballistica/mgen',
|
||||
'src/ballistica/*/mgen',
|
||||
'src/assets/ba_data/python/*/_mgen',
|
||||
'src/meta/*/mgen',
|
||||
'ballisticakit-cmake/.clang-format',
|
||||
'ballisticakit-android/BallisticaKit/src/cardboard/res',
|
||||
'ballisticakit-windows/*/BallisticaKit.ico',
|
||||
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets',
|
||||
'ballisticakit-android/BallisticaKit/src/*/res',
|
||||
'ballisticakit-android/BallisticaKit/src/*/assets',
|
||||
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/*/*.png',
|
||||
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/*/*/*.png',
|
||||
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/*/*/*/*/*.png',
|
||||
'ballisticakit-xcode/BallisticaKit.xcodeproj/'
|
||||
'project.xcworkspace/xcuserdata',
|
||||
'ballisticakit-android/BallisticaKit/src/*/res/*/*.png',
|
||||
'ballisticakit-android/BallisticaKit/src/*/assets/ballistica_files',
|
||||
'ballisticakit-android/local.properties',
|
||||
'ballisticakit-android/.gradle',
|
||||
'ballisticakit-android/build',
|
||||
@ -139,7 +152,6 @@ ctx.filter_dirs = {
|
||||
'ballisticakit-cmake',
|
||||
'ballisticakit-xcode/BallisticaKit.xcodeproj',
|
||||
'ballisticakit-ios.xcodeproj',
|
||||
'ballisticakit-mac.xcodeproj',
|
||||
'config',
|
||||
'src/assets/pdoc',
|
||||
}
|
||||
@ -182,6 +194,7 @@ ctx.filter_file_names = {
|
||||
'.projectile',
|
||||
'.editorconfig',
|
||||
'ci.yml',
|
||||
'cd.yml',
|
||||
'LICENSE',
|
||||
'cloudtool',
|
||||
'bacloud',
|
||||
@ -263,6 +276,7 @@ ctx.filter_file_extensions = {
|
||||
'.frag',
|
||||
'.vert',
|
||||
'.xcsettings',
|
||||
'.xcstrings',
|
||||
'.filters',
|
||||
}
|
||||
|
||||
|
||||
@ -1465,6 +1465,10 @@
|
||||
"ba_data/textures/discordLogo.ktx",
|
||||
"ba_data/textures/discordLogo.pvr",
|
||||
"ba_data/textures/discordLogo_preview.png",
|
||||
"ba_data/textures/discordServer.dds",
|
||||
"ba_data/textures/discordServer.ktx",
|
||||
"ba_data/textures/discordServer.pvr",
|
||||
"ba_data/textures/discordServer_preview.png",
|
||||
"ba_data/textures/doomShroomBGColor.dds",
|
||||
"ba_data/textures/doomShroomBGColor.ktx",
|
||||
"ba_data/textures/doomShroomBGColor.pvr",
|
||||
|
||||
@ -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",
|
||||
@ -366,11 +368,11 @@
|
||||
"ba_data/python/bauiv1lib/__pycache__/characterpicker.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/colorpicker.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/config.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/configerror.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/confirm.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/continues.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/creditslist.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/debug.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/discord.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/feedback.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/fileselector.cpython-311.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/__pycache__/getcurrency.cpython-311.opt-1.pyc",
|
||||
@ -417,7 +419,6 @@
|
||||
"ba_data/python/bauiv1lib/characterpicker.py",
|
||||
"ba_data/python/bauiv1lib/colorpicker.py",
|
||||
"ba_data/python/bauiv1lib/config.py",
|
||||
"ba_data/python/bauiv1lib/configerror.py",
|
||||
"ba_data/python/bauiv1lib/confirm.py",
|
||||
"ba_data/python/bauiv1lib/continues.py",
|
||||
"ba_data/python/bauiv1lib/coop/__init__.py",
|
||||
@ -432,6 +433,7 @@
|
||||
"ba_data/python/bauiv1lib/coop/tournamentbutton.py",
|
||||
"ba_data/python/bauiv1lib/creditslist.py",
|
||||
"ba_data/python/bauiv1lib/debug.py",
|
||||
"ba_data/python/bauiv1lib/discord.py",
|
||||
"ba_data/python/bauiv1lib/feedback.py",
|
||||
"ba_data/python/bauiv1lib/fileselector.py",
|
||||
"ba_data/python/bauiv1lib/gather/__init__.py",
|
||||
|
||||
@ -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 \
|
||||
@ -341,7 +342,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/characterpicker.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/colorpicker.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/config.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/configerror.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/confirm.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/continues.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/__init__.py \
|
||||
@ -351,6 +351,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/tournamentbutton.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/creditslist.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/debug.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/discord.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/feedback.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/fileselector.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__init__.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 \
|
||||
@ -615,7 +617,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/characterpicker.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/colorpicker.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/config.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/configerror.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/confirm.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/continues.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/__pycache__/__init__.cpython-311.opt-1.pyc \
|
||||
@ -625,6 +626,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/__pycache__/tournamentbutton.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/creditslist.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/debug.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/discord.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/feedback.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/fileselector.cpython-311.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/gather/__pycache__/__init__.cpython-311.opt-1.pyc \
|
||||
@ -5710,6 +5712,7 @@ TEX2D_DDS_TARGETS = \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIcon.dds \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIconColorMask.dds \
|
||||
$(BUILD_DIR)/ba_data/textures/discordLogo.dds \
|
||||
$(BUILD_DIR)/ba_data/textures/discordServer.dds \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomBGColor.dds \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomLevelColor.dds \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomPreview.dds \
|
||||
@ -6113,6 +6116,7 @@ TEX2D_PVR_TARGETS = \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIcon.pvr \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIconColorMask.pvr \
|
||||
$(BUILD_DIR)/ba_data/textures/discordLogo.pvr \
|
||||
$(BUILD_DIR)/ba_data/textures/discordServer.pvr \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomBGColor.pvr \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomLevelColor.pvr \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomPreview.pvr \
|
||||
@ -6516,6 +6520,7 @@ TEX2D_KTX_TARGETS = \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIcon.ktx \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIconColorMask.ktx \
|
||||
$(BUILD_DIR)/ba_data/textures/discordLogo.ktx \
|
||||
$(BUILD_DIR)/ba_data/textures/discordServer.ktx \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomBGColor.ktx \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomLevelColor.ktx \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomPreview.ktx \
|
||||
@ -6919,6 +6924,7 @@ TEX2D_PREVIEW_PNG_TARGETS = \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIconColorMask_preview.png \
|
||||
$(BUILD_DIR)/ba_data/textures/cyborgIcon_preview.png \
|
||||
$(BUILD_DIR)/ba_data/textures/discordLogo_preview.png \
|
||||
$(BUILD_DIR)/ba_data/textures/discordServer_preview.png \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomBGColor_preview.png \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomLevelColor_preview.png \
|
||||
$(BUILD_DIR)/ba_data/textures/doomShroomPreview_preview.png \
|
||||
|
||||
@ -27,7 +27,10 @@ from _babase import (
|
||||
apptime,
|
||||
apptimer,
|
||||
AppTimer,
|
||||
can_toggle_fullscreen,
|
||||
fullscreen_control_available,
|
||||
fullscreen_control_get,
|
||||
fullscreen_control_key_shortcut,
|
||||
fullscreen_control_set,
|
||||
charstr,
|
||||
clipboard_get_text,
|
||||
clipboard_has_text,
|
||||
@ -57,11 +60,10 @@ from _babase import (
|
||||
have_permission,
|
||||
in_logic_thread,
|
||||
increment_analytics_count,
|
||||
invoke_main_menu,
|
||||
is_os_playing_music,
|
||||
is_running_on_fire_tv,
|
||||
is_xcode_build,
|
||||
lock_all_input,
|
||||
mac_music_app_get_library_source,
|
||||
mac_music_app_get_playlists,
|
||||
mac_music_app_get_volume,
|
||||
mac_music_app_init,
|
||||
@ -72,7 +74,10 @@ from _babase import (
|
||||
music_player_set_volume,
|
||||
music_player_shutdown,
|
||||
music_player_stop,
|
||||
native_review_request,
|
||||
native_review_request_supported,
|
||||
native_stack_trace,
|
||||
open_file_externally,
|
||||
print_load_info,
|
||||
pushcall,
|
||||
quit,
|
||||
@ -82,7 +87,6 @@ from _babase import (
|
||||
screenmessage,
|
||||
set_analytics_screen,
|
||||
set_low_level_config_value,
|
||||
set_stress_testing,
|
||||
set_thread_name,
|
||||
set_ui_input_device,
|
||||
show_progress_bar,
|
||||
@ -151,9 +155,8 @@ from babase._general import (
|
||||
getclass,
|
||||
get_type_name,
|
||||
)
|
||||
from babase._keyboard import Keyboard
|
||||
from babase._language import Lstr, LanguageSubsystem
|
||||
from babase._login import LoginAdapter
|
||||
from babase._login import LoginAdapter, LoginInfo
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
# (PyCharm inspection bug?)
|
||||
@ -200,7 +203,10 @@ __all__ = [
|
||||
'apptimer',
|
||||
'AppTimer',
|
||||
'Call',
|
||||
'can_toggle_fullscreen',
|
||||
'fullscreen_control_available',
|
||||
'fullscreen_control_get',
|
||||
'fullscreen_control_key_shortcut',
|
||||
'fullscreen_control_set',
|
||||
'charstr',
|
||||
'clipboard_get_text',
|
||||
'clipboard_has_text',
|
||||
@ -249,18 +255,17 @@ __all__ = [
|
||||
'increment_analytics_count',
|
||||
'InputDeviceNotFoundError',
|
||||
'InputType',
|
||||
'invoke_main_menu',
|
||||
'is_browser_likely_available',
|
||||
'is_browser_likely_available',
|
||||
'is_os_playing_music',
|
||||
'is_point_in_box',
|
||||
'is_running_on_fire_tv',
|
||||
'is_xcode_build',
|
||||
'Keyboard',
|
||||
'LanguageSubsystem',
|
||||
'lock_all_input',
|
||||
'LoginAdapter',
|
||||
'LoginInfo',
|
||||
'Lstr',
|
||||
'mac_music_app_get_library_source',
|
||||
'mac_music_app_get_playlists',
|
||||
'mac_music_app_get_volume',
|
||||
'mac_music_app_init',
|
||||
@ -273,10 +278,13 @@ __all__ = [
|
||||
'music_player_set_volume',
|
||||
'music_player_shutdown',
|
||||
'music_player_stop',
|
||||
'native_review_request',
|
||||
'native_review_request_supported',
|
||||
'native_stack_trace',
|
||||
'NodeNotFoundError',
|
||||
'normalized_color',
|
||||
'NotFoundError',
|
||||
'open_file_externally',
|
||||
'Permission',
|
||||
'PlayerNotFoundError',
|
||||
'Plugin',
|
||||
@ -297,7 +305,6 @@ __all__ = [
|
||||
'SessionTeamNotFoundError',
|
||||
'set_analytics_screen',
|
||||
'set_low_level_config_value',
|
||||
'set_stress_testing',
|
||||
'set_thread_name',
|
||||
'set_ui_input_device',
|
||||
'show_progress_bar',
|
||||
|
||||
@ -6,7 +6,7 @@ from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, assert_never
|
||||
|
||||
from efro.call import tpartial
|
||||
from efro.error import CommunicationError
|
||||
@ -16,7 +16,7 @@ import _babase
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any
|
||||
|
||||
from babase._login import LoginAdapter
|
||||
from babase._login import LoginAdapter, LoginInfo
|
||||
|
||||
|
||||
DEBUG_LOG = False
|
||||
@ -27,10 +27,12 @@ class AccountV2Subsystem:
|
||||
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.accounts'.
|
||||
Access the single shared instance of this class at 'ba.app.plus.accounts'.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
from babase._login import LoginAdapterGPGS, LoginAdapterGameCenter
|
||||
|
||||
# Whether or not everything related to an initial login
|
||||
# (or lack thereof) has completed. This includes things like
|
||||
# workspace syncing. Completion of this is what flips the app
|
||||
@ -45,16 +47,13 @@ class AccountV2Subsystem:
|
||||
self._implicit_state_changed = False
|
||||
self._can_do_auto_sign_in = True
|
||||
|
||||
if _babase.app.classic is None:
|
||||
raise RuntimeError('Needs updating for no-classic case.')
|
||||
|
||||
if (
|
||||
_babase.app.classic.platform == 'android'
|
||||
and _babase.app.classic.subplatform == 'google'
|
||||
):
|
||||
from babase._login import LoginAdapterGPGS
|
||||
|
||||
self.login_adapters[LoginType.GPGS] = LoginAdapterGPGS()
|
||||
adapter: LoginAdapter
|
||||
if _babase.using_google_play_game_services():
|
||||
adapter = LoginAdapterGPGS()
|
||||
self.login_adapters[adapter.login_type] = adapter
|
||||
if _babase.using_game_center():
|
||||
adapter = LoginAdapterGameCenter()
|
||||
self.login_adapters[adapter.login_type] = adapter
|
||||
|
||||
def on_app_loading(self) -> None:
|
||||
"""Should be called at standard on_app_loading time."""
|
||||
@ -62,10 +61,6 @@ class AccountV2Subsystem:
|
||||
for adapter in self.login_adapters.values():
|
||||
adapter.on_app_loading()
|
||||
|
||||
def set_primary_credentials(self, credentials: str | None) -> None:
|
||||
"""Set credentials for the primary app account."""
|
||||
raise NotImplementedError('This should be overridden.')
|
||||
|
||||
def have_primary_credentials(self) -> bool:
|
||||
"""Are credentials currently set for the primary app account?
|
||||
|
||||
@ -80,10 +75,6 @@ class AccountV2Subsystem:
|
||||
"""The primary account for the app, or None if not logged in."""
|
||||
return self.do_get_primary()
|
||||
|
||||
def do_get_primary(self) -> AccountV2Handle | None:
|
||||
"""Internal - should be overridden by subclass."""
|
||||
return None
|
||||
|
||||
def on_primary_account_changed(
|
||||
self, account: AccountV2Handle | None
|
||||
) -> None:
|
||||
@ -142,6 +133,8 @@ class AccountV2Subsystem:
|
||||
"""An implicit sign-in happened (called by native layer)."""
|
||||
from babase._login import LoginAdapter
|
||||
|
||||
assert _babase.in_logic_thread()
|
||||
|
||||
with _babase.ContextRef.empty():
|
||||
self.login_adapters[login_type].set_implicit_login_state(
|
||||
LoginAdapter.ImplicitLoginState(
|
||||
@ -151,6 +144,7 @@ class AccountV2Subsystem:
|
||||
|
||||
def on_implicit_sign_out(self, login_type: LoginType) -> None:
|
||||
"""An implicit sign-out happened (called by native layer)."""
|
||||
assert _babase.in_logic_thread()
|
||||
with _babase.ContextRef.empty():
|
||||
self.login_adapters[login_type].set_implicit_login_state(None)
|
||||
|
||||
@ -192,9 +186,10 @@ class AccountV2Subsystem:
|
||||
cfgkey = 'ImplicitLoginStates'
|
||||
cfgdict = _babase.app.config.setdefault(cfgkey, {})
|
||||
|
||||
# Store which (if any) adapter is currently implicitly signed in.
|
||||
# Making the assumption there will only ever be one implicit
|
||||
# adapter at a time; may need to update this if that changes.
|
||||
# Store which (if any) adapter is currently implicitly signed
|
||||
# in. Making the assumption there will only ever be one implicit
|
||||
# adapter at a time; may need to revisit this logic if that
|
||||
# changes.
|
||||
prev_state = cfgdict.get(login_type.value)
|
||||
if state is None:
|
||||
self._implicit_signed_in_adapter = None
|
||||
@ -205,18 +200,26 @@ class AccountV2Subsystem:
|
||||
state.login_id
|
||||
)
|
||||
|
||||
# Special case: if the user is already signed in but not with
|
||||
# this implicit login, we may want to let them know that the
|
||||
# 'Welcome back FOO' they likely just saw is not actually
|
||||
# accurate.
|
||||
# Special case: if the user is already signed in but not
|
||||
# with this implicit login, let them know that the 'Welcome
|
||||
# back FOO' they likely just saw is not actually accurate.
|
||||
if (
|
||||
self.primary is not None
|
||||
and not self.login_adapters[login_type].is_back_end_active()
|
||||
):
|
||||
service_str: Lstr | None
|
||||
if login_type is LoginType.GPGS:
|
||||
service_str = Lstr(resource='googlePlayText')
|
||||
else:
|
||||
elif login_type is LoginType.GAME_CENTER:
|
||||
# Note: Apparently Game Center is just called 'Game
|
||||
# Center' in all languages. Can revisit if not true.
|
||||
# https://developer.apple.com/forums/thread/725779
|
||||
service_str = Lstr(value='Game Center')
|
||||
elif login_type is LoginType.EMAIL:
|
||||
# Not possible; just here for exhaustive coverage.
|
||||
service_str = None
|
||||
else:
|
||||
assert_never(login_type)
|
||||
if service_str is not None:
|
||||
_babase.apptimer(
|
||||
2.0,
|
||||
@ -259,6 +262,14 @@ class AccountV2Subsystem:
|
||||
# We may want to auto-sign-in based on this new state.
|
||||
self._update_auto_sign_in()
|
||||
|
||||
def do_get_primary(self) -> AccountV2Handle | None:
|
||||
"""Internal - should be overridden by subclass."""
|
||||
raise NotImplementedError('This should be overridden.')
|
||||
|
||||
def set_primary_credentials(self, credentials: str | None) -> None:
|
||||
"""Set credentials for the primary app account."""
|
||||
raise NotImplementedError('This should be overridden.')
|
||||
|
||||
def _update_auto_sign_in(self) -> None:
|
||||
plus = _babase.app.plus
|
||||
assert plus is not None
|
||||
@ -266,7 +277,7 @@ class AccountV2Subsystem:
|
||||
# If implicit state has changed, try to respond.
|
||||
if self._implicit_state_changed:
|
||||
if self._implicit_signed_in_adapter is None:
|
||||
# If implicit back-end is signed out, follow suit
|
||||
# If implicit back-end has signed out, we follow suit
|
||||
# immediately; no need to wait for network connectivity.
|
||||
if DEBUG_LOG:
|
||||
logging.debug(
|
||||
@ -286,9 +297,8 @@ class AccountV2Subsystem:
|
||||
# Consider this an 'explicit' sign in because the
|
||||
# implicit-login state change presumably was triggered
|
||||
# by some user action (signing in, signing out, or
|
||||
# switching accounts via the back-end).
|
||||
# NOTE: should test case where we don't have
|
||||
# connectivity here.
|
||||
# switching accounts via the back-end). NOTE: should
|
||||
# test case where we don't have connectivity here.
|
||||
if plus.cloud.is_connected():
|
||||
if DEBUG_LOG:
|
||||
logging.debug(
|
||||
@ -419,14 +429,11 @@ class AccountV2Handle:
|
||||
used with some operations such as cloud messaging.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.tag = '?'
|
||||
|
||||
self.workspacename: str | None = None
|
||||
self.workspaceid: str | None = None
|
||||
|
||||
# Login types and their display-names associated with this account.
|
||||
self.logins: dict[LoginType, str] = {}
|
||||
accountid: str
|
||||
tag: str
|
||||
workspacename: str | None
|
||||
workspaceid: str | None
|
||||
logins: dict[LoginType, LoginInfo]
|
||||
|
||||
def __enter__(self) -> None:
|
||||
"""Support for "with" statement.
|
||||
|
||||
@ -56,6 +56,8 @@ class App:
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
# A few things defined as non-optional values but not actually
|
||||
# available until the app starts.
|
||||
plugins: PluginSubsystem
|
||||
lang: LanguageSubsystem
|
||||
health_monitor: AppHealthMonitor
|
||||
@ -92,7 +94,7 @@ class App:
|
||||
|
||||
# Used on platforms such as mobile where the app basically needs
|
||||
# to shut down while backgrounded. In this state, all event
|
||||
# loops are suspended and all graphics and audio should cease
|
||||
# loops are suspended and all graphics and audio must cease
|
||||
# completely. Be aware that the suspended state can be entered
|
||||
# from any other state including NATIVE_BOOTSTRAPPING and
|
||||
# SHUTTING_DOWN.
|
||||
@ -149,9 +151,9 @@ class App:
|
||||
def __init__(self) -> None:
|
||||
"""(internal)
|
||||
|
||||
Do not instantiate this class; access the single shared instance
|
||||
of it as 'app' which is available in various Ballistica
|
||||
feature-set modules such as babase.
|
||||
Do not instantiate this class. You can access the single shared
|
||||
instance of it through various high level packages: 'babase.app',
|
||||
'bascenev1.app', 'bauiv1.app', etc.
|
||||
"""
|
||||
|
||||
# Hack for docs-generation: we can be imported with dummy modules
|
||||
@ -182,7 +184,6 @@ class App:
|
||||
# foregrounded; can be a simple way to determine if network data
|
||||
# should be refreshed/etc.
|
||||
self.fg_state = 0
|
||||
self.config_file_healthy: bool = False
|
||||
|
||||
self._subsystems: list[AppSubsystem] = []
|
||||
self._native_bootstrapping_completed = False
|
||||
@ -208,7 +209,8 @@ class App:
|
||||
self._shutdown_task: asyncio.Task[None] | None = None
|
||||
self._shutdown_tasks: list[Coroutine[None, None, None]] = [
|
||||
self._wait_for_shutdown_suppressions(),
|
||||
self._fade_for_shutdown(),
|
||||
self._fade_and_shutdown_graphics(),
|
||||
self._fade_and_shutdown_audio(),
|
||||
]
|
||||
self._pool_thread_count = 0
|
||||
|
||||
@ -227,6 +229,15 @@ class App:
|
||||
self.lang = LanguageSubsystem()
|
||||
self.plugins = PluginSubsystem()
|
||||
|
||||
@property
|
||||
def active(self) -> bool:
|
||||
"""Whether the app is currently front and center.
|
||||
|
||||
This will be False when the app is hidden, other activities
|
||||
are covering it, etc. (depending on the platform).
|
||||
"""
|
||||
return _babase.app_is_active()
|
||||
|
||||
@property
|
||||
def aioloop(self) -> asyncio.AbstractEventLoop:
|
||||
"""The logic thread's asyncio event loop.
|
||||
@ -426,11 +437,17 @@ class App:
|
||||
self._native_shutdown_complete_called = True
|
||||
self._update_state()
|
||||
|
||||
def on_native_active_changed(self) -> None:
|
||||
"""Called by the native layer when the app active state changes."""
|
||||
assert _babase.in_logic_thread()
|
||||
if self._mode is not None:
|
||||
self._mode.on_app_active_changed()
|
||||
|
||||
def read_config(self) -> None:
|
||||
"""(internal)"""
|
||||
from babase._appconfig import read_app_config
|
||||
|
||||
self._config, self.config_file_healthy = read_app_config()
|
||||
self._config = read_app_config()
|
||||
|
||||
def handle_deep_link(self, url: str) -> None:
|
||||
"""Handle a deep link URL."""
|
||||
@ -508,7 +525,7 @@ class App:
|
||||
except Exception:
|
||||
logging.exception('Error setting app intent to %s.', intent)
|
||||
_babase.pushcall(
|
||||
tpartial(self._apply_intent_error, intent),
|
||||
tpartial(self._display_set_intent_error, intent),
|
||||
from_other_thread=True,
|
||||
)
|
||||
|
||||
@ -553,10 +570,11 @@ class App:
|
||||
'Error handling intent %s in app-mode %s.', intent, mode
|
||||
)
|
||||
|
||||
def _apply_intent_error(self, intent: AppIntent) -> None:
|
||||
def _display_set_intent_error(self, intent: AppIntent) -> None:
|
||||
"""Show the *user* something went wrong setting an intent."""
|
||||
from babase._language import Lstr
|
||||
|
||||
del intent # Unused.
|
||||
del intent
|
||||
_babase.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
|
||||
_babase.getsimplesound('error').play()
|
||||
|
||||
@ -579,19 +597,6 @@ class App:
|
||||
self._aioloop = _asyncio.setup_asyncio()
|
||||
self.health_monitor = AppHealthMonitor()
|
||||
|
||||
# Only proceed if our config file is healthy so we don't
|
||||
# overwrite a broken one or whatnot and wipe out data.
|
||||
if not self.config_file_healthy:
|
||||
if self.classic is not None:
|
||||
handled = self.classic.show_config_error_window()
|
||||
if handled:
|
||||
return
|
||||
|
||||
# For now on other systems we just overwrite the bum config.
|
||||
# At this point settings are already set; lets just commit
|
||||
# them to disk.
|
||||
_appconfig.commit_app_config(force=True)
|
||||
|
||||
# __FEATURESET_APP_SUBSYSTEM_CREATE_BEGIN__
|
||||
# This section generated by batools.appmodule; do not edit.
|
||||
|
||||
@ -795,6 +800,7 @@ class App:
|
||||
async def _shutdown(self) -> None:
|
||||
import asyncio
|
||||
|
||||
_babase.lock_all_input()
|
||||
try:
|
||||
async with asyncio.TaskGroup() as task_group:
|
||||
for task_coro in self._shutdown_tasks:
|
||||
@ -890,23 +896,45 @@ class App:
|
||||
import asyncio
|
||||
|
||||
# Spin and wait for anything blocking shutdown to complete.
|
||||
starttime = _babase.apptime()
|
||||
_babase.lifecyclelog('shutdown-suppress wait begin')
|
||||
while _babase.shutdown_suppress_count() > 0:
|
||||
await asyncio.sleep(0.001)
|
||||
_babase.lifecyclelog('shutdown-suppress wait end')
|
||||
duration = _babase.apptime() - starttime
|
||||
if duration > 1.0:
|
||||
logging.warning(
|
||||
'Shutdown-suppressions lasted longer than ideal '
|
||||
'(%.2f seconds).',
|
||||
duration,
|
||||
)
|
||||
|
||||
async def _fade_for_shutdown(self) -> None:
|
||||
async def _fade_and_shutdown_graphics(self) -> None:
|
||||
import asyncio
|
||||
|
||||
# Kick off a fade, block input, and wait for a short bit.
|
||||
# Ideally most shutdown activity completes during the fade so
|
||||
# there's no tangible wait.
|
||||
_babase.lifecyclelog('fade-for-shutdown begin')
|
||||
# Kick off a short fade and give it time to complete.
|
||||
_babase.lifecyclelog('fade-and-shutdown-graphics begin')
|
||||
_babase.fade_screen(False, time=0.15)
|
||||
_babase.lock_all_input()
|
||||
# _babase.getsimplesound('swish2').play()
|
||||
await asyncio.sleep(0.15)
|
||||
_babase.lifecyclelog('fade-for-shutdown end')
|
||||
|
||||
# Now tell the graphics system to go down and wait until
|
||||
# it has done so.
|
||||
_babase.graphics_shutdown_begin()
|
||||
while not _babase.graphics_shutdown_is_complete():
|
||||
await asyncio.sleep(0.01)
|
||||
_babase.lifecyclelog('fade-and-shutdown-graphics end')
|
||||
|
||||
async def _fade_and_shutdown_audio(self) -> None:
|
||||
import asyncio
|
||||
|
||||
# Tell the audio system to go down and give it a bit of
|
||||
# time to do so gracefully.
|
||||
_babase.lifecyclelog('fade-and-shutdown-audio begin')
|
||||
_babase.audio_shutdown_begin()
|
||||
await asyncio.sleep(0.15)
|
||||
while not _babase.audio_shutdown_is_complete():
|
||||
await asyncio.sleep(0.01)
|
||||
_babase.lifecyclelog('fade-and-shutdown-audio end')
|
||||
|
||||
def _threadpool_no_wait_done(self, fut: Future) -> None:
|
||||
try:
|
||||
|
||||
@ -101,15 +101,13 @@ class AppConfig(dict):
|
||||
self.commit()
|
||||
|
||||
|
||||
def read_app_config() -> tuple[AppConfig, bool]:
|
||||
def read_app_config() -> AppConfig:
|
||||
"""Read the app config."""
|
||||
import os
|
||||
import json
|
||||
|
||||
config_file_healthy = False
|
||||
|
||||
# NOTE: it is assumed that this only gets called once and the
|
||||
# config object will not change from here on out
|
||||
# NOTE: it is assumed that this only gets called once and the config
|
||||
# object will not change from here on out
|
||||
config_file_path = _babase.app.env.config_file_path
|
||||
config_contents = ''
|
||||
try:
|
||||
@ -119,20 +117,16 @@ def read_app_config() -> tuple[AppConfig, bool]:
|
||||
config = AppConfig(json.loads(config_contents))
|
||||
else:
|
||||
config = AppConfig()
|
||||
config_file_healthy = True
|
||||
|
||||
except Exception:
|
||||
logging.exception(
|
||||
"Error reading config file at time %.3f: '%s'.",
|
||||
"Error reading config file '%s' at time %.3f.\n"
|
||||
"Backing up broken config to'%s.broken'.",
|
||||
config_file_path,
|
||||
_babase.apptime(),
|
||||
config_file_path,
|
||||
)
|
||||
|
||||
# Whenever this happens lets back up the broken one just in case it
|
||||
# gets overwritten accidentally.
|
||||
logging.info(
|
||||
"Backing up current config file to '%s.broken'", config_file_path
|
||||
)
|
||||
try:
|
||||
import shutil
|
||||
|
||||
@ -141,23 +135,10 @@ def read_app_config() -> tuple[AppConfig, bool]:
|
||||
logging.exception('Error copying broken config.')
|
||||
config = AppConfig()
|
||||
|
||||
# Now attempt to read one of our 'prev' backup copies.
|
||||
prev_path = config_file_path + '.prev'
|
||||
try:
|
||||
if os.path.exists(prev_path):
|
||||
with open(prev_path, encoding='utf-8') as infile:
|
||||
config_contents = infile.read()
|
||||
config = AppConfig(json.loads(config_contents))
|
||||
else:
|
||||
config = AppConfig()
|
||||
config_file_healthy = True
|
||||
logging.info('Successfully read backup config.')
|
||||
except Exception:
|
||||
logging.exception('Error reading prev backup config.')
|
||||
return config, config_file_healthy
|
||||
return config
|
||||
|
||||
|
||||
def commit_app_config(force: bool = False) -> None:
|
||||
def commit_app_config() -> None:
|
||||
"""Commit the config to persistent storage.
|
||||
|
||||
Category: **General Utility Functions**
|
||||
@ -167,10 +148,4 @@ def commit_app_config(force: bool = False) -> None:
|
||||
plus = _babase.app.plus
|
||||
assert plus is not None
|
||||
|
||||
if not _babase.app.config_file_healthy and not force:
|
||||
logging.warning(
|
||||
'Current config file is broken; '
|
||||
'skipping write to avoid losing settings.'
|
||||
)
|
||||
return
|
||||
plus.mark_config_dirty()
|
||||
|
||||
@ -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
|
||||
@ -51,3 +52,10 @@ class AppMode:
|
||||
|
||||
def on_deactivate(self) -> None:
|
||||
"""Called when the mode is being deactivated."""
|
||||
|
||||
def on_app_active_changed(self) -> None:
|
||||
"""Called when babase.app.active changes.
|
||||
|
||||
The app-mode may want to take action such as pausing a running
|
||||
game in such cases.
|
||||
"""
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -26,6 +26,11 @@ DEBUG_LOG = False
|
||||
class CloudSubsystem(AppSubsystem):
|
||||
"""Manages communication with cloud components."""
|
||||
|
||||
@property
|
||||
def connected(self) -> bool:
|
||||
"""Property equivalent of CloudSubsystem.is_connected()."""
|
||||
return self.is_connected()
|
||||
|
||||
def is_connected(self) -> bool:
|
||||
"""Return whether a connection to the cloud is present.
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"""Dev-Console functionality."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
@ -154,9 +155,10 @@ class DevConsoleSubsystem:
|
||||
# All tabs in the dev-console. Add your own stuff here via
|
||||
# plugins or whatnot.
|
||||
self.tabs: list[DevConsoleTabEntry] = [
|
||||
DevConsoleTabEntry('Python', DevConsoleTabPython),
|
||||
DevConsoleTabEntry('Test', DevConsoleTabTest),
|
||||
DevConsoleTabEntry('Python', DevConsoleTabPython)
|
||||
]
|
||||
if os.environ.get('BA_DEV_CONSOLE_TEST_TAB', '0') == '1':
|
||||
self.tabs.append(DevConsoleTabEntry('Test', DevConsoleTabTest))
|
||||
self.is_refreshing = False
|
||||
|
||||
def do_refresh_tab(self, tabname: str) -> None:
|
||||
|
||||
@ -185,10 +185,8 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None:
|
||||
def _on_log(entry: LogEntry) -> None:
|
||||
# Forward this along to the engine to display in the in-app
|
||||
# console, in the Android log, etc.
|
||||
_babase.display_log(
|
||||
name=entry.name,
|
||||
level=entry.level.name,
|
||||
message=entry.message,
|
||||
_babase.emit_log(
|
||||
name=entry.name, level=entry.level.name, message=entry.message
|
||||
)
|
||||
|
||||
# We also want to feed some logs to the old v1-cloud-log system.
|
||||
|
||||
@ -33,18 +33,47 @@ def reset_to_main_menu() -> None:
|
||||
logging.warning('reset_to_main_menu: no-op due to classic not present.')
|
||||
|
||||
|
||||
def set_config_fullscreen_on() -> None:
|
||||
def get_v2_account_id() -> str | None:
|
||||
"""Return the current V2 account id if signed in, or None if not."""
|
||||
try:
|
||||
plus = _babase.app.plus
|
||||
if plus is not None:
|
||||
account = plus.accounts.primary
|
||||
if account is not None:
|
||||
accountid = account.accountid
|
||||
# (Avoids mypy complaints when plus is not present)
|
||||
assert isinstance(accountid, (str, type(None)))
|
||||
return accountid
|
||||
return None
|
||||
except Exception:
|
||||
logging.exception('Error fetching v2 account id.')
|
||||
return None
|
||||
|
||||
|
||||
def store_config_fullscreen_on() -> None:
|
||||
"""The OS has changed our fullscreen state and we should take note."""
|
||||
_babase.app.config['Fullscreen'] = True
|
||||
_babase.app.config.commit()
|
||||
|
||||
|
||||
def set_config_fullscreen_off() -> None:
|
||||
def store_config_fullscreen_off() -> None:
|
||||
"""The OS has changed our fullscreen state and we should take note."""
|
||||
_babase.app.config['Fullscreen'] = False
|
||||
_babase.app.config.commit()
|
||||
|
||||
|
||||
def set_config_fullscreen_on() -> None:
|
||||
"""Set and store fullscreen state"""
|
||||
_babase.app.config['Fullscreen'] = True
|
||||
_babase.app.config.apply_and_commit()
|
||||
|
||||
|
||||
def set_config_fullscreen_off() -> None:
|
||||
"""The OS has changed our fullscreen state and we should take note."""
|
||||
_babase.app.config['Fullscreen'] = False
|
||||
_babase.app.config.apply_and_commit()
|
||||
|
||||
|
||||
def not_signed_in_screen_message() -> None:
|
||||
from babase._language import Lstr
|
||||
|
||||
@ -111,6 +140,14 @@ def error_message() -> None:
|
||||
_babase.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
|
||||
|
||||
|
||||
def success_message() -> None:
|
||||
from babase._language import Lstr
|
||||
|
||||
if _babase.app.env.gui:
|
||||
_babase.getsimplesound('dingSmall').play()
|
||||
_babase.screenmessage(Lstr(resource='successText'), color=(0, 1, 0))
|
||||
|
||||
|
||||
def purchase_not_valid_error() -> None:
|
||||
from babase._language import Lstr
|
||||
|
||||
@ -300,6 +337,7 @@ def implicit_sign_in(
|
||||
from bacommon.login import LoginType
|
||||
|
||||
assert _babase.app.plus is not None
|
||||
|
||||
_babase.app.plus.accounts.on_implicit_sign_in(
|
||||
login_type=LoginType(login_type_str),
|
||||
login_id=login_id,
|
||||
@ -377,3 +415,17 @@ def string_edit_adapter_can_be_replaced(adapter: StringEditAdapter) -> bool:
|
||||
def get_dev_console_tab_names() -> list[str]:
|
||||
"""Return the current set of dev-console tab names."""
|
||||
return [t.name for t in _babase.app.devconsole.tabs]
|
||||
|
||||
|
||||
def unsupported_controller_message(name: str) -> None:
|
||||
"""Print a message when an unsupported controller is connected."""
|
||||
from babase._language import Lstr
|
||||
|
||||
# Ick; this can get called early in the bootstrapping process
|
||||
# before we're allowed to load assets. Guard against that.
|
||||
if _babase.asset_loads_allowed():
|
||||
_babase.getsimplesound('error').play()
|
||||
_babase.screenmessage(
|
||||
Lstr(resource='unsupportedControllerText', subs=[('${NAME}', name)]),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
|
||||
@ -20,6 +20,13 @@ if TYPE_CHECKING:
|
||||
DEBUG_LOG = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class LoginInfo:
|
||||
"""Basic info about a login available in the app.plus.accounts section."""
|
||||
|
||||
name: str
|
||||
|
||||
|
||||
class LoginAdapter:
|
||||
"""Allows using implicit login types in an explicit way.
|
||||
|
||||
@ -138,7 +145,7 @@ class LoginAdapter:
|
||||
is actually being used by the app. It should therefore register
|
||||
unlocked achievements, leaderboard scores, allow viewing native
|
||||
UIs, etc. When not active it should ignore everything and behave
|
||||
as if logged out, even if it technically is still logged in.
|
||||
as if signed out, even if it technically is still signed in.
|
||||
"""
|
||||
assert _babase.in_logic_thread()
|
||||
del active # Unused.
|
||||
@ -149,7 +156,7 @@ class LoginAdapter:
|
||||
result_cb: Callable[[LoginAdapter, SignInResult | Exception], None],
|
||||
description: str,
|
||||
) -> None:
|
||||
"""Attempt an explicit sign in via this adapter.
|
||||
"""Attempt to sign in via this adapter.
|
||||
|
||||
This can be called even if the back-end is not implicitly signed in;
|
||||
the adapter will attempt to sign in if possible. An exception will
|
||||
@ -161,7 +168,7 @@ class LoginAdapter:
|
||||
# Have been seeing multiple sign-in attempts come through
|
||||
# nearly simultaneously which can be problematic server-side.
|
||||
# Let's error if a sign-in attempt is made within a few seconds
|
||||
# of the last one to address this.
|
||||
# of the last one to try and address this.
|
||||
now = time.monotonic()
|
||||
appnow = _babase.apptime()
|
||||
if self._last_sign_in_time is not None:
|
||||
@ -229,6 +236,7 @@ class LoginAdapter:
|
||||
def _got_sign_in_response(
|
||||
response: bacommon.cloud.SignInResponse | Exception,
|
||||
) -> None:
|
||||
# This likely means we couldn't communicate with the server.
|
||||
if isinstance(response, Exception):
|
||||
if DEBUG_LOG:
|
||||
logging.debug(
|
||||
@ -239,20 +247,18 @@ class LoginAdapter:
|
||||
)
|
||||
_babase.pushcall(Call(result_cb, self, response))
|
||||
else:
|
||||
if DEBUG_LOG:
|
||||
logging.debug(
|
||||
'LoginAdapter: %s adapter got successful'
|
||||
' sign-in response',
|
||||
self.login_type.name,
|
||||
)
|
||||
# This means our credentials were explicitly rejected.
|
||||
if response.credentials is None:
|
||||
result2: LoginAdapter.SignInResult | Exception = (
|
||||
RuntimeError(
|
||||
'No credentials returned after'
|
||||
' submitting sign-in-token.'
|
||||
)
|
||||
RuntimeError('Sign-in-token was rejected.')
|
||||
)
|
||||
else:
|
||||
if DEBUG_LOG:
|
||||
logging.debug(
|
||||
'LoginAdapter: %s adapter got successful'
|
||||
' sign-in response',
|
||||
self.login_type.name,
|
||||
)
|
||||
result2 = self.SignInResult(
|
||||
credentials=response.credentials
|
||||
)
|
||||
@ -269,7 +275,7 @@ class LoginAdapter:
|
||||
on_response=_got_sign_in_response,
|
||||
)
|
||||
|
||||
# Kick off the process by fetching a sign-in token.
|
||||
# Kick off the sign-in process by fetching a sign-in token.
|
||||
self.get_sign_in_token(completion_cb=_got_sign_in_token_result)
|
||||
|
||||
def is_back_end_active(self) -> bool:
|
||||
@ -282,11 +288,10 @@ class LoginAdapter:
|
||||
"""Get a sign-in token from the adapter back end.
|
||||
|
||||
This token is then passed to the master-server to complete the
|
||||
login process.
|
||||
The adapter can use this opportunity to bring up account creation
|
||||
UI, call its internal sign_in function, etc. as needed.
|
||||
The provided completion_cb should then be called with either a token
|
||||
or None if sign in failed or was cancelled.
|
||||
sign-in process. The adapter can use this opportunity to bring
|
||||
up account creation UI, call its internal sign_in function, etc.
|
||||
as needed. The provided completion_cb should then be called with
|
||||
either a token or None if sign in failed or was cancelled.
|
||||
"""
|
||||
from babase._general import Call
|
||||
|
||||
@ -295,7 +300,7 @@ class LoginAdapter:
|
||||
|
||||
def _update_implicit_login_state(self) -> None:
|
||||
# If we've received an implicit login state, schedule it to be
|
||||
# sent along to the app. We wait until on-app-launch has been
|
||||
# sent along to the app. We wait until on-app-loading has been
|
||||
# called so that account-client-v2 has had a chance to load
|
||||
# any existing state so it can properly respond to this.
|
||||
if self._implicit_login_state_dirty and self._on_app_loading_called:
|
||||
@ -340,8 +345,8 @@ class LoginAdapter:
|
||||
class LoginAdapterNative(LoginAdapter):
|
||||
"""A login adapter that does its work in the native layer."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(LoginType.GPGS)
|
||||
def __init__(self, login_type: LoginType) -> None:
|
||||
super().__init__(login_type)
|
||||
|
||||
# Store int ids for in-flight attempts since they may go through
|
||||
# various platform layers and back.
|
||||
@ -375,3 +380,13 @@ class LoginAdapterNative(LoginAdapter):
|
||||
|
||||
class LoginAdapterGPGS(LoginAdapterNative):
|
||||
"""Google Play Game Services adapter."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(LoginType.GPGS)
|
||||
|
||||
|
||||
class LoginAdapterGameCenter(LoginAdapterNative):
|
||||
"""Apple Game Center adapter."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(LoginType.GAME_CENTER)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -104,8 +104,8 @@ def show_user_scripts() -> None:
|
||||
|
||||
_error.print_exception('error writing about_this_folder stuff')
|
||||
|
||||
# On a few platforms we try to open the dir in the UI.
|
||||
if app.classic is not None and app.classic.platform in ['mac', 'windows']:
|
||||
# On platforms that support it, open the dir in the UI.
|
||||
if _babase.supports_open_dir_externally():
|
||||
_babase.open_dir_externally(env.python_directory_user)
|
||||
|
||||
# Otherwise we just print a pretty version of it.
|
||||
|
||||
@ -302,6 +302,11 @@ class AccountV1Subsystem:
|
||||
"""(internal)"""
|
||||
plus = babase.app.plus
|
||||
if plus is None:
|
||||
import logging
|
||||
|
||||
logging.warning(
|
||||
'Error adding pending promo code; plus not present.'
|
||||
)
|
||||
babase.screenmessage(
|
||||
babase.Lstr(resource='errorText'), color=(1, 0, 0)
|
||||
)
|
||||
|
||||
@ -4,10 +4,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import babase
|
||||
import bauiv1
|
||||
import bascenev1
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -31,6 +32,7 @@ class AdsSubsystem:
|
||||
self.last_in_game_ad_remove_message_show_time: float | None = None
|
||||
self.last_ad_completion_time: float | None = None
|
||||
self.last_ad_was_short = False
|
||||
self._fallback_task: asyncio.Task | None = None
|
||||
|
||||
def do_remove_in_game_ads_message(self) -> None:
|
||||
"""(internal)"""
|
||||
@ -69,7 +71,8 @@ class AdsSubsystem:
|
||||
) -> None:
|
||||
"""(internal)"""
|
||||
self.last_ad_purpose = purpose
|
||||
bauiv1.show_ad(purpose, on_completion_call)
|
||||
assert babase.app.plus is not None
|
||||
babase.app.plus.show_ad(purpose, on_completion_call)
|
||||
|
||||
def show_ad_2(
|
||||
self,
|
||||
@ -78,7 +81,8 @@ class AdsSubsystem:
|
||||
) -> None:
|
||||
"""(internal)"""
|
||||
self.last_ad_purpose = purpose
|
||||
bauiv1.show_ad_2(purpose, on_completion_call)
|
||||
assert babase.app.plus is not None
|
||||
babase.app.plus.show_ad_2(purpose, on_completion_call)
|
||||
|
||||
def call_after_ad(self, call: Callable[[], Any]) -> None:
|
||||
"""Run a call after potentially showing an ad."""
|
||||
@ -94,7 +98,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.
|
||||
@ -132,7 +136,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(
|
||||
@ -181,36 +185,53 @@ class AdsSubsystem:
|
||||
|
||||
# If we're *still* cleared to show, actually tell the system to show.
|
||||
if show:
|
||||
# As a safety-check, set up an object that will run
|
||||
# the completion callback if we've returned and sat for 10 seconds
|
||||
# (in case some random ad network doesn't properly deliver its
|
||||
# completion callback).
|
||||
# As a safety-check, we set up an object that will run the
|
||||
# completion callback if we've returned and sat for several
|
||||
# seconds (in case some random ad network doesn't properly
|
||||
# deliver its completion callback).
|
||||
class _Payload:
|
||||
def __init__(self, pcall: Callable[[], Any]):
|
||||
self._call = pcall
|
||||
self._ran = False
|
||||
|
||||
def run(self, fallback: bool = False) -> None:
|
||||
"""Run fallback call (and issue a warning about it)."""
|
||||
"""Run the payload."""
|
||||
assert app.classic is not None
|
||||
if not self._ran:
|
||||
if fallback:
|
||||
lanst = app.classic.ads.last_ad_network_set_time
|
||||
print(
|
||||
'ERROR: relying on fallback ad-callback! '
|
||||
'last network: '
|
||||
+ app.classic.ads.last_ad_network
|
||||
+ ' (set '
|
||||
+ str(int(time.time() - lanst))
|
||||
+ 's ago); purpose='
|
||||
+ app.classic.ads.last_ad_purpose
|
||||
logging.error(
|
||||
'Relying on fallback ad-callback! '
|
||||
'last network: %s (set %s seconds ago);'
|
||||
' purpose=%s.',
|
||||
app.classic.ads.last_ad_network,
|
||||
time.time() - lanst,
|
||||
app.classic.ads.last_ad_purpose,
|
||||
)
|
||||
babase.pushcall(self._call)
|
||||
self._ran = True
|
||||
|
||||
payload = _Payload(call)
|
||||
|
||||
# Set up our backup.
|
||||
with babase.ContextRef.empty():
|
||||
babase.apptimer(5.0, lambda: payload.run(fallback=True))
|
||||
# Note to self: Previously this was a simple 5 second
|
||||
# timer because the app got totally suspended while ads
|
||||
# were showing (which delayed the timer), but these days
|
||||
# the app may continue to run, so we need to be more
|
||||
# careful and only fire the fallback after we see that
|
||||
# the app has been front-and-center for several seconds.
|
||||
async def add_fallback_task() -> None:
|
||||
activesecs = 5
|
||||
while activesecs > 0:
|
||||
if babase.app.active:
|
||||
activesecs -= 1
|
||||
await asyncio.sleep(1.0)
|
||||
payload.run(fallback=True)
|
||||
|
||||
_fallback_task = babase.app.aioloop.create_task(
|
||||
add_fallback_task()
|
||||
)
|
||||
self.show_ad('between_game', on_completion_call=payload.run)
|
||||
else:
|
||||
babase.pushcall(call) # Just run the callback without the ad.
|
||||
|
||||
@ -41,5 +41,6 @@ class AppDelegate:
|
||||
sessiontype,
|
||||
settings,
|
||||
completion_call=completion_call,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check since we don't know.
|
||||
)
|
||||
|
||||
@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import babase
|
||||
import bascenev1
|
||||
import _baclassic
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence
|
||||
@ -54,7 +55,6 @@ def run_stress_test(
|
||||
round_duration: int = 30,
|
||||
) -> None:
|
||||
"""Run a stress test."""
|
||||
from babase import modutils
|
||||
|
||||
babase.screenmessage(
|
||||
"Beginning stress test.. use 'End Test' to stop testing.",
|
||||
@ -69,22 +69,12 @@ def run_stress_test(
|
||||
'round_duration': round_duration,
|
||||
}
|
||||
)
|
||||
babase.apptimer(
|
||||
7.0,
|
||||
babase.Call(
|
||||
babase.screenmessage,
|
||||
(
|
||||
'stats will be written to '
|
||||
+ modutils.get_human_readable_user_scripts_path()
|
||||
+ '/stress_test_stats.csv'
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def stop_stress_test() -> None:
|
||||
"""End a running stress test."""
|
||||
babase.set_stress_testing(False, 0)
|
||||
|
||||
_baclassic.set_stress_testing(False, 0)
|
||||
assert babase.app.classic is not None
|
||||
try:
|
||||
if babase.app.classic.stress_test_reset_timer is not None:
|
||||
@ -134,14 +124,14 @@ def start_stress_test(args: dict[str, Any]) -> None:
|
||||
babase.Call(bascenev1.new_host_session, FreeForAllSession),
|
||||
),
|
||||
)
|
||||
babase.set_stress_testing(True, args['player_count'])
|
||||
_baclassic.set_stress_testing(True, args['player_count'])
|
||||
babase.app.classic.stress_test_reset_timer = babase.AppTimer(
|
||||
args['round_duration'], babase.Call(_reset_stress_test, args)
|
||||
)
|
||||
|
||||
|
||||
def _reset_stress_test(args: dict[str, Any]) -> None:
|
||||
babase.set_stress_testing(False, args['player_count'])
|
||||
_baclassic.set_stress_testing(False, args['player_count'])
|
||||
babase.screenmessage('Resetting stress test...')
|
||||
session = bascenev1.get_foreground_host_session()
|
||||
assert session is not None
|
||||
|
||||
@ -20,7 +20,6 @@ def get_input_device_mapped_value(
|
||||
This checks the user config and falls back to default values
|
||||
where available.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-return-statements
|
||||
# pylint: disable=too-many-branches
|
||||
|
||||
@ -40,7 +39,14 @@ def get_input_device_mapped_value(
|
||||
mapping = ccfgs[devicename][unique_id]
|
||||
elif 'default' in ccfgs[devicename]:
|
||||
mapping = ccfgs[devicename]['default']
|
||||
if mapping is not None:
|
||||
|
||||
# We now use the config mapping *only* if it is not empty.
|
||||
# There have been cases of config writing code messing up
|
||||
# and leaving empty dicts in the app config, which currently
|
||||
# leaves the device unusable. Alternatively, we'd perhaps
|
||||
# want to fall back to defaults for individual missing
|
||||
# values, but that is a bigger change we can make later.
|
||||
if isinstance(mapping, dict) and mapping:
|
||||
return mapping.get(name, -1)
|
||||
|
||||
if platform == 'windows':
|
||||
@ -76,91 +82,6 @@ def get_input_device_mapped_value(
|
||||
'triggerRun1': 5,
|
||||
}.get(name, -1)
|
||||
|
||||
# Look for some exact types.
|
||||
if babase.is_running_on_fire_tv():
|
||||
if devicename in ['Thunder', 'Amazon Fire Game Controller']:
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'unassignedButtonsRun': False,
|
||||
'buttonPickUp': 101,
|
||||
'buttonBomb': 98,
|
||||
'buttonJump': 97,
|
||||
'analogStickDeadZone': 0.0,
|
||||
'startButtonActivatesDefaultWidget': False,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 100,
|
||||
'buttonRun2': 103,
|
||||
'buttonRun1': 104,
|
||||
'triggerRun1': 24,
|
||||
}.get(name, -1)
|
||||
if devicename == 'NYKO PLAYPAD PRO':
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'triggerRun1': 24,
|
||||
'buttonPickUp': 101,
|
||||
'buttonBomb': 98,
|
||||
'buttonJump': 97,
|
||||
'buttonUp': 20,
|
||||
'buttonLeft': 22,
|
||||
'buttonRight': 23,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 100,
|
||||
'buttonDown': 21,
|
||||
}.get(name, -1)
|
||||
if devicename == 'Logitech Dual Action':
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'triggerRun1': 24,
|
||||
'buttonPickUp': 98,
|
||||
'buttonBomb': 101,
|
||||
'buttonJump': 100,
|
||||
'buttonStart': 109,
|
||||
'buttonPunch': 97,
|
||||
}.get(name, -1)
|
||||
if devicename == 'Xbox 360 Wireless Receiver':
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'triggerRun1': 24,
|
||||
'buttonPickUp': 101,
|
||||
'buttonBomb': 98,
|
||||
'buttonJump': 97,
|
||||
'buttonUp': 20,
|
||||
'buttonLeft': 22,
|
||||
'buttonRight': 23,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 100,
|
||||
'buttonDown': 21,
|
||||
}.get(name, -1)
|
||||
if devicename == 'Microsoft X-Box 360 pad':
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'triggerRun1': 24,
|
||||
'buttonPickUp': 101,
|
||||
'buttonBomb': 98,
|
||||
'buttonJump': 97,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 100,
|
||||
}.get(name, -1)
|
||||
if devicename in [
|
||||
'Amazon Remote',
|
||||
'Amazon Bluetooth Dev',
|
||||
'Amazon Fire TV Remote',
|
||||
]:
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'triggerRun1': 24,
|
||||
'buttonPickUp': 24,
|
||||
'buttonBomb': 91,
|
||||
'buttonJump': 86,
|
||||
'buttonUp': 20,
|
||||
'buttonLeft': 22,
|
||||
'startButtonActivatesDefaultWidget': False,
|
||||
'buttonRight': 23,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 90,
|
||||
'buttonDown': 21,
|
||||
}.get(name, -1)
|
||||
|
||||
elif 'NVIDIA SHIELD;' in useragentstring:
|
||||
if 'NVIDIA Controller' in devicename:
|
||||
return {
|
||||
@ -175,112 +96,6 @@ def get_input_device_mapped_value(
|
||||
'buttonIgnored': 184,
|
||||
'buttonIgnored2': 86,
|
||||
}.get(name, -1)
|
||||
elif platform == 'mac':
|
||||
if devicename == 'PLAYSTATION(R)3 Controller':
|
||||
return {
|
||||
'buttonLeft': 8,
|
||||
'buttonUp': 5,
|
||||
'buttonRight': 6,
|
||||
'buttonDown': 7,
|
||||
'buttonJump': 15,
|
||||
'buttonPunch': 16,
|
||||
'buttonBomb': 14,
|
||||
'buttonPickUp': 13,
|
||||
'buttonStart': 4,
|
||||
'buttonIgnored': 17,
|
||||
}.get(name, -1)
|
||||
if devicename in ['Wireless 360 Controller', 'Controller']:
|
||||
# Xbox360 gamepads
|
||||
return {
|
||||
'analogStickDeadZone': 1.2,
|
||||
'buttonBomb': 13,
|
||||
'buttonDown': 2,
|
||||
'buttonJump': 12,
|
||||
'buttonLeft': 3,
|
||||
'buttonPickUp': 15,
|
||||
'buttonPunch': 14,
|
||||
'buttonRight': 4,
|
||||
'buttonStart': 5,
|
||||
'buttonUp': 1,
|
||||
'triggerRun1': 5,
|
||||
'triggerRun2': 6,
|
||||
'buttonIgnored': 11,
|
||||
}.get(name, -1)
|
||||
if devicename in [
|
||||
'Logitech Dual Action',
|
||||
'Logitech Cordless RumblePad 2',
|
||||
]:
|
||||
return {
|
||||
'buttonJump': 2,
|
||||
'buttonPunch': 1,
|
||||
'buttonBomb': 3,
|
||||
'buttonPickUp': 4,
|
||||
'buttonStart': 10,
|
||||
}.get(name, -1)
|
||||
|
||||
# Old gravis gamepad.
|
||||
if devicename == 'GamePad Pro USB ':
|
||||
return {
|
||||
'buttonJump': 2,
|
||||
'buttonPunch': 1,
|
||||
'buttonBomb': 3,
|
||||
'buttonPickUp': 4,
|
||||
'buttonStart': 10,
|
||||
}.get(name, -1)
|
||||
|
||||
if devicename == 'Microsoft SideWinder Plug & Play Game Pad':
|
||||
return {
|
||||
'buttonJump': 1,
|
||||
'buttonPunch': 3,
|
||||
'buttonBomb': 2,
|
||||
'buttonPickUp': 4,
|
||||
'buttonStart': 6,
|
||||
}.get(name, -1)
|
||||
|
||||
# Saitek P2500 Rumble Force Pad.. (hopefully works for others too?..)
|
||||
if devicename == 'Saitek P2500 Rumble Force Pad':
|
||||
return {
|
||||
'buttonJump': 3,
|
||||
'buttonPunch': 1,
|
||||
'buttonBomb': 4,
|
||||
'buttonPickUp': 2,
|
||||
'buttonStart': 11,
|
||||
}.get(name, -1)
|
||||
|
||||
# Some crazy 'Senze' dual gamepad.
|
||||
if devicename == 'Twin USB Joystick':
|
||||
return {
|
||||
'analogStickLR': 3,
|
||||
'analogStickLR_B': 7,
|
||||
'analogStickUD': 4,
|
||||
'analogStickUD_B': 8,
|
||||
'buttonBomb': 2,
|
||||
'buttonBomb_B': 14,
|
||||
'buttonJump': 3,
|
||||
'buttonJump_B': 15,
|
||||
'buttonPickUp': 1,
|
||||
'buttonPickUp_B': 13,
|
||||
'buttonPunch': 4,
|
||||
'buttonPunch_B': 16,
|
||||
'buttonRun1': 7,
|
||||
'buttonRun1_B': 19,
|
||||
'buttonRun2': 8,
|
||||
'buttonRun2_B': 20,
|
||||
'buttonStart': 10,
|
||||
'buttonStart_B': 22,
|
||||
'enableSecondary': 1,
|
||||
'unassignedButtonsRun': False,
|
||||
}.get(name, -1)
|
||||
if devicename == 'USB Gamepad ': # some weird 'JITE' gamepad
|
||||
return {
|
||||
'analogStickLR': 4,
|
||||
'analogStickUD': 5,
|
||||
'buttonJump': 3,
|
||||
'buttonPunch': 4,
|
||||
'buttonBomb': 2,
|
||||
'buttonPickUp': 1,
|
||||
'buttonStart': 10,
|
||||
}.get(name, -1)
|
||||
|
||||
default_android_mapping = {
|
||||
'triggerRun2': 19,
|
||||
@ -303,6 +118,41 @@ def get_input_device_mapped_value(
|
||||
|
||||
# Generic android...
|
||||
if platform == 'android':
|
||||
if devicename in ['Amazon Fire Game Controller']:
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'unassignedButtonsRun': False,
|
||||
'buttonPickUp': 101,
|
||||
'buttonBomb': 98,
|
||||
'buttonJump': 97,
|
||||
'analogStickDeadZone': 0.0,
|
||||
'startButtonActivatesDefaultWidget': False,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 100,
|
||||
'buttonRun2': 103,
|
||||
'buttonRun1': 104,
|
||||
'triggerRun1': 24,
|
||||
}.get(name, -1)
|
||||
if devicename in [
|
||||
'Amazon Remote',
|
||||
'Amazon Bluetooth Dev',
|
||||
'Amazon Fire TV Remote',
|
||||
]:
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'triggerRun1': 24,
|
||||
'buttonPickUp': 24,
|
||||
'buttonBomb': 91,
|
||||
'buttonJump': 86,
|
||||
'buttonUp': 20,
|
||||
'buttonLeft': 22,
|
||||
'startButtonActivatesDefaultWidget': False,
|
||||
'buttonRight': 23,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 90,
|
||||
'buttonDown': 21,
|
||||
}.get(name, -1)
|
||||
|
||||
# Steelseries stratus xl.
|
||||
if devicename == 'SteelSeries Stratus XL':
|
||||
return {
|
||||
@ -380,14 +230,6 @@ def get_input_device_mapped_value(
|
||||
'uiOnly': True,
|
||||
}.get(name, -1)
|
||||
|
||||
# flag particular gamepads to use exact android defaults..
|
||||
# (so they don't even ask to configure themselves)
|
||||
if devicename in [
|
||||
'Samsung Game Pad EI-GP20',
|
||||
'ASUS Gamepad',
|
||||
] or devicename.startswith('Freefly VR Glide'):
|
||||
return default_android_mapping.get(name, -1)
|
||||
|
||||
# Nvidia controller is default, but gets some strange
|
||||
# keypresses we want to ignore.. touching the touchpad,
|
||||
# so lets ignore those.
|
||||
@ -445,76 +287,11 @@ def get_input_device_mapped_value(
|
||||
'buttonRight': 100,
|
||||
}.get(name, -1)
|
||||
|
||||
# Ok, this gamepad's not in our specific preset list;
|
||||
# fall back to some (hopefully) reasonable defaults.
|
||||
|
||||
# Leaving these in here for now but not gonna add any more now that we have
|
||||
# fancy-pants config sharing across the internet.
|
||||
if platform == 'mac':
|
||||
if 'PLAYSTATION' in devicename: # ps3 gamepad?..
|
||||
return {
|
||||
'buttonLeft': 8,
|
||||
'buttonUp': 5,
|
||||
'buttonRight': 6,
|
||||
'buttonDown': 7,
|
||||
'buttonJump': 15,
|
||||
'buttonPunch': 16,
|
||||
'buttonBomb': 14,
|
||||
'buttonPickUp': 13,
|
||||
'buttonStart': 4,
|
||||
}.get(name, -1)
|
||||
|
||||
# Dual Action Config - hopefully applies to more...
|
||||
if 'Logitech' in devicename:
|
||||
return {
|
||||
'buttonJump': 2,
|
||||
'buttonPunch': 1,
|
||||
'buttonBomb': 3,
|
||||
'buttonPickUp': 4,
|
||||
'buttonStart': 10,
|
||||
}.get(name, -1)
|
||||
|
||||
# Saitek P2500 Rumble Force Pad.. (hopefully works for others too?..)
|
||||
if 'Saitek' in devicename:
|
||||
return {
|
||||
'buttonJump': 3,
|
||||
'buttonPunch': 1,
|
||||
'buttonBomb': 4,
|
||||
'buttonPickUp': 2,
|
||||
'buttonStart': 11,
|
||||
}.get(name, -1)
|
||||
|
||||
# Gravis stuff?...
|
||||
if 'GamePad' in devicename:
|
||||
return {
|
||||
'buttonJump': 2,
|
||||
'buttonPunch': 1,
|
||||
'buttonBomb': 3,
|
||||
'buttonPickUp': 4,
|
||||
'buttonStart': 10,
|
||||
}.get(name, -1)
|
||||
# Ok, this gamepad's not in our specific preset list; fall back to
|
||||
# some (hopefully) reasonable defaults.
|
||||
|
||||
# Reasonable defaults.
|
||||
if platform == 'android':
|
||||
if babase.is_running_on_fire_tv():
|
||||
# Mostly same as default firetv controller.
|
||||
return {
|
||||
'triggerRun2': 23,
|
||||
'triggerRun1': 24,
|
||||
'buttonPickUp': 101,
|
||||
'buttonBomb': 98,
|
||||
'buttonJump': 97,
|
||||
'buttonStart': 83,
|
||||
'buttonPunch': 100,
|
||||
'buttonDown': 21,
|
||||
'buttonUp': 20,
|
||||
'buttonLeft': 22,
|
||||
'buttonRight': 23,
|
||||
'startButtonActivatesDefaultWidget': False,
|
||||
}.get(name, -1)
|
||||
|
||||
# Mostly same as 'Gamepad' except with 'menu' for default start
|
||||
# button instead of 'mode'.
|
||||
return default_android_mapping.get(name, -1)
|
||||
|
||||
# Is there a point to any sort of fallbacks here?.. should check.
|
||||
@ -533,9 +310,9 @@ def _gen_android_input_hash() -> str:
|
||||
|
||||
md5 = hashlib.md5()
|
||||
|
||||
# Currently we just do a single hash of *all* inputs on android
|
||||
# and that's it.. good enough.
|
||||
# (grabbing mappings for a specific device looks to be non-trivial)
|
||||
# Currently we just do a single hash of *all* inputs on android and
|
||||
# that's it. Good enough. (grabbing mappings for a specific device
|
||||
# looks to be non-trivial)
|
||||
for dirname in [
|
||||
'/system/usr/keylayout',
|
||||
'/data/usr/keylayout',
|
||||
@ -544,9 +321,9 @@ def _gen_android_input_hash() -> str:
|
||||
try:
|
||||
if os.path.isdir(dirname):
|
||||
for f_name in os.listdir(dirname):
|
||||
# This is usually volume keys and stuff;
|
||||
# assume we can skip it?..
|
||||
# (since it'll vary a lot across devices)
|
||||
# This is usually volume keys and stuff; assume we
|
||||
# can skip it?.. (since it'll vary a lot across
|
||||
# devices)
|
||||
if f_name == 'gpio-keys.kl':
|
||||
continue
|
||||
try:
|
||||
@ -569,8 +346,8 @@ def get_input_device_map_hash() -> str:
|
||||
"""
|
||||
app = babase.app
|
||||
|
||||
# Currently only using this when classic is present.
|
||||
# Need to replace with a modern equivalent.
|
||||
# Currently only using this when classic is present. Need to replace
|
||||
# with a modern equivalent.
|
||||
if app.classic is not None:
|
||||
try:
|
||||
if app.classic.input_map_hash is None:
|
||||
|
||||
@ -165,15 +165,16 @@ class MusicSubsystem:
|
||||
|
||||
def supports_soundtrack_entry_type(self, entry_type: str) -> bool:
|
||||
"""Return whether provided soundtrack entry type is supported here."""
|
||||
uas = babase.env()['legacy_user_agent_string']
|
||||
assert isinstance(uas, str)
|
||||
|
||||
# FIXME: Generalize this.
|
||||
# Note to self; can't access babase.app.classic here because
|
||||
# we are called during its construction.
|
||||
env = babase.env()
|
||||
platform = env.get('platform')
|
||||
assert isinstance(platform, str)
|
||||
if entry_type == 'iTunesPlaylist':
|
||||
return 'Mac' in uas
|
||||
return platform == 'mac' and babase.is_xcode_build()
|
||||
if entry_type in ('musicFile', 'musicFolder'):
|
||||
return (
|
||||
'android' in uas
|
||||
platform == 'android'
|
||||
and babase.android_get_external_files_dir() is not None
|
||||
)
|
||||
if entry_type == 'default':
|
||||
|
||||
@ -423,6 +423,10 @@ class ServerController:
|
||||
bascenev1.set_public_party_stats_url(self._config.stats_url)
|
||||
bascenev1.set_public_party_enabled(self._config.party_is_public)
|
||||
|
||||
bascenev1.set_player_rejoin_cooldown(
|
||||
self._config.player_rejoin_cooldown
|
||||
)
|
||||
|
||||
# And here.. we.. go.
|
||||
if self._config.stress_test_players is not None:
|
||||
# Special case: run a stress test.
|
||||
|
||||
@ -451,15 +451,6 @@ class ClassicSubsystem(babase.AppSubsystem):
|
||||
if playtype in val.get_play_types()
|
||||
)
|
||||
|
||||
def show_online_score_ui(
|
||||
self,
|
||||
show: str = 'general',
|
||||
game: str | None = None,
|
||||
game_version: str | None = None,
|
||||
) -> None:
|
||||
"""(internal)"""
|
||||
bauiv1.show_online_score_ui(show, game, game_version)
|
||||
|
||||
def game_begin_analytics(self) -> None:
|
||||
"""(internal)"""
|
||||
from baclassic import _analytics
|
||||
@ -627,15 +618,6 @@ class ClassicSubsystem(babase.AppSubsystem):
|
||||
"""(internal)"""
|
||||
return bascenev1.get_foreground_host_activity()
|
||||
|
||||
def show_config_error_window(self) -> bool:
|
||||
"""(internal)"""
|
||||
if self.platform in ('mac', 'linux', 'windows'):
|
||||
from bauiv1lib.configerror import ConfigErrorWindow
|
||||
|
||||
babase.pushcall(ConfigErrorWindow)
|
||||
return True
|
||||
return False
|
||||
|
||||
def value_test(
|
||||
self,
|
||||
arg: str,
|
||||
@ -809,5 +791,6 @@ class ClassicSubsystem(babase.AppSubsystem):
|
||||
bauiv1.getsound('swish').play()
|
||||
|
||||
babase.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow().get_root_widget()
|
||||
MainMenuWindow().get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
|
||||
@ -80,14 +80,13 @@ class _MacMusicAppThread(threading.Thread):
|
||||
def run(self) -> None:
|
||||
"""Run the Music.app thread."""
|
||||
babase.set_thread_name('BA_MacMusicAppThread')
|
||||
babase.mac_music_app_init()
|
||||
|
||||
# Let's mention to the user we're launching Music.app in case
|
||||
# it causes any funny business (this used to background the app
|
||||
# sometimes, though I think that is fixed now)
|
||||
def do_print() -> None:
|
||||
babase.apptimer(
|
||||
1.0,
|
||||
0.5,
|
||||
babase.Call(
|
||||
babase.screenmessage,
|
||||
babase.Lstr(resource='usingItunesText'),
|
||||
@ -97,9 +96,8 @@ class _MacMusicAppThread(threading.Thread):
|
||||
|
||||
babase.pushcall(do_print, from_other_thread=True)
|
||||
|
||||
# Here we grab this to force the actual launch.
|
||||
babase.mac_music_app_get_volume()
|
||||
babase.mac_music_app_get_library_source()
|
||||
babase.mac_music_app_init()
|
||||
|
||||
done = False
|
||||
while not done:
|
||||
self._commands_available.wait()
|
||||
|
||||
@ -40,7 +40,7 @@ if TYPE_CHECKING:
|
||||
# the last load. Either way, however, multiple execs will happen in some
|
||||
# form.
|
||||
#
|
||||
# So we need to do a few things to handle that situation gracefully.
|
||||
# To handle that situation gracefully, we need to do a few things:
|
||||
#
|
||||
# - First, we need to store any mutable global state in the __main__
|
||||
# module; not in ourself. This way, alternate versions of ourself will
|
||||
@ -48,12 +48,12 @@ if TYPE_CHECKING:
|
||||
#
|
||||
# - Second, we should avoid the use of isinstance and similar calls for
|
||||
# our types. An EnvConfig we create would technically be a different
|
||||
# type than that created by an alternate baenv.
|
||||
# type than an EnvConfig created by an alternate baenv.
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21445
|
||||
TARGET_BALLISTICA_VERSION = '1.7.28'
|
||||
TARGET_BALLISTICA_BUILD = 21743
|
||||
TARGET_BALLISTICA_VERSION = '1.7.33'
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Provides classic app subsystem."""
|
||||
"""Provides plus app subsystem."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@ -249,3 +249,41 @@ 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()
|
||||
|
||||
@staticmethod
|
||||
def show_ad(
|
||||
purpose: str, on_completion_call: Callable[[], None] | None = None
|
||||
) -> None:
|
||||
"""Show an ad."""
|
||||
_baplus.show_ad(purpose, on_completion_call)
|
||||
|
||||
@staticmethod
|
||||
def show_ad_2(
|
||||
purpose: str, on_completion_call: Callable[[bool], None] | None = None
|
||||
) -> None:
|
||||
"""Show an ad."""
|
||||
_baplus.show_ad_2(purpose, on_completion_call)
|
||||
|
||||
@staticmethod
|
||||
def show_game_service_ui(
|
||||
show: str = 'general',
|
||||
game: str | None = None,
|
||||
game_version: str | None = None,
|
||||
) -> None:
|
||||
"""Show game-service provided UI."""
|
||||
_baplus.show_game_service_ui(show, game, game_version)
|
||||
|
||||
@ -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 (
|
||||
@ -226,7 +228,7 @@ from bascenev1._settings import (
|
||||
IntSetting,
|
||||
Setting,
|
||||
)
|
||||
from bascenev1._session import Session
|
||||
from bascenev1._session import Session, set_player_rejoin_cooldown
|
||||
from bascenev1._stats import PlayerScoredMessage, PlayerRecord, Stats
|
||||
from bascenev1._team import SessionTeam, Team, EmptyTeam
|
||||
from bascenev1._teamgame import TeamGameActivity
|
||||
@ -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',
|
||||
@ -415,6 +419,7 @@ __all__ = [
|
||||
'set_public_party_name',
|
||||
'set_public_party_queue_enabled',
|
||||
'set_public_party_stats_url',
|
||||
'set_player_rejoin_cooldown',
|
||||
'set_replay_speed_exponent',
|
||||
'set_touchscreen_editing',
|
||||
'setmusic',
|
||||
|
||||
@ -6,7 +6,13 @@ from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from bacommon.app import AppExperience
|
||||
from babase import AppMode, AppIntentExec, AppIntentDefault
|
||||
from babase import (
|
||||
app,
|
||||
AppMode,
|
||||
AppIntentExec,
|
||||
AppIntentDefault,
|
||||
invoke_main_menu,
|
||||
)
|
||||
|
||||
import _bascenev1
|
||||
|
||||
@ -40,3 +46,9 @@ class SceneV1AppMode(AppMode):
|
||||
def on_deactivate(self) -> None:
|
||||
# Let the native layer do its thing.
|
||||
_bascenev1.on_app_mode_deactivate()
|
||||
|
||||
def on_app_active_changed(self) -> None:
|
||||
# If we've gone inactive, bring up the main menu, which has the
|
||||
# side effect of pausing the action (when possible).
|
||||
if not app.active:
|
||||
invoke_main_menu()
|
||||
|
||||
@ -438,10 +438,16 @@ class GameActivity(Activity[PlayerT, TeamT]):
|
||||
assert classic is not None
|
||||
continues_window = classic.continues_window
|
||||
|
||||
# Turning these off. I want to migrate towards monetization that
|
||||
# feels less pay-to-win-ish.
|
||||
allow_continues = False
|
||||
|
||||
plus = babase.app.plus
|
||||
try:
|
||||
if plus is not None and plus.get_v1_account_misc_read_val(
|
||||
'enableContinues', False
|
||||
if (
|
||||
plus is not None
|
||||
and plus.get_v1_account_misc_read_val('enableContinues', False)
|
||||
and allow_continues
|
||||
):
|
||||
session = self.session
|
||||
|
||||
|
||||
24
src/assets/ba_data/python/bascenev1/_net.py
Normal file
24
src/assets/ba_data/python/bascenev1/_net.py
Normal file
@ -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
|
||||
@ -3,6 +3,7 @@
|
||||
"""Defines base session class."""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import weakref
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
@ -17,6 +18,17 @@ if TYPE_CHECKING:
|
||||
|
||||
import bascenev1
|
||||
|
||||
# How many seconds someone who left the session (but not the party) must
|
||||
# wait to rejoin the session again. Intended to prevent game exploits
|
||||
# such as skipping respawn waits.
|
||||
_g_player_rejoin_cooldown: float = 0.0
|
||||
|
||||
|
||||
def set_player_rejoin_cooldown(cooldown: float) -> None:
|
||||
"""Set the cooldown for individual players rejoining after leaving."""
|
||||
global _g_player_rejoin_cooldown # pylint: disable=global-statement
|
||||
_g_player_rejoin_cooldown = max(0.0, cooldown)
|
||||
|
||||
|
||||
class Session:
|
||||
"""Defines a high level series of bascenev1.Activity-es.
|
||||
@ -203,6 +215,11 @@ class Session:
|
||||
# Instantiate our session globals node which will apply its settings.
|
||||
self._sessionglobalsnode = _bascenev1.newnode('sessionglobals')
|
||||
|
||||
# Rejoin cooldown stuff.
|
||||
self._players_on_wait: dict = {}
|
||||
self._player_requested_identifiers: dict = {}
|
||||
self._waitlist_timers: dict = {}
|
||||
|
||||
@property
|
||||
def context(self) -> bascenev1.ContextRef:
|
||||
"""A context-ref pointing at this activity."""
|
||||
@ -253,6 +270,33 @@ class Session:
|
||||
)
|
||||
return False
|
||||
|
||||
# Rejoin cooldown.
|
||||
identifier = player.get_v1_account_id()
|
||||
if identifier:
|
||||
leave_time = self._players_on_wait.get(identifier)
|
||||
if leave_time:
|
||||
diff = str(
|
||||
math.ceil(
|
||||
_g_player_rejoin_cooldown
|
||||
- babase.apptime()
|
||||
+ leave_time
|
||||
)
|
||||
)
|
||||
_bascenev1.broadcastmessage(
|
||||
babase.Lstr(
|
||||
translate=(
|
||||
'serverResponses',
|
||||
'You can join in ${COUNT} seconds.',
|
||||
),
|
||||
subs=[('${COUNT}', diff)],
|
||||
),
|
||||
color=(1, 1, 0),
|
||||
clients=[player.inputdevice.client_id],
|
||||
transient=True,
|
||||
)
|
||||
return False
|
||||
self._player_requested_identifiers[player.id] = identifier
|
||||
|
||||
_bascenev1.getsound('dripity').play()
|
||||
return True
|
||||
|
||||
@ -270,6 +314,16 @@ class Session:
|
||||
|
||||
activity = self._activity_weak()
|
||||
|
||||
# Rejoin cooldown.
|
||||
identifier = self._player_requested_identifiers.get(sessionplayer.id)
|
||||
if identifier:
|
||||
self._players_on_wait[identifier] = babase.apptime()
|
||||
with babase.ContextRef.empty():
|
||||
self._waitlist_timers[identifier] = babase.AppTimer(
|
||||
_g_player_rejoin_cooldown,
|
||||
babase.Call(self._remove_player_from_waitlist, identifier),
|
||||
)
|
||||
|
||||
if not sessionplayer.in_game:
|
||||
# Ok, the player is still in the lobby; simply remove them.
|
||||
with self.context:
|
||||
@ -770,3 +824,9 @@ class Session:
|
||||
if pass_to_activity:
|
||||
activity.add_player(sessionplayer)
|
||||
return sessionplayer
|
||||
|
||||
def _remove_player_from_waitlist(self, identifier: str) -> None:
|
||||
try:
|
||||
self._players_on_wait.pop(identifier)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@ -9,6 +9,7 @@ import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from bacommon.login import LoginType
|
||||
import bascenev1 as bs
|
||||
import bauiv1 as bui
|
||||
|
||||
@ -59,29 +60,25 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
)
|
||||
)
|
||||
|
||||
self._account_type = (
|
||||
plus.get_v1_account_type()
|
||||
if plus.get_v1_account_state() == 'signed_in'
|
||||
else None
|
||||
)
|
||||
|
||||
self._game_service_icon_color: Sequence[float] | None
|
||||
self._game_service_achievements_texture: bui.Texture | None
|
||||
self._game_service_leaderboards_texture: bui.Texture | None
|
||||
|
||||
if self._account_type == 'Game Center':
|
||||
# Tie in to specific game services if they are active.
|
||||
adapter = plus.accounts.login_adapters.get(LoginType.GPGS)
|
||||
gpgs_active = adapter is not None and adapter.is_back_end_active()
|
||||
adapter = plus.accounts.login_adapters.get(LoginType.GAME_CENTER)
|
||||
game_center_active = (
|
||||
adapter is not None and adapter.is_back_end_active()
|
||||
)
|
||||
|
||||
if game_center_active:
|
||||
self._game_service_icon_color = (1.0, 1.0, 1.0)
|
||||
icon = bui.gettexture('gameCenterIcon')
|
||||
self._game_service_achievements_texture = icon
|
||||
self._game_service_leaderboards_texture = icon
|
||||
self._account_has_achievements = True
|
||||
elif self._account_type == 'Game Circle':
|
||||
icon = bui.gettexture('gameCircleIcon')
|
||||
self._game_service_icon_color = (1, 1, 1)
|
||||
self._game_service_achievements_texture = icon
|
||||
self._game_service_leaderboards_texture = icon
|
||||
self._account_has_achievements = True
|
||||
elif self._account_type == 'Google Play':
|
||||
elif gpgs_active:
|
||||
self._game_service_icon_color = (0.8, 1.0, 0.6)
|
||||
self._game_service_achievements_texture = bui.gettexture(
|
||||
'googlePlayAchievementsIcon'
|
||||
@ -193,7 +190,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
super().__del__()
|
||||
|
||||
# If our UI is still up, kill it.
|
||||
if self._root_ui:
|
||||
if self._root_ui and not self._root_ui.transitioning_out:
|
||||
with bui.ContextRef.empty():
|
||||
bui.containerwidget(edit=self._root_ui, transition='out_left')
|
||||
|
||||
@ -287,20 +284,20 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
self.end({'outcome': 'next_level'})
|
||||
|
||||
def _ui_gc(self) -> None:
|
||||
if bs.app.classic is not None:
|
||||
bs.app.classic.show_online_score_ui(
|
||||
if bs.app.plus is not None:
|
||||
bs.app.plus.show_game_service_ui(
|
||||
'leaderboard',
|
||||
game=self._game_name_str,
|
||||
game_version=self._game_config_str,
|
||||
)
|
||||
else:
|
||||
logging.warning('show_online_score_ui requires classic')
|
||||
logging.warning('show_game_service_ui requires plus feature-set')
|
||||
|
||||
def _ui_show_achievements(self) -> None:
|
||||
if bs.app.classic is not None:
|
||||
bs.app.classic.show_online_score_ui('achievements')
|
||||
if bs.app.plus is not None:
|
||||
bs.app.plus.show_game_service_ui('achievements')
|
||||
else:
|
||||
logging.warning('show_online_score_ui requires classic')
|
||||
logging.warning('show_game_service_ui requires plus feature-set')
|
||||
|
||||
def _ui_worlds_best(self) -> None:
|
||||
if self._score_link is None:
|
||||
|
||||
@ -35,7 +35,7 @@ class ControlsGuide(bs.Actor):
|
||||
delay: is the time in seconds before the overlay fades in.
|
||||
|
||||
lifespan: if not None, the overlay will fade back out and die after
|
||||
that long (in milliseconds).
|
||||
that long (in seconds).
|
||||
|
||||
bright: if True, brighter colors will be used; handy when showing
|
||||
over gameplay but may be too bright for join-screens, etc.
|
||||
@ -50,6 +50,7 @@ class ControlsGuide(bs.Actor):
|
||||
offs5 = 43.0 * scale
|
||||
ouya = False
|
||||
maxw = 50
|
||||
xtweak = -2.8 * scale
|
||||
self._lifespan = lifespan
|
||||
self._dead = False
|
||||
self._bright = bright
|
||||
@ -117,7 +118,7 @@ class ControlsGuide(bs.Actor):
|
||||
'host_only': True,
|
||||
'shadow': 1.0,
|
||||
'maxwidth': maxw,
|
||||
'position': (pos[0], pos[1] - offs5),
|
||||
'position': (pos[0] + xtweak, pos[1] - offs5),
|
||||
'color': clr,
|
||||
},
|
||||
)
|
||||
@ -145,7 +146,7 @@ class ControlsGuide(bs.Actor):
|
||||
'host_only': True,
|
||||
'shadow': 1.0,
|
||||
'maxwidth': maxw,
|
||||
'position': (pos[0], pos[1] - offs5),
|
||||
'position': (pos[0] + xtweak, pos[1] - offs5),
|
||||
'color': clr,
|
||||
},
|
||||
)
|
||||
@ -173,7 +174,7 @@ class ControlsGuide(bs.Actor):
|
||||
'host_only': True,
|
||||
'shadow': 1.0,
|
||||
'maxwidth': maxw,
|
||||
'position': (pos[0], pos[1] - offs5),
|
||||
'position': (pos[0] + xtweak, pos[1] - offs5),
|
||||
'color': clr,
|
||||
},
|
||||
)
|
||||
@ -201,7 +202,7 @@ class ControlsGuide(bs.Actor):
|
||||
'host_only': True,
|
||||
'shadow': 1.0,
|
||||
'maxwidth': maxw,
|
||||
'position': (pos[0], pos[1] - offs5),
|
||||
'position': (pos[0] + xtweak, pos[1] - offs5),
|
||||
'color': clr,
|
||||
},
|
||||
)
|
||||
@ -264,10 +265,19 @@ class ControlsGuide(bs.Actor):
|
||||
bs.timer(delay, bs.WeakCall(self._start_updating))
|
||||
|
||||
@staticmethod
|
||||
def _meaningful_button_name(device: bs.InputDevice, button: int) -> str:
|
||||
def _meaningful_button_name(
|
||||
device: bs.InputDevice, button_name: str
|
||||
) -> str:
|
||||
"""Return a flattened string button name; empty for non-meaningful."""
|
||||
if not device.has_meaningful_button_names:
|
||||
return ''
|
||||
assert bs.app.classic is not None
|
||||
button = bs.app.classic.get_input_device_mapped_value(
|
||||
device, button_name
|
||||
)
|
||||
# -1 means unset; let's show that.
|
||||
if button == -1:
|
||||
return bs.Lstr(resource='configGamepadWindow.unsetText').evaluate()
|
||||
return device.get_button_name(button).evaluate()
|
||||
|
||||
def _start_updating(self) -> None:
|
||||
@ -289,10 +299,10 @@ class ControlsGuide(bs.Actor):
|
||||
def _check_fade_in(self) -> None:
|
||||
assert bs.app.classic is not None
|
||||
|
||||
# If we have a touchscreen, we only fade in if we have a player with
|
||||
# an input device that is *not* the touchscreen.
|
||||
# (otherwise it is confusing to see the touchscreen buttons right
|
||||
# next to our display buttons)
|
||||
# If we have a touchscreen, we only fade in if we have a player
|
||||
# with an input device that is *not* the touchscreen. Otherwise
|
||||
# it is confusing to see the touchscreen buttons right next to
|
||||
# our display buttons.
|
||||
touchscreen: bs.InputDevice | None = bs.getinputdevice(
|
||||
'TouchScreen', '#1', doraise=False
|
||||
)
|
||||
@ -318,15 +328,7 @@ class ControlsGuide(bs.Actor):
|
||||
'buttonBomb',
|
||||
'buttonPickUp',
|
||||
):
|
||||
if (
|
||||
self._meaningful_button_name(
|
||||
device,
|
||||
bs.app.classic.get_input_device_mapped_value(
|
||||
device, name
|
||||
),
|
||||
)
|
||||
!= ''
|
||||
):
|
||||
if self._meaningful_button_name(device, name) != '':
|
||||
fade_in = True
|
||||
break
|
||||
if fade_in:
|
||||
@ -401,58 +403,30 @@ class ControlsGuide(bs.Actor):
|
||||
# We only care about movement buttons in the case of keyboards.
|
||||
if all_keyboards:
|
||||
right_button_names.add(
|
||||
device.get_button_name(
|
||||
classic.get_input_device_mapped_value(
|
||||
device, 'buttonRight'
|
||||
)
|
||||
)
|
||||
self._meaningful_button_name(device, 'buttonRight')
|
||||
)
|
||||
left_button_names.add(
|
||||
device.get_button_name(
|
||||
classic.get_input_device_mapped_value(
|
||||
device, 'buttonLeft'
|
||||
)
|
||||
)
|
||||
self._meaningful_button_name(device, 'buttonLeft')
|
||||
)
|
||||
down_button_names.add(
|
||||
device.get_button_name(
|
||||
classic.get_input_device_mapped_value(
|
||||
device, 'buttonDown'
|
||||
)
|
||||
)
|
||||
self._meaningful_button_name(device, 'buttonDown')
|
||||
)
|
||||
up_button_names.add(
|
||||
device.get_button_name(
|
||||
classic.get_input_device_mapped_value(
|
||||
device, 'buttonUp'
|
||||
)
|
||||
)
|
||||
self._meaningful_button_name(device, 'buttonUp')
|
||||
)
|
||||
|
||||
# Ignore empty values; things like the remote app or
|
||||
# wiimotes can return these.
|
||||
bname = self._meaningful_button_name(
|
||||
device,
|
||||
classic.get_input_device_mapped_value(device, 'buttonPunch'),
|
||||
)
|
||||
bname = self._meaningful_button_name(device, 'buttonPunch')
|
||||
if bname != '':
|
||||
punch_button_names.add(bname)
|
||||
bname = self._meaningful_button_name(
|
||||
device,
|
||||
classic.get_input_device_mapped_value(device, 'buttonJump'),
|
||||
)
|
||||
bname = self._meaningful_button_name(device, 'buttonJump')
|
||||
if bname != '':
|
||||
jump_button_names.add(bname)
|
||||
bname = self._meaningful_button_name(
|
||||
device,
|
||||
classic.get_input_device_mapped_value(device, 'buttonBomb'),
|
||||
)
|
||||
bname = self._meaningful_button_name(device, 'buttonBomb')
|
||||
if bname != '':
|
||||
bomb_button_names.add(bname)
|
||||
bname = self._meaningful_button_name(
|
||||
device,
|
||||
classic.get_input_device_mapped_value(device, 'buttonPickUp'),
|
||||
)
|
||||
bname = self._meaningful_button_name(device, 'buttonPickUp')
|
||||
if bname != '':
|
||||
pickup_button_names.add(bname)
|
||||
|
||||
@ -582,8 +556,8 @@ class ControlsGuide(bs.Actor):
|
||||
if msg.immediate:
|
||||
self._die()
|
||||
else:
|
||||
# If they don't need immediate,
|
||||
# fade out our nodes and die later.
|
||||
# If they don't need immediate, fade out our nodes and
|
||||
# die later.
|
||||
for node in self._nodes:
|
||||
bs.animate(node, 'opacity', {0: node.opacity, 3.0: 0.0})
|
||||
bs.timer(3.1, bs.WeakCall(self._die))
|
||||
|
||||
@ -624,7 +624,7 @@ class Spaz(bs.Actor):
|
||||
1000.0 * (tval + self.curse_time)
|
||||
)
|
||||
self._curse_timer = bs.Timer(
|
||||
5.0, bs.WeakCall(self.curse_explode)
|
||||
5.0, bs.WeakCall(self.handlemessage, CurseExplodeMessage())
|
||||
)
|
||||
|
||||
def equip_boxing_gloves(self) -> None:
|
||||
|
||||
@ -317,7 +317,8 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
from bauiv1lib.kiosk import KioskWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
KioskWindow().get_root_widget()
|
||||
KioskWindow().get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
# ..or in normal cases go back to the main menu
|
||||
else:
|
||||
@ -326,14 +327,16 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
from bauiv1lib.gather import GatherWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(transition=None).get_root_widget()
|
||||
GatherWindow(transition=None).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Watch':
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.watch import WatchWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
WatchWindow(transition=None).get_root_widget()
|
||||
WatchWindow(transition=None).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Team Game Select':
|
||||
# pylint: disable=cyclic-import
|
||||
@ -344,7 +347,8 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
PlaylistBrowserWindow(
|
||||
sessiontype=bs.DualTeamSession, transition=None
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Free-for-All Game Select':
|
||||
# pylint: disable=cyclic-import
|
||||
@ -356,28 +360,34 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
PlaylistBrowserWindow(
|
||||
sessiontype=bs.FreeForAllSession,
|
||||
transition=None,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Coop Select':
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
CoopBrowserWindow(transition=None).get_root_widget()
|
||||
CoopBrowserWindow(
|
||||
transition=None
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
elif main_menu_location == 'Benchmarks & Stress Tests':
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.debug import DebugWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
DebugWindow(transition=None).get_root_widget()
|
||||
DebugWindow(transition=None).get_root_widget(),
|
||||
from_window=False, # Disable check here.
|
||||
)
|
||||
else:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
bs.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition=None).get_root_widget()
|
||||
MainMenuWindow(transition=None).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
# attempt to show any pending offers immediately.
|
||||
|
||||
@ -31,7 +31,10 @@ from babase import (
|
||||
apptimer,
|
||||
AppTimer,
|
||||
Call,
|
||||
can_toggle_fullscreen,
|
||||
fullscreen_control_available,
|
||||
fullscreen_control_get,
|
||||
fullscreen_control_key_shortcut,
|
||||
fullscreen_control_set,
|
||||
charstr,
|
||||
clipboard_is_supported,
|
||||
clipboard_set_text,
|
||||
@ -57,13 +60,15 @@ from babase import (
|
||||
in_logic_thread,
|
||||
increment_analytics_count,
|
||||
is_browser_likely_available,
|
||||
is_running_on_fire_tv,
|
||||
is_xcode_build,
|
||||
Keyboard,
|
||||
lock_all_input,
|
||||
LoginAdapter,
|
||||
LoginInfo,
|
||||
Lstr,
|
||||
native_review_request,
|
||||
native_review_request_supported,
|
||||
NotFoundError,
|
||||
open_file_externally,
|
||||
Permission,
|
||||
Plugin,
|
||||
PluginSpec,
|
||||
@ -88,7 +93,6 @@ from babase import (
|
||||
|
||||
from _bauiv1 import (
|
||||
buttonwidget,
|
||||
can_show_ad,
|
||||
checkboxwidget,
|
||||
columnwidget,
|
||||
containerwidget,
|
||||
@ -97,21 +101,15 @@ from _bauiv1 import (
|
||||
getmesh,
|
||||
getsound,
|
||||
gettexture,
|
||||
has_video_ads,
|
||||
have_incentivized_ad,
|
||||
hscrollwidget,
|
||||
imagewidget,
|
||||
is_party_icon_visible,
|
||||
Mesh,
|
||||
open_file_externally,
|
||||
open_url,
|
||||
rowwidget,
|
||||
scrollwidget,
|
||||
set_party_icon_always_visible,
|
||||
set_party_window_open,
|
||||
show_ad,
|
||||
show_ad_2,
|
||||
show_online_score_ui,
|
||||
Sound,
|
||||
Texture,
|
||||
textwidget,
|
||||
@ -119,6 +117,7 @@ from _bauiv1 import (
|
||||
Widget,
|
||||
widget,
|
||||
)
|
||||
from bauiv1._keyboard import Keyboard
|
||||
from bauiv1._uitypes import Window, uicleanupcheck
|
||||
from bauiv1._subsystem import UIV1Subsystem
|
||||
|
||||
@ -138,8 +137,10 @@ __all__ = [
|
||||
'AppTimer',
|
||||
'buttonwidget',
|
||||
'Call',
|
||||
'can_show_ad',
|
||||
'can_toggle_fullscreen',
|
||||
'fullscreen_control_available',
|
||||
'fullscreen_control_get',
|
||||
'fullscreen_control_key_shortcut',
|
||||
'fullscreen_control_set',
|
||||
'charstr',
|
||||
'checkboxwidget',
|
||||
'clipboard_is_supported',
|
||||
@ -169,8 +170,6 @@ __all__ = [
|
||||
'getmesh',
|
||||
'getsound',
|
||||
'gettexture',
|
||||
'has_video_ads',
|
||||
'have_incentivized_ad',
|
||||
'have_permission',
|
||||
'hscrollwidget',
|
||||
'imagewidget',
|
||||
@ -178,13 +177,15 @@ __all__ = [
|
||||
'increment_analytics_count',
|
||||
'is_browser_likely_available',
|
||||
'is_party_icon_visible',
|
||||
'is_running_on_fire_tv',
|
||||
'is_xcode_build',
|
||||
'Keyboard',
|
||||
'lock_all_input',
|
||||
'LoginAdapter',
|
||||
'LoginInfo',
|
||||
'Lstr',
|
||||
'Mesh',
|
||||
'native_review_request',
|
||||
'native_review_request_supported',
|
||||
'NotFoundError',
|
||||
'open_file_externally',
|
||||
'open_url',
|
||||
@ -204,9 +205,6 @@ __all__ = [
|
||||
'set_party_icon_always_visible',
|
||||
'set_party_window_open',
|
||||
'set_ui_input_device',
|
||||
'show_ad',
|
||||
'show_ad_2',
|
||||
'show_online_score_ui',
|
||||
'Sound',
|
||||
'SpecialChar',
|
||||
'supports_max_fps',
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _bauiv1
|
||||
@ -87,3 +88,19 @@ def show_url_window(address: str) -> None:
|
||||
return
|
||||
|
||||
app.classic.show_url_window(address)
|
||||
|
||||
|
||||
def double_transition_out_warning() -> None:
|
||||
"""Called if a widget is set to transition out twice."""
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_filename = caller_frame.filename
|
||||
caller_line_number = caller_frame.lineno
|
||||
logging.warning(
|
||||
'ContainerWidget was set to transition out twice;'
|
||||
' this often implies buggy code (%s line %s).\n'
|
||||
' Generally you should check the value of'
|
||||
' _root_widget.transitioning_out and perform no actions if that'
|
||||
' is True.',
|
||||
caller_filename,
|
||||
caller_line_number,
|
||||
)
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import babase
|
||||
@ -66,6 +67,16 @@ class UIV1Subsystem(babase.AppSubsystem):
|
||||
# a more elegant way once we revamp high level UI stuff a bit.
|
||||
self.selecting_private_party_playlist: bool = False
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Can uiv1 currently be used?
|
||||
|
||||
Code that may run in headless mode, before the UI has been spun up,
|
||||
while other ui systems are active, etc. can check this to avoid
|
||||
likely erroring.
|
||||
"""
|
||||
return _bauiv1.is_available()
|
||||
|
||||
@property
|
||||
def uiscale(self) -> babase.UIScale:
|
||||
"""Current ui scale for the app."""
|
||||
@ -106,21 +117,69 @@ class UIV1Subsystem(babase.AppSubsystem):
|
||||
# FIXME: Can probably kill this if we do immediate UI death checks.
|
||||
self.upkeeptimer = babase.AppTimer(2.6543, ui_upkeep, repeat=True)
|
||||
|
||||
def set_main_menu_window(self, window: bauiv1.Widget) -> None:
|
||||
"""Set the current 'main' window, replacing any existing."""
|
||||
def set_main_menu_window(
|
||||
self,
|
||||
window: bauiv1.Widget,
|
||||
from_window: bauiv1.Widget | None | bool = True,
|
||||
) -> None:
|
||||
"""Set the current 'main' window, replacing any existing.
|
||||
|
||||
If 'from_window' is passed as a bauiv1.Widget or None, a warning
|
||||
will be issued if it that value does not match the current main
|
||||
window. This can help clean up flawed code that can lead to bad
|
||||
UI states. A value of False will disable the check.
|
||||
"""
|
||||
|
||||
existing = self._main_menu_window
|
||||
from inspect import currentframe, getframeinfo
|
||||
|
||||
try:
|
||||
if isinstance(from_window, bool):
|
||||
# For default val True we warn that the arg wasn't
|
||||
# passed. False can be explicitly passed to disable this
|
||||
# check.
|
||||
if from_window is True:
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_filename = caller_frame.filename
|
||||
caller_line_number = caller_frame.lineno
|
||||
logging.warning(
|
||||
'set_main_menu_window() should be passed a'
|
||||
" 'from_window' value to help ensure proper UI behavior"
|
||||
' (%s line %i).',
|
||||
caller_filename,
|
||||
caller_line_number,
|
||||
)
|
||||
else:
|
||||
# For everything else, warn if what they passed wasn't
|
||||
# the previous main menu widget.
|
||||
if from_window is not existing:
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_filename = caller_frame.filename
|
||||
caller_line_number = caller_frame.lineno
|
||||
logging.warning(
|
||||
"set_main_menu_window() was passed 'from_window' %s"
|
||||
' but existing main-menu-window is %s. (%s line %i).',
|
||||
from_window,
|
||||
existing,
|
||||
caller_filename,
|
||||
caller_line_number,
|
||||
)
|
||||
except Exception:
|
||||
# Prevent any bugs in these checks from causing problems.
|
||||
logging.exception('Error checking from_window')
|
||||
|
||||
# Once the above code leads to us fixing all leftover window bugs
|
||||
# at the source, we can kill the code below.
|
||||
|
||||
# Let's grab the location where we were called from to report
|
||||
# if we have to force-kill the existing window (which normally
|
||||
# should not happen).
|
||||
frameline = None
|
||||
try:
|
||||
frame = currentframe()
|
||||
frame = inspect.currentframe()
|
||||
if frame is not None:
|
||||
frame = frame.f_back
|
||||
if frame is not None:
|
||||
frameinfo = getframeinfo(frame)
|
||||
frameinfo = inspect.getframeinfo(frame)
|
||||
frameline = f'{frameinfo.filename} {frameinfo.lineno}'
|
||||
except Exception:
|
||||
logging.exception('Error calcing line for set_main_menu_window')
|
||||
@ -150,13 +209,18 @@ class UIV1Subsystem(babase.AppSubsystem):
|
||||
|
||||
def clear_main_menu_window(self, transition: str | None = None) -> None:
|
||||
"""Clear any existing 'main' window with the provided transition."""
|
||||
assert transition is None or not transition.endswith('_in')
|
||||
if self._main_menu_window:
|
||||
if transition is not None:
|
||||
if (
|
||||
transition is not None
|
||||
and not self._main_menu_window.transitioning_out
|
||||
):
|
||||
_bauiv1.containerwidget(
|
||||
edit=self._main_menu_window, transition=transition
|
||||
)
|
||||
else:
|
||||
self._main_menu_window.delete()
|
||||
self._main_menu_window = None
|
||||
|
||||
def add_main_menu_close_callback(self, call: Callable[[], Any]) -> None:
|
||||
"""(internal)"""
|
||||
|
||||
@ -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:
|
||||
@ -51,6 +52,19 @@ class OnScreenKeyboardWindow(Window):
|
||||
else (0, 0),
|
||||
)
|
||||
)
|
||||
self._cancel_button = _bauiv1.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
scale=0.5,
|
||||
position=(30, self._height - 55),
|
||||
size=(60, 60),
|
||||
label='',
|
||||
enable_sound=False,
|
||||
on_activate_call=self._cancel,
|
||||
autoselect=True,
|
||||
color=(0.55, 0.5, 0.6),
|
||||
icon=_bauiv1.gettexture('crossOut'),
|
||||
iconscale=1.2,
|
||||
)
|
||||
self._done_button = _bauiv1.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width - 200, 44),
|
||||
@ -240,9 +254,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 []
|
||||
)
|
||||
@ -274,10 +286,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:
|
||||
@ -372,9 +384,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()
|
||||
|
||||
@ -63,20 +63,14 @@ class AccountSettingsWindow(bui.Window):
|
||||
1.0, bui.WeakCall(self._update), repeat=True
|
||||
)
|
||||
|
||||
# Currently we can only reset achievements on game-center.
|
||||
v1_account_type: str | None
|
||||
if self._v1_signed_in:
|
||||
v1_account_type = plus.get_v1_account_type()
|
||||
else:
|
||||
v1_account_type = None
|
||||
self._can_reset_achievements = v1_account_type == 'Game Center'
|
||||
self._can_reset_achievements = False
|
||||
|
||||
app = bui.app
|
||||
assert app.classic is not None
|
||||
uiscale = app.ui_v1.uiscale
|
||||
|
||||
self._width = 760 if uiscale is bui.UIScale.SMALL else 660
|
||||
x_offs = 50 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._width = 860 if uiscale is bui.UIScale.SMALL else 660
|
||||
x_offs = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._height = (
|
||||
390
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -98,6 +92,9 @@ class AccountSettingsWindow(bui.Window):
|
||||
if LoginType.GPGS in plus.accounts.login_adapters:
|
||||
self._show_sign_in_buttons.append('Google Play')
|
||||
|
||||
if LoginType.GAME_CENTER in plus.accounts.login_adapters:
|
||||
self._show_sign_in_buttons.append('Game Center')
|
||||
|
||||
# Always want to show our web-based v2 login option.
|
||||
self._show_sign_in_buttons.append('V2Proxy')
|
||||
|
||||
@ -227,6 +224,8 @@ class AccountSettingsWindow(bui.Window):
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
via_lines: list[str] = []
|
||||
|
||||
primary_v2_account = plus.accounts.primary
|
||||
|
||||
v1_state = plus.get_v1_account_state()
|
||||
@ -237,14 +236,55 @@ class AccountSettingsWindow(bui.Window):
|
||||
# We expose GPGS-specific functionality only if it is 'active'
|
||||
# (meaning the current GPGS player matches one of our account's
|
||||
# logins).
|
||||
gpgs_adapter = plus.accounts.login_adapters.get(LoginType.GPGS)
|
||||
is_gpgs = (
|
||||
False if gpgs_adapter is None else gpgs_adapter.is_back_end_active()
|
||||
adapter = plus.accounts.login_adapters.get(LoginType.GPGS)
|
||||
gpgs_active = adapter is not None and adapter.is_back_end_active()
|
||||
|
||||
# Ditto for Game Center.
|
||||
adapter = plus.accounts.login_adapters.get(LoginType.GAME_CENTER)
|
||||
game_center_active = (
|
||||
adapter is not None and adapter.is_back_end_active()
|
||||
)
|
||||
|
||||
show_signed_in_as = self._v1_signed_in
|
||||
signed_in_as_space = 95.0
|
||||
|
||||
# To reduce confusion about the whole V2 account situation for
|
||||
# people used to seeing their Google Play Games or Game Center
|
||||
# account name and icon and whatnot, let's show those underneath
|
||||
# the V2 tag to help communicate that they are in fact logged in
|
||||
# through that account.
|
||||
via_space = 25.0
|
||||
if show_signed_in_as and bui.app.plus is not None:
|
||||
accounts = bui.app.plus.accounts
|
||||
if accounts.primary is not None:
|
||||
# For these login types, we show 'via' IF there is a
|
||||
# login of that type attached to our account AND it is
|
||||
# currently active (We don't want to show 'via Game
|
||||
# Center' if we're signed out of Game Center or
|
||||
# currently running on Steam, even if there is a Game
|
||||
# Center login attached to our account).
|
||||
for ltype, lchar in [
|
||||
(LoginType.GPGS, bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO),
|
||||
(LoginType.GAME_CENTER, bui.SpecialChar.GAME_CENTER_LOGO),
|
||||
]:
|
||||
linfo = accounts.primary.logins.get(ltype)
|
||||
ladapter = accounts.login_adapters.get(ltype)
|
||||
if (
|
||||
linfo is not None
|
||||
and ladapter is not None
|
||||
and ladapter.is_back_end_active()
|
||||
):
|
||||
via_lines.append(f'{bui.charstr(lchar)}{linfo.name}')
|
||||
|
||||
# TEMP TESTING
|
||||
if bool(False):
|
||||
icontxt = bui.charstr(bui.SpecialChar.GAME_CENTER_LOGO)
|
||||
via_lines.append(f'{icontxt}FloofDibble')
|
||||
icontxt = bui.charstr(
|
||||
bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO
|
||||
)
|
||||
via_lines.append(f'{icontxt}StinkBobble')
|
||||
|
||||
show_sign_in_benefits = not self._v1_signed_in
|
||||
sign_in_benefits_space = 80.0
|
||||
|
||||
@ -258,6 +298,11 @@ class AccountSettingsWindow(bui.Window):
|
||||
and self._signing_in_adapter is None
|
||||
and 'Google Play' in self._show_sign_in_buttons
|
||||
)
|
||||
show_game_center_sign_in_button = (
|
||||
v1_state == 'signed_out'
|
||||
and self._signing_in_adapter is None
|
||||
and 'Game Center' in self._show_sign_in_buttons
|
||||
)
|
||||
show_v2_proxy_sign_in_button = (
|
||||
v1_state == 'signed_out'
|
||||
and self._signing_in_adapter is None
|
||||
@ -271,9 +316,8 @@ class AccountSettingsWindow(bui.Window):
|
||||
sign_in_button_space = 70.0
|
||||
deprecated_space = 60
|
||||
|
||||
show_game_service_button = self._v1_signed_in and v1_account_type in [
|
||||
'Game Center'
|
||||
]
|
||||
# Game Center currently has a single UI for everything.
|
||||
show_game_service_button = game_center_active
|
||||
game_service_button_space = 60.0
|
||||
|
||||
show_what_is_v2 = self._v1_signed_in and v1_account_type == 'V2'
|
||||
@ -281,11 +325,9 @@ class AccountSettingsWindow(bui.Window):
|
||||
show_linked_accounts_text = self._v1_signed_in
|
||||
linked_accounts_text_space = 60.0
|
||||
|
||||
show_achievements_button = self._v1_signed_in and v1_account_type in (
|
||||
'Google Play',
|
||||
'Local',
|
||||
'V2',
|
||||
)
|
||||
# Always show achievements except in the game-center case where
|
||||
# its unified UI covers them.
|
||||
show_achievements_button = self._v1_signed_in and not game_center_active
|
||||
achievements_button_space = 60.0
|
||||
|
||||
show_achievements_text = (
|
||||
@ -293,7 +335,7 @@ class AccountSettingsWindow(bui.Window):
|
||||
)
|
||||
achievements_text_space = 27.0
|
||||
|
||||
show_leaderboards_button = self._v1_signed_in and is_gpgs
|
||||
show_leaderboards_button = self._v1_signed_in and gpgs_active
|
||||
leaderboards_button_space = 60.0
|
||||
|
||||
show_campaign_progress = self._v1_signed_in
|
||||
@ -330,7 +372,6 @@ class AccountSettingsWindow(bui.Window):
|
||||
|
||||
show_sign_out_button = self._v1_signed_in and v1_account_type in [
|
||||
'Local',
|
||||
'Google Play',
|
||||
'V2',
|
||||
]
|
||||
sign_out_button_space = 70.0
|
||||
@ -349,10 +390,13 @@ class AccountSettingsWindow(bui.Window):
|
||||
self._sub_height = 60.0
|
||||
if show_signed_in_as:
|
||||
self._sub_height += signed_in_as_space
|
||||
self._sub_height += via_space * len(via_lines)
|
||||
if show_signing_in_text:
|
||||
self._sub_height += signing_in_text_space
|
||||
if show_google_play_sign_in_button:
|
||||
self._sub_height += sign_in_button_space
|
||||
if show_game_center_sign_in_button:
|
||||
self._sub_height += sign_in_button_space
|
||||
if show_v2_proxy_sign_in_button:
|
||||
self._sub_height += sign_in_button_space
|
||||
if show_device_sign_in_button:
|
||||
@ -442,20 +486,21 @@ class AccountSettingsWindow(bui.Window):
|
||||
self._account_name_what_is_text = bui.textwidget(
|
||||
parent=self._subcontainer,
|
||||
position=(0.0, self._account_name_what_is_y),
|
||||
size=(200.0, 60),
|
||||
size=(220.0, 60),
|
||||
text=bui.Lstr(
|
||||
value='${WHAT} -->',
|
||||
subs=[('${WHAT}', bui.Lstr(resource='whatIsThisText'))],
|
||||
),
|
||||
scale=0.6,
|
||||
color=(0.3, 0.7, 0.05),
|
||||
maxwidth=200.0,
|
||||
maxwidth=130.0,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
autoselect=True,
|
||||
selectable=True,
|
||||
on_activate_call=show_what_is_v2_page,
|
||||
click_activate=True,
|
||||
glow_type='uniform',
|
||||
)
|
||||
if first_selectable is None:
|
||||
first_selectable = self._account_name_what_is_text
|
||||
@ -466,6 +511,54 @@ class AccountSettingsWindow(bui.Window):
|
||||
|
||||
v -= signed_in_as_space * 0.4
|
||||
|
||||
for via in via_lines:
|
||||
v -= via_space * 0.1
|
||||
sscale = 0.7
|
||||
swidth = (
|
||||
bui.get_string_width(via, suppress_warning=True) * sscale
|
||||
)
|
||||
bui.textwidget(
|
||||
parent=self._subcontainer,
|
||||
position=(self._sub_width * 0.5, v),
|
||||
size=(0, 0),
|
||||
text=via,
|
||||
scale=sscale,
|
||||
color=(0.6, 0.6, 0.6),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
)
|
||||
bui.textwidget(
|
||||
parent=self._subcontainer,
|
||||
position=(self._sub_width * 0.5 - swidth * 0.5 - 5, v),
|
||||
size=(0, 0),
|
||||
text=bui.Lstr(
|
||||
value='(${VIA}',
|
||||
subs=[('${VIA}', bui.Lstr(resource='viaText'))],
|
||||
),
|
||||
scale=0.5,
|
||||
color=(0.4, 0.6, 0.4, 0.5),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
)
|
||||
bui.textwidget(
|
||||
parent=self._subcontainer,
|
||||
position=(self._sub_width * 0.5 + swidth * 0.5 + 10, v),
|
||||
size=(0, 0),
|
||||
text=')',
|
||||
scale=0.5,
|
||||
color=(0.4, 0.6, 0.4, 0.5),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
)
|
||||
|
||||
v -= via_space * 0.9
|
||||
|
||||
else:
|
||||
self._account_name_text = None
|
||||
self._account_name_what_is_text = None
|
||||
@ -477,22 +570,6 @@ class AccountSettingsWindow(bui.Window):
|
||||
|
||||
if show_sign_in_benefits:
|
||||
v -= sign_in_benefits_space
|
||||
app = bui.app
|
||||
assert app.classic is not None
|
||||
extra: str | bui.Lstr | None
|
||||
if (
|
||||
app.classic.platform in ['mac', 'ios']
|
||||
and app.classic.subplatform == 'appstore'
|
||||
):
|
||||
extra = bui.Lstr(
|
||||
value='\n${S}',
|
||||
subs=[
|
||||
('${S}', bui.Lstr(resource='signInWithGameCenterText'))
|
||||
],
|
||||
)
|
||||
else:
|
||||
extra = ''
|
||||
|
||||
bui.textwidget(
|
||||
parent=self._subcontainer,
|
||||
position=(
|
||||
@ -500,16 +577,7 @@ class AccountSettingsWindow(bui.Window):
|
||||
v + sign_in_benefits_space * 0.4,
|
||||
),
|
||||
size=(0, 0),
|
||||
text=bui.Lstr(
|
||||
value='${A}${B}',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
bui.Lstr(resource=self._r + '.signInInfoText'),
|
||||
),
|
||||
('${B}', extra),
|
||||
],
|
||||
),
|
||||
text=bui.Lstr(resource=self._r + '.signInInfoText'),
|
||||
max_height=sign_in_benefits_space * 0.9,
|
||||
scale=0.9,
|
||||
color=(0.75, 0.7, 0.8),
|
||||
@ -554,7 +622,13 @@ class AccountSettingsWindow(bui.Window):
|
||||
(
|
||||
'${B}',
|
||||
bui.Lstr(
|
||||
resource=self._r + '.signInWithGooglePlayText'
|
||||
resource=self._r + '.signInWithText',
|
||||
subs=[
|
||||
(
|
||||
'${SERVICE}',
|
||||
bui.Lstr(resource='googlePlayText'),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -572,6 +646,48 @@ class AccountSettingsWindow(bui.Window):
|
||||
bui.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
|
||||
self._sign_in_text = None
|
||||
|
||||
if show_game_center_sign_in_button:
|
||||
button_width = 350
|
||||
v -= sign_in_button_space
|
||||
self._sign_in_google_play_button = btn = bui.buttonwidget(
|
||||
parent=self._subcontainer,
|
||||
position=((self._sub_width - button_width) * 0.5, v - 20),
|
||||
autoselect=True,
|
||||
size=(button_width, 60),
|
||||
# Note: Apparently Game Center is just called 'Game Center'
|
||||
# in all languages. Can revisit if not true.
|
||||
# https://developer.apple.com/forums/thread/725779
|
||||
label=bui.Lstr(
|
||||
value='${A}${B}',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
bui.charstr(bui.SpecialChar.GAME_CENTER_LOGO),
|
||||
),
|
||||
(
|
||||
'${B}',
|
||||
bui.Lstr(
|
||||
resource=self._r + '.signInWithText',
|
||||
subs=[('${SERVICE}', 'Game Center')],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
on_activate_call=lambda: self._sign_in_press(
|
||||
LoginType.GAME_CENTER
|
||||
),
|
||||
)
|
||||
if first_selectable is None:
|
||||
first_selectable = btn
|
||||
if bui.app.ui_v1.use_toolbars:
|
||||
bui.widget(
|
||||
edit=btn,
|
||||
right_widget=bui.get_special_widget('party_button'),
|
||||
)
|
||||
bui.widget(edit=btn, left_widget=bbtn)
|
||||
bui.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
|
||||
self._sign_in_text = None
|
||||
|
||||
if show_v2_proxy_sign_in_button:
|
||||
button_width = 350
|
||||
v -= sign_in_button_space
|
||||
@ -704,7 +820,7 @@ class AccountSettingsWindow(bui.Window):
|
||||
position=((self._sub_width - button_width) * 0.5, v + 30),
|
||||
autoselect=True,
|
||||
size=(button_width, 60),
|
||||
label=bui.Lstr(resource=self._r + '.manageAccountText'),
|
||||
label=bui.Lstr(resource=f'{self._r}.manageAccountText'),
|
||||
color=(0.55, 0.5, 0.6),
|
||||
icon=bui.gettexture('settingsIcon'),
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
@ -745,10 +861,15 @@ class AccountSettingsWindow(bui.Window):
|
||||
# the button to go to OS-Specific leaderboards/high-score-lists/etc.
|
||||
if show_game_service_button:
|
||||
button_width = 300
|
||||
v -= game_service_button_space * 0.85
|
||||
v1_account_type = plus.get_v1_account_type()
|
||||
if v1_account_type == 'Game Center':
|
||||
v1_account_type_name = bui.Lstr(resource='gameCenterText')
|
||||
v -= game_service_button_space * 0.6
|
||||
if game_center_active:
|
||||
# Note: Apparently Game Center is just called 'Game Center'
|
||||
# in all languages. Can revisit if not true.
|
||||
# https://developer.apple.com/forums/thread/725779
|
||||
game_service_button_label = bui.Lstr(
|
||||
value=bui.charstr(bui.SpecialChar.GAME_CENTER_LOGO)
|
||||
+ 'Game Center'
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"unknown account type: '" + str(v1_account_type) + "'"
|
||||
@ -761,7 +882,7 @@ class AccountSettingsWindow(bui.Window):
|
||||
autoselect=True,
|
||||
on_activate_call=self._on_game_service_button_press,
|
||||
size=(button_width, 50),
|
||||
label=v1_account_type_name,
|
||||
label=game_service_button_label,
|
||||
)
|
||||
if first_selectable is None:
|
||||
first_selectable = btn
|
||||
@ -771,7 +892,7 @@ class AccountSettingsWindow(bui.Window):
|
||||
right_widget=bui.get_special_widget('party_button'),
|
||||
)
|
||||
bui.widget(edit=btn, left_widget=bbtn)
|
||||
v -= game_service_button_space * 0.15
|
||||
v -= game_service_button_space * 0.4
|
||||
else:
|
||||
self.game_service_button = None
|
||||
|
||||
@ -804,13 +925,15 @@ class AccountSettingsWindow(bui.Window):
|
||||
autoselect=True,
|
||||
icon=bui.gettexture(
|
||||
'googlePlayAchievementsIcon'
|
||||
if is_gpgs
|
||||
if gpgs_active
|
||||
else 'achievementsIcon'
|
||||
),
|
||||
icon_color=(0.8, 0.95, 0.7) if is_gpgs else (0.85, 0.8, 0.9),
|
||||
icon_color=(0.8, 0.95, 0.7)
|
||||
if gpgs_active
|
||||
else (0.85, 0.8, 0.9),
|
||||
on_activate_call=(
|
||||
self._on_custom_achievements_press
|
||||
if is_gpgs
|
||||
if gpgs_active
|
||||
else self._on_achievements_press
|
||||
),
|
||||
size=(button_width, 50),
|
||||
@ -1135,19 +1258,21 @@ class AccountSettingsWindow(bui.Window):
|
||||
self._needs_refresh = False
|
||||
|
||||
def _on_game_service_button_press(self) -> None:
|
||||
if bui.app.classic is not None:
|
||||
bui.app.classic.show_online_score_ui()
|
||||
if bui.app.plus is not None:
|
||||
bui.app.plus.show_game_service_ui()
|
||||
else:
|
||||
logging.warning('game service ui not available without classic.')
|
||||
logging.warning(
|
||||
'game-service-ui not available without plus feature-set.'
|
||||
)
|
||||
|
||||
def _on_custom_achievements_press(self) -> None:
|
||||
if bui.app.classic is not None:
|
||||
if bui.app.plus is not None:
|
||||
bui.apptimer(
|
||||
0.15,
|
||||
bui.Call(bui.app.classic.show_online_score_ui, 'achievements'),
|
||||
bui.Call(bui.app.plus.show_game_service_ui, 'achievements'),
|
||||
)
|
||||
else:
|
||||
logging.warning('show_online_score_ui requires classic')
|
||||
logging.warning('show_game_service_ui requires plus feature-set.')
|
||||
|
||||
def _on_achievements_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
@ -1162,11 +1287,21 @@ class AccountSettingsWindow(bui.Window):
|
||||
show_what_is_v2_page()
|
||||
|
||||
def _on_manage_account_press(self) -> None:
|
||||
bui.screenmessage(bui.Lstr(resource='oneMomentText'))
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
# Preemptively fail if it looks like we won't be able to talk to
|
||||
# the server anyway.
|
||||
if not plus.cloud.connected:
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='internal.unavailableNoConnectionText'),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
return
|
||||
|
||||
bui.screenmessage(bui.Lstr(resource='oneMomentText'))
|
||||
|
||||
# We expect to have a v2 account signed in if we get here.
|
||||
if plus.accounts.primary is None:
|
||||
logging.exception(
|
||||
@ -1184,6 +1319,9 @@ class AccountSettingsWindow(bui.Window):
|
||||
self, response: bacommon.cloud.ManageAccountResponse | Exception
|
||||
) -> None:
|
||||
if isinstance(response, Exception) or response.url is None:
|
||||
logging.warning(
|
||||
'Got error in manage-account-response: %s.', response
|
||||
)
|
||||
bui.screenmessage(bui.Lstr(resource='errorText'), color=(1, 0, 0))
|
||||
bui.getsound('error').play()
|
||||
return
|
||||
@ -1191,13 +1329,13 @@ class AccountSettingsWindow(bui.Window):
|
||||
bui.open_url(response.url)
|
||||
|
||||
def _on_leaderboards_press(self) -> None:
|
||||
if bui.app.classic is not None:
|
||||
if bui.app.plus is not None:
|
||||
bui.apptimer(
|
||||
0.15,
|
||||
bui.Call(bui.app.classic.show_online_score_ui, 'leaderboards'),
|
||||
bui.Call(bui.app.plus.show_game_service_ui, 'leaderboards'),
|
||||
)
|
||||
else:
|
||||
logging.warning('show_online_score_ui requires classic')
|
||||
logging.warning('show_game_service_ui requires classic')
|
||||
|
||||
def _have_unlinkable_v1_accounts(self) -> bool:
|
||||
plus = bui.app.plus
|
||||
@ -1323,7 +1461,7 @@ class AccountSettingsWindow(bui.Window):
|
||||
swidth = bui.get_string_width(name_str, suppress_warning=True)
|
||||
# Eww; number-fudging. Need to recalibrate this if
|
||||
# account name scaling changes.
|
||||
x = self._sub_width * 0.5 - swidth * 0.75 - 170
|
||||
x = self._sub_width * 0.5 - swidth * 0.75 - 190
|
||||
|
||||
bui.textwidget(
|
||||
edit=self._account_name_what_is_text,
|
||||
@ -1371,9 +1509,18 @@ class AccountSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.profile.browser import ProfileBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
ProfileBrowserWindow(origin_widget=self._player_profiles_button)
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ProfileBrowserWindow(
|
||||
origin_widget=self._player_profiles_button
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _cancel_sign_in_press(self) -> None:
|
||||
# If we're waiting on an adapter to give us credentials, abort.
|
||||
@ -1466,7 +1613,11 @@ class AccountSettingsWindow(bui.Window):
|
||||
if isinstance(result, Exception):
|
||||
# For now just make a bit of noise if anything went wrong;
|
||||
# can get more specific as needed later.
|
||||
bui.screenmessage(bui.Lstr(resource='errorText'), color=(1, 0, 0))
|
||||
logging.warning('Got error in v2 sign-in result: %s', result)
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='internal.signInNoConnectionText'),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
else:
|
||||
# Success! Plug in these credentials which will begin
|
||||
@ -1530,6 +1681,10 @@ class AccountSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -1538,7 +1693,8 @@ class AccountSettingsWindow(bui.Window):
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -62,14 +62,11 @@ class V2ProxySignInWindow(bui.Window):
|
||||
label=bui.Lstr(resource='cancelText'),
|
||||
on_activate_call=self._done,
|
||||
autoselect=True,
|
||||
color=(0.55, 0.5, 0.6),
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
)
|
||||
|
||||
if bool(False):
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, cancel_button=self._cancel_button
|
||||
)
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, cancel_button=self._cancel_button
|
||||
)
|
||||
|
||||
self._update_timer: bui.AppTimer | None = None
|
||||
|
||||
@ -242,4 +239,7 @@ class V2ProxySignInWindow(bui.Window):
|
||||
)
|
||||
|
||||
def _done(self) -> None:
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_scale')
|
||||
|
||||
@ -93,6 +93,7 @@ class ConfigNumberEdit:
|
||||
displayname: str | bui.Lstr | None = None,
|
||||
changesound: bool = True,
|
||||
textscale: float = 1.0,
|
||||
as_percent: bool = False,
|
||||
):
|
||||
if displayname is None:
|
||||
displayname = configkey
|
||||
@ -103,6 +104,7 @@ class ConfigNumberEdit:
|
||||
self._increment = increment
|
||||
self._callback = callback
|
||||
self._value = bui.app.config.resolve(configkey)
|
||||
self._as_percent = as_percent
|
||||
|
||||
self.nametext = bui.textwidget(
|
||||
parent=parent,
|
||||
@ -166,4 +168,8 @@ class ConfigNumberEdit:
|
||||
bui.app.config.apply_and_commit()
|
||||
|
||||
def _update_display(self) -> None:
|
||||
bui.textwidget(edit=self.valuetext, text=f'{self._value:.1f}')
|
||||
if self._as_percent:
|
||||
val = f'{round(self._value*100.0)}%'
|
||||
else:
|
||||
val = f'{self._value:.1f}'
|
||||
bui.textwidget(edit=self.valuetext, text=val)
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""UI for dealing with broken config files."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bauiv1 as bui
|
||||
|
||||
|
||||
class ConfigErrorWindow(bui.Window):
|
||||
"""Window for dealing with a broken config."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._config_file_path = bui.app.env.config_file_path
|
||||
width = 800
|
||||
super().__init__(
|
||||
bui.containerwidget(size=(width, 400), transition='in_right')
|
||||
)
|
||||
padding = 20
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(padding, 220 + 60),
|
||||
size=(width - 2 * padding, 100 - 2 * padding),
|
||||
h_align='center',
|
||||
v_align='top',
|
||||
scale=0.73,
|
||||
text=(
|
||||
f'Error reading {bui.appnameupper()} config file'
|
||||
':\n\n\nCheck the console'
|
||||
' (press ~ twice) for details.\n\nWould you like to quit and'
|
||||
' try to fix it by hand\nor overwrite it with defaults?\n\n'
|
||||
'(high scores, player profiles, etc will be lost if you'
|
||||
' overwrite)'
|
||||
),
|
||||
)
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(padding, 198 + 60),
|
||||
size=(width - 2 * padding, 100 - 2 * padding),
|
||||
h_align='center',
|
||||
v_align='top',
|
||||
scale=0.5,
|
||||
text=self._config_file_path,
|
||||
)
|
||||
quit_button = bui.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(35, 30),
|
||||
size=(240, 54),
|
||||
label='Quit and Edit',
|
||||
on_activate_call=self._quit,
|
||||
)
|
||||
bui.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(width - 370, 30),
|
||||
size=(330, 54),
|
||||
label='Overwrite with Defaults',
|
||||
on_activate_call=self._defaults,
|
||||
)
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget,
|
||||
cancel_button=quit_button,
|
||||
selected_child=quit_button,
|
||||
)
|
||||
|
||||
def _quit(self) -> None:
|
||||
bui.apptimer(0.001, self._edit_and_quit)
|
||||
bui.lock_all_input()
|
||||
|
||||
def _edit_and_quit(self) -> None:
|
||||
bui.open_file_externally(self._config_file_path)
|
||||
bui.apptimer(0.1, bui.quit)
|
||||
|
||||
def _defaults(self) -> None:
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
bui.getsound('gunCocking').play()
|
||||
bui.screenmessage('settings reset.', color=(1, 1, 0))
|
||||
|
||||
# At this point settings are already set; lets just commit them
|
||||
# to disk.
|
||||
bui.commit_app_config(force=True)
|
||||
@ -85,8 +85,8 @@ class CoopBrowserWindow(bui.Window):
|
||||
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 1320 if uiscale is bui.UIScale.SMALL else 1120
|
||||
self._x_inset = x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._width = 1520 if uiscale is bui.UIScale.SMALL else 1120
|
||||
self._x_inset = x_inset = 200 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._height = (
|
||||
657
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -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,
|
||||
@ -1019,6 +1019,10 @@ class CoopBrowserWindow(bui.Window):
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.league.rankwindow import LeagueRankWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1032,7 +1036,8 @@ class CoopBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
LeagueRankWindow(
|
||||
origin_widget=self._league_rank_button.get_button()
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _switch_to_score(
|
||||
@ -1043,6 +1048,10 @@ class CoopBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1058,7 +1067,8 @@ class CoopBrowserWindow(bui.Window):
|
||||
origin_widget=self._store_button.get_button(),
|
||||
show_tab=show_tab,
|
||||
back_location='CoopBrowserWindow',
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def is_tourney_data_up_to_date(self) -> bool:
|
||||
@ -1218,6 +1228,10 @@ class CoopBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# If something is selected, store it.
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
@ -1225,7 +1239,8 @@ class CoopBrowserWindow(bui.Window):
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(transition='in_left').get_root_widget()
|
||||
PlayWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -359,10 +359,15 @@ class CreditsListWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -379,8 +379,13 @@ class DebugWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
133
src/assets/ba_data/python/bauiv1lib/discord.py
Normal file
133
src/assets/ba_data/python/bauiv1lib/discord.py
Normal file
@ -0,0 +1,133 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""UI functionality for the Discord window."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bauiv1 as bui
|
||||
|
||||
|
||||
class DiscordWindow(bui.Window):
|
||||
"""Window for joining the Discord."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
transition: str = 'in_right',
|
||||
origin_widget: bui.Widget | None = None,
|
||||
):
|
||||
if bui.app.classic is None:
|
||||
raise RuntimeError('This requires classic support.')
|
||||
|
||||
app = bui.app
|
||||
assert app.classic is not None
|
||||
|
||||
# If they provided an origin-widget, scale up from that.
|
||||
scale_origin: tuple[float, float] | None
|
||||
if origin_widget is not None:
|
||||
self._transition_out = 'out_scale'
|
||||
scale_origin = origin_widget.get_screen_space_center()
|
||||
transition = 'in_scale'
|
||||
else:
|
||||
self._transition_out = 'out_right'
|
||||
scale_origin = None
|
||||
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 800
|
||||
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._height = 320
|
||||
top_extra = 10 if uiscale is bui.UIScale.SMALL else 0
|
||||
super().__init__(
|
||||
root_widget=bui.containerwidget(
|
||||
size=(self._width, self._height + top_extra),
|
||||
transition=transition,
|
||||
toolbar_visibility='menu_minimal',
|
||||
scale_origin_stack_offset=scale_origin,
|
||||
scale=(
|
||||
1.6
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
else 1.3
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else 1.0
|
||||
),
|
||||
stack_offset=(0, 5) if uiscale is bui.UIScale.SMALL else (0, 0),
|
||||
)
|
||||
)
|
||||
|
||||
if app.ui_v1.use_toolbars and uiscale is bui.UIScale.SMALL:
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, on_cancel_call=self._do_back
|
||||
)
|
||||
self._back_button = None
|
||||
else:
|
||||
self._back_button = bui.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(53 + x_inset, self._height - 60),
|
||||
size=(140, 60),
|
||||
scale=0.8,
|
||||
autoselect=True,
|
||||
label=bui.Lstr(resource='backText'),
|
||||
button_type='back',
|
||||
on_activate_call=self._do_back,
|
||||
)
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, cancel_button=self._back_button
|
||||
)
|
||||
|
||||
# Do we need to translate 'Discord'? Or is that always the name?
|
||||
self._title_text = bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(0, self._height - 52),
|
||||
size=(self._width, 25),
|
||||
text='Discord',
|
||||
color=app.ui_v1.title_color,
|
||||
h_align='center',
|
||||
v_align='top',
|
||||
)
|
||||
|
||||
min_size = min(self._width - 25, self._height - 25)
|
||||
bui.imagewidget(
|
||||
parent=self._root_widget,
|
||||
position=(40, -15),
|
||||
size=(min_size, min_size),
|
||||
texture=bui.gettexture('discordServer'),
|
||||
)
|
||||
|
||||
# Hmm should we translate this? The discord server is mostly
|
||||
# English so being able to read this might be a good screening
|
||||
# process?..
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width / 2 - 60, self._height - 100),
|
||||
text='We have our own Discord server where you can:\n- Find new'
|
||||
' friends and people to play with\n- Participate in Office'
|
||||
' Hours/Coffee with Eric\n- Share mods, plugins, art, and'
|
||||
' memes\n- Report bugs and make feature suggestions\n'
|
||||
'- Troubleshoot issues',
|
||||
maxwidth=(self._width - 10) / 2,
|
||||
color=(1, 1, 1, 1),
|
||||
h_align='left',
|
||||
v_align='top',
|
||||
)
|
||||
|
||||
bui.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width / 2 - 30, 20),
|
||||
size=(self._width / 2 - 60, 60),
|
||||
autoselect=True,
|
||||
label=bui.Lstr(resource='discordJoinText'),
|
||||
text_scale=1.0,
|
||||
on_activate_call=bui.Call(
|
||||
bui.open_url, 'https://ballistica.net/discord'
|
||||
),
|
||||
)
|
||||
|
||||
if self._back_button is not None:
|
||||
bui.buttonwidget(
|
||||
edit=self._back_button,
|
||||
button_type='backSmall',
|
||||
size=(60, 60),
|
||||
label=bui.charstr(bui.SpecialChar.BACK),
|
||||
)
|
||||
|
||||
def _do_back(self) -> None:
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_scale')
|
||||
@ -94,8 +94,8 @@ class GatherWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_location('Gather')
|
||||
bui.set_party_icon_always_visible(True)
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 1240 if uiscale is bui.UIScale.SMALL else 1040
|
||||
x_offs = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._width = 1440 if uiscale is bui.UIScale.SMALL else 1040
|
||||
x_offs = 200 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._height = (
|
||||
582
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -270,12 +270,17 @@ class GatherWindow(bui.Window):
|
||||
"""Called by the private-hosting tab to select a playlist."""
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.selecting_private_party_playlist = True
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(origin_widget=origin_widget).get_root_widget()
|
||||
PlayWindow(origin_widget=origin_widget).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _set_tab(self, tab_id: TabID) -> None:
|
||||
@ -383,11 +388,16 @@ class GatherWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -16,10 +16,6 @@ if TYPE_CHECKING:
|
||||
class AboutGatherTab(GatherTab):
|
||||
"""The about tab in the gather UI"""
|
||||
|
||||
def __init__(self, window: GatherWindow) -> None:
|
||||
super().__init__(window)
|
||||
self._container: bui.Widget | None = None
|
||||
|
||||
def on_activate(
|
||||
self,
|
||||
parent_widget: bui.Widget,
|
||||
@ -29,9 +25,45 @@ class AboutGatherTab(GatherTab):
|
||||
region_left: float,
|
||||
region_bottom: float,
|
||||
) -> bui.Widget:
|
||||
# pylint: disable=too-many-locals
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
try_tickets = plus.get_v1_account_misc_read_val(
|
||||
'friendTryTickets', None
|
||||
)
|
||||
|
||||
show_message = True
|
||||
# Squish message as needed to get things to fit nicely at
|
||||
# various scales.
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
message_height = (
|
||||
210
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
else 305
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else 370
|
||||
)
|
||||
# Let's not talk about sharing in vr-mode; its tricky to fit more
|
||||
# than one head in a VR-headset.
|
||||
show_message_extra = not bui.app.env.vr
|
||||
message_extra_height = 60
|
||||
show_invite = try_tickets is not None
|
||||
invite_height = 80
|
||||
show_discord = True
|
||||
discord_height = 80
|
||||
|
||||
c_height = 0
|
||||
if show_message:
|
||||
c_height += message_height
|
||||
if show_message_extra:
|
||||
c_height += message_extra_height
|
||||
if show_invite:
|
||||
c_height += invite_height
|
||||
if show_discord:
|
||||
c_height += discord_height
|
||||
|
||||
party_button_label = bui.charstr(bui.SpecialChar.TOP_BUTTON)
|
||||
message = bui.Lstr(
|
||||
resource='gatherWindow.aboutDescriptionText',
|
||||
@ -41,9 +73,7 @@ class AboutGatherTab(GatherTab):
|
||||
],
|
||||
)
|
||||
|
||||
# Let's not talk about sharing in vr-mode; its tricky to fit more
|
||||
# than one head in a VR-headset ;-)
|
||||
if not bui.app.env.vr:
|
||||
if show_message_extra:
|
||||
message = bui.Lstr(
|
||||
value='${A}\n\n${B}',
|
||||
subs=[
|
||||
@ -57,47 +87,52 @@ class AboutGatherTab(GatherTab):
|
||||
),
|
||||
],
|
||||
)
|
||||
string_height = 400
|
||||
include_invite = True
|
||||
msc_scale = 1.1
|
||||
c_height_2 = min(region_height, string_height * msc_scale + 100)
|
||||
try_tickets = plus.get_v1_account_misc_read_val(
|
||||
'friendTryTickets', None
|
||||
)
|
||||
if try_tickets is None:
|
||||
include_invite = False
|
||||
self._container = bui.containerwidget(
|
||||
|
||||
scroll_widget = bui.scrollwidget(
|
||||
parent=parent_widget,
|
||||
position=(region_left, region_bottom),
|
||||
size=(region_width, region_height),
|
||||
highlight=False,
|
||||
border_opacity=0,
|
||||
)
|
||||
msc_scale = 1.1
|
||||
|
||||
container = bui.containerwidget(
|
||||
parent=scroll_widget,
|
||||
position=(
|
||||
region_left,
|
||||
region_bottom + (region_height - c_height_2) * 0.5,
|
||||
region_bottom + (region_height - c_height) * 0.5,
|
||||
),
|
||||
size=(region_width, c_height_2),
|
||||
size=(region_width, c_height),
|
||||
background=False,
|
||||
selectable=include_invite,
|
||||
selectable=show_invite or show_discord,
|
||||
)
|
||||
bui.widget(edit=self._container, up_widget=tab_button)
|
||||
# Allows escaping if we select the container somehow (though
|
||||
# shouldn't be possible when buttons are present).
|
||||
bui.widget(edit=container, up_widget=tab_button)
|
||||
|
||||
bui.textwidget(
|
||||
parent=self._container,
|
||||
position=(
|
||||
region_width * 0.5,
|
||||
c_height_2 * (0.58 if include_invite else 0.5),
|
||||
),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=msc_scale,
|
||||
size=(0, 0),
|
||||
maxwidth=region_width * 0.9,
|
||||
max_height=c_height_2 * (0.7 if include_invite else 0.9),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=message,
|
||||
)
|
||||
|
||||
if include_invite:
|
||||
y = c_height - 30
|
||||
if show_message:
|
||||
bui.textwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.57, 35),
|
||||
parent=container,
|
||||
position=(region_width * 0.5, y),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=msc_scale,
|
||||
size=(0, 0),
|
||||
maxwidth=region_width * 0.9,
|
||||
max_height=message_height,
|
||||
h_align='center',
|
||||
v_align='top',
|
||||
text=message,
|
||||
)
|
||||
y -= message_height
|
||||
if show_message_extra:
|
||||
y -= message_extra_height
|
||||
|
||||
if show_invite:
|
||||
bui.textwidget(
|
||||
parent=container,
|
||||
position=(region_width * 0.57, y),
|
||||
color=(0, 1, 0),
|
||||
scale=0.6,
|
||||
size=(0, 0),
|
||||
@ -110,9 +145,9 @@ class AboutGatherTab(GatherTab):
|
||||
subs=[('${COUNT}', str(try_tickets))],
|
||||
),
|
||||
)
|
||||
bui.buttonwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.59, 10),
|
||||
invite_button = bui.buttonwidget(
|
||||
parent=container,
|
||||
position=(region_width * 0.59, y - 25),
|
||||
size=(230, 50),
|
||||
color=(0.54, 0.42, 0.56),
|
||||
textcolor=(0, 1, 0),
|
||||
@ -124,7 +159,44 @@ class AboutGatherTab(GatherTab):
|
||||
on_activate_call=bui.WeakCall(self._invite_to_try_press),
|
||||
up_widget=tab_button,
|
||||
)
|
||||
return self._container
|
||||
y -= invite_height
|
||||
else:
|
||||
invite_button = None
|
||||
|
||||
if show_discord:
|
||||
bui.textwidget(
|
||||
parent=container,
|
||||
position=(region_width * 0.57, y),
|
||||
color=(0.6, 0.6, 1),
|
||||
scale=0.6,
|
||||
size=(0, 0),
|
||||
maxwidth=region_width * 0.5,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
flatness=1.0,
|
||||
text=bui.Lstr(resource='discordFriendsText'),
|
||||
)
|
||||
discord_button = bui.buttonwidget(
|
||||
parent=container,
|
||||
position=(region_width * 0.59, y - 25),
|
||||
size=(230, 50),
|
||||
color=(0.54, 0.42, 0.56),
|
||||
textcolor=(0.6, 0.6, 1),
|
||||
label=bui.Lstr(resource='discordJoinText'),
|
||||
autoselect=True,
|
||||
on_activate_call=bui.WeakCall(self._join_the_discord_press),
|
||||
up_widget=(
|
||||
invite_button if invite_button is not None else tab_button
|
||||
),
|
||||
)
|
||||
y -= discord_height
|
||||
else:
|
||||
discord_button = None
|
||||
|
||||
if discord_button is not None:
|
||||
pass
|
||||
|
||||
return scroll_widget
|
||||
|
||||
def _invite_to_try_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
@ -137,3 +209,10 @@ class AboutGatherTab(GatherTab):
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
handle_app_invites_press()
|
||||
|
||||
def _join_the_discord_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.discord import DiscordWindow
|
||||
|
||||
assert bui.app.classic is not None
|
||||
DiscordWindow().get_root_widget()
|
||||
|
||||
@ -99,6 +99,7 @@ class ManualGatherTab(GatherTab):
|
||||
self._party_edit_name_text: bui.Widget | None = None
|
||||
self._party_edit_addr_text: bui.Widget | None = None
|
||||
self._party_edit_port_text: bui.Widget | None = None
|
||||
self._no_parties_added_text: bui.Widget | None = None
|
||||
|
||||
def on_activate(
|
||||
self,
|
||||
@ -142,6 +143,7 @@ class ManualGatherTab(GatherTab):
|
||||
playsound=True,
|
||||
),
|
||||
text=bui.Lstr(resource='gatherWindow.manualJoinSectionText'),
|
||||
glow_type='uniform',
|
||||
)
|
||||
self._favorites_text = bui.textwidget(
|
||||
parent=self._container,
|
||||
@ -162,6 +164,7 @@ class ManualGatherTab(GatherTab):
|
||||
playsound=True,
|
||||
),
|
||||
text=bui.Lstr(resource='gatherWindow.favoritesText'),
|
||||
glow_type='uniform',
|
||||
)
|
||||
bui.widget(edit=self._join_by_address_text, up_widget=tab_button)
|
||||
bui.widget(
|
||||
@ -316,7 +319,7 @@ class ManualGatherTab(GatherTab):
|
||||
self._check_button = bui.textwidget(
|
||||
parent=self._container,
|
||||
size=(250, 60),
|
||||
text=bui.Lstr(resource='gatherWindow.' 'showMyAddressText'),
|
||||
text=bui.Lstr(resource='gatherWindow.showMyAddressText'),
|
||||
v_align='center',
|
||||
h_align='center',
|
||||
click_activate=True,
|
||||
@ -331,6 +334,7 @@ class ManualGatherTab(GatherTab):
|
||||
self._container,
|
||||
c_width,
|
||||
),
|
||||
glow_type='uniform',
|
||||
)
|
||||
bui.widget(edit=self._check_button, up_widget=btn)
|
||||
|
||||
@ -453,6 +457,24 @@ class ManualGatherTab(GatherTab):
|
||||
claims_left_right=True,
|
||||
)
|
||||
|
||||
self._no_parties_added_text = bui.textwidget(
|
||||
parent=self._container,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text='',
|
||||
color=(0.6, 0.6, 0.6),
|
||||
scale=1.2,
|
||||
position=(
|
||||
(
|
||||
(190 if uiscale is bui.UIScale.SMALL else 225)
|
||||
+ sub_scroll_width * 0.5
|
||||
),
|
||||
v + sub_scroll_height * 0.5,
|
||||
),
|
||||
glow_type='uniform',
|
||||
)
|
||||
|
||||
self._favorite_selected = None
|
||||
self._refresh_favorites()
|
||||
|
||||
@ -695,6 +717,12 @@ class ManualGatherTab(GatherTab):
|
||||
|
||||
assert self._favorites_scroll_width is not None
|
||||
assert self._favorites_connect_button is not None
|
||||
|
||||
bui.textwidget(
|
||||
edit=self._no_parties_added_text,
|
||||
text='',
|
||||
)
|
||||
num_of_fav = 0
|
||||
for i, server in enumerate(servers):
|
||||
txt = bui.textwidget(
|
||||
parent=self._columnwidget,
|
||||
@ -718,11 +746,13 @@ class ManualGatherTab(GatherTab):
|
||||
)
|
||||
if i == 0:
|
||||
bui.widget(edit=txt, up_widget=self._favorites_text)
|
||||
self._favorite_selected = server
|
||||
bui.widget(
|
||||
edit=txt,
|
||||
left_widget=self._favorites_connect_button,
|
||||
right_widget=txt,
|
||||
)
|
||||
num_of_fav = num_of_fav + 1
|
||||
|
||||
# If there's no servers, allow selecting out of the scroll area
|
||||
bui.containerwidget(
|
||||
@ -735,6 +765,11 @@ class ManualGatherTab(GatherTab):
|
||||
up_widget=self._favorites_text,
|
||||
left_widget=self._favorites_connect_button,
|
||||
)
|
||||
if num_of_fav == 0:
|
||||
bui.textwidget(
|
||||
edit=self._no_parties_added_text,
|
||||
text=bui.Lstr(resource='gatherWindow.noPartiesAddedText'),
|
||||
)
|
||||
|
||||
def on_deactivate(self) -> None:
|
||||
self._access_check_timer = None
|
||||
@ -800,8 +835,17 @@ class ManualGatherTab(GatherTab):
|
||||
}
|
||||
config.commit()
|
||||
bui.getsound('gunCocking').play()
|
||||
bui.screenmessage(
|
||||
bui.Lstr(
|
||||
resource='addedToFavoritesText', subs=[('${NAME}', addr)]
|
||||
),
|
||||
color=(0, 1, 0),
|
||||
)
|
||||
else:
|
||||
bui.screenmessage('Invalid Address', color=(1, 0, 0))
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='internal.invalidAddressErrorText'),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
|
||||
def _host_lookup_result(
|
||||
|
||||
@ -120,6 +120,7 @@ class PrivateGatherTab(GatherTab):
|
||||
playsound=True,
|
||||
),
|
||||
text=bui.Lstr(resource='gatherWindow.privatePartyJoinText'),
|
||||
glow_type='uniform',
|
||||
)
|
||||
self._host_sub_tab_text = bui.textwidget(
|
||||
parent=self._container,
|
||||
@ -138,6 +139,7 @@ class PrivateGatherTab(GatherTab):
|
||||
playsound=True,
|
||||
),
|
||||
text=bui.Lstr(resource='gatherWindow.privatePartyHostText'),
|
||||
glow_type='uniform',
|
||||
)
|
||||
bui.widget(edit=self._join_sub_tab_text, up_widget=tab_button)
|
||||
bui.widget(
|
||||
@ -458,9 +460,9 @@ class PrivateGatherTab(GatherTab):
|
||||
scale=1.5,
|
||||
size=(300, 50),
|
||||
editable=True,
|
||||
max_chars=20,
|
||||
description=bui.Lstr(resource='gatherWindow.partyCodeText'),
|
||||
autoselect=True,
|
||||
maxwidth=250,
|
||||
h_align='left',
|
||||
v_align='center',
|
||||
text='',
|
||||
@ -962,7 +964,7 @@ class PrivateGatherTab(GatherTab):
|
||||
code = cast(str, bui.textwidget(query=self._join_party_code_text))
|
||||
if not code:
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='internal.invalidAddressErrorText'),
|
||||
bui.Lstr(translate=('serverResponses', 'Invalid code.')),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
|
||||
@ -114,7 +114,7 @@ class UIRow:
|
||||
self._name_widget = bui.textwidget(
|
||||
text=bui.Lstr(value=party.name),
|
||||
parent=columnwidget,
|
||||
size=(sub_scroll_width * 0.63, 20),
|
||||
size=(sub_scroll_width * 0.46, 20),
|
||||
position=(0 + hpos, 4 + vpos),
|
||||
selectable=True,
|
||||
on_select_call=bui.WeakCall(
|
||||
@ -248,6 +248,7 @@ class AddrFetchThread(Thread):
|
||||
self._call = call
|
||||
|
||||
def run(self) -> None:
|
||||
sock: socket.socket | None = None
|
||||
try:
|
||||
# FIXME: Update this to work with IPv6 at some point.
|
||||
import socket
|
||||
@ -255,7 +256,6 @@ class AddrFetchThread(Thread):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.connect(('8.8.8.8', 80))
|
||||
val = sock.getsockname()[0]
|
||||
sock.close()
|
||||
bui.pushcall(bui.Call(self._call, val), from_other_thread=True)
|
||||
except Exception as exc:
|
||||
from efro.error import is_udp_communication_error
|
||||
@ -265,6 +265,9 @@ class AddrFetchThread(Thread):
|
||||
pass
|
||||
else:
|
||||
logging.exception('Error in addr-fetch-thread')
|
||||
finally:
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
|
||||
|
||||
class PingThread(Thread):
|
||||
@ -361,6 +364,7 @@ class PublicGatherTab(GatherTab):
|
||||
self._last_server_list_query_time: float | None = None
|
||||
self._join_list_column: bui.Widget | None = None
|
||||
self._join_status_text: bui.Widget | None = None
|
||||
self._no_servers_found_text: bui.Widget | None = None
|
||||
self._host_max_party_size_value: bui.Widget | None = None
|
||||
self._host_max_party_size_minus_button: (bui.Widget | None) = None
|
||||
self._host_max_party_size_plus_button: (bui.Widget | None) = None
|
||||
@ -431,6 +435,7 @@ class PublicGatherTab(GatherTab):
|
||||
text=bui.Lstr(
|
||||
resource='gatherWindow.' 'joinPublicPartyDescriptionText'
|
||||
),
|
||||
glow_type='uniform',
|
||||
)
|
||||
self._host_text = bui.textwidget(
|
||||
parent=self._container,
|
||||
@ -453,6 +458,7 @@ class PublicGatherTab(GatherTab):
|
||||
text=bui.Lstr(
|
||||
resource='gatherWindow.' 'hostPublicPartyDescriptionText'
|
||||
),
|
||||
glow_type='uniform',
|
||||
)
|
||||
bui.widget(edit=self._join_text, up_widget=tab_button)
|
||||
bui.widget(
|
||||
@ -658,6 +664,18 @@ class PublicGatherTab(GatherTab):
|
||||
color=(0.6, 0.6, 0.6),
|
||||
position=(c_width * 0.5, c_height * 0.5),
|
||||
)
|
||||
self._no_servers_found_text = bui.textwidget(
|
||||
parent=self._container,
|
||||
text='',
|
||||
size=(0, 0),
|
||||
scale=0.9,
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
h_align='center',
|
||||
v_align='top',
|
||||
color=(0.6, 0.6, 0.6),
|
||||
position=(c_width * 0.5, c_height * 0.5),
|
||||
)
|
||||
|
||||
def _build_host_tab(
|
||||
self, region_width: float, region_height: float
|
||||
@ -950,6 +968,9 @@ class PublicGatherTab(GatherTab):
|
||||
self._update_party_rows()
|
||||
|
||||
def _update_party_rows(self) -> None:
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
columnwidget = self._join_list_column
|
||||
if not columnwidget:
|
||||
return
|
||||
@ -963,6 +984,7 @@ class PublicGatherTab(GatherTab):
|
||||
edit=self._host_scrollwidget,
|
||||
claims_up_down=(len(self._parties_displayed) > 0),
|
||||
)
|
||||
bui.textwidget(edit=self._no_servers_found_text, text='')
|
||||
|
||||
# Clip if we have more UI rows than parties to show.
|
||||
clipcount = len(self._ui_rows) - len(self._parties_displayed)
|
||||
@ -972,6 +994,15 @@ class PublicGatherTab(GatherTab):
|
||||
|
||||
# If we have no parties to show, we're done.
|
||||
if not self._parties_displayed:
|
||||
text = self._join_status_text
|
||||
if (
|
||||
plus.get_v1_account_state() == 'signed_in'
|
||||
and cast(str, bui.textwidget(query=text)) == ''
|
||||
):
|
||||
bui.textwidget(
|
||||
edit=self._no_servers_found_text,
|
||||
text=bui.Lstr(resource='noServersFoundText'),
|
||||
)
|
||||
return
|
||||
|
||||
sub_scroll_width = 830
|
||||
|
||||
@ -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
|
||||
@ -732,8 +732,13 @@ class GetCurrencyWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.store import browser
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
@ -745,7 +750,9 @@ class GetCurrencyWindow(bui.Window):
|
||||
).get_root_widget()
|
||||
if not self._from_modal_store:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(window)
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
window, from_window=self._root_widget
|
||||
)
|
||||
self._transitioning_out = True
|
||||
|
||||
|
||||
|
||||
@ -36,8 +36,8 @@ class HelpWindow(bui.Window):
|
||||
self._main_menu = main_menu
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
width = 950 if uiscale is bui.UIScale.SMALL else 750
|
||||
x_offs = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
width = 1050 if uiscale is bui.UIScale.SMALL else 750
|
||||
x_offs = 150 if uiscale is bui.UIScale.SMALL else 0
|
||||
height = (
|
||||
460
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -645,11 +645,16 @@ class HelpWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
if self._main_menu:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -501,9 +501,15 @@ class KioskWindow(bui.Window):
|
||||
def _do_full_menu(self) -> None:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
bui.app.classic.did_menu_intro = True # prevent delayed transition-in
|
||||
bui.app.ui_v1.set_main_menu_window(MainMenuWindow().get_root_widget())
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow().get_root_widget(), from_window=self._root_widget
|
||||
)
|
||||
|
||||
@ -1142,6 +1142,10 @@ class LeagueRankWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -1149,5 +1153,6 @@ class LeagueRankWindow(bui.Window):
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
CoopBrowserWindow(transition='in_left').get_root_widget()
|
||||
CoopBrowserWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -311,8 +311,8 @@ class MainMenuWindow(bui.Window):
|
||||
else self._confirm_end_game
|
||||
),
|
||||
)
|
||||
# Assume we're in a client-session.
|
||||
else:
|
||||
# Assume we're in a client-session.
|
||||
bui.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(h - self._button_width * 0.5 * scale, v),
|
||||
@ -360,7 +360,6 @@ class MainMenuWindow(bui.Window):
|
||||
tilt_scale=0.0,
|
||||
draw_controller=store_button,
|
||||
)
|
||||
|
||||
self._tdelay += self._t_delay_inc
|
||||
else:
|
||||
self._store_button = None
|
||||
@ -1039,6 +1038,10 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.confirm import QuitWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Note: Normally we should go through bui.quit(confirm=True) but
|
||||
# invoking the window directly lets us scale it up from the
|
||||
# button.
|
||||
@ -1048,24 +1051,34 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.kiosk import KioskWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
KioskWindow(transition='in_left').get_root_widget()
|
||||
KioskWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _show_account_window(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account.settings import AccountSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AccountSettingsWindow(
|
||||
origin_widget=self._account_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_store_pressed(self) -> None:
|
||||
@ -1073,6 +1086,10 @@ class MainMenuWindow(bui.Window):
|
||||
from bauiv1lib.store.browser import StoreBrowserWindow
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1085,7 +1102,8 @@ class MainMenuWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
StoreBrowserWindow(
|
||||
origin_widget=self._store_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _is_benchmark(self) -> bool:
|
||||
@ -1150,8 +1168,11 @@ class MainMenuWindow(bui.Window):
|
||||
|
||||
def _end_game(self) -> None:
|
||||
assert bui.app.classic is not None
|
||||
if not self._root_widget:
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
bui.app.classic.return_to_main_menu_session_gracefully(reset_ui=False)
|
||||
|
||||
@ -1167,39 +1188,54 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.creditslist import CreditsListWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
CreditsListWindow(
|
||||
origin_widget=self._credits_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _howtoplay(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.helpui import HelpWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
HelpWindow(
|
||||
main_menu=True, origin_widget=self._how_to_play_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _settings(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.allsettings import AllSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AllSettingsWindow(
|
||||
origin_widget=self._settings_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _resume_and_call(self, call: Callable[[], Any]) -> None:
|
||||
@ -1208,10 +1244,12 @@ class MainMenuWindow(bui.Window):
|
||||
|
||||
def _do_game_service_press(self) -> None:
|
||||
self._save_state()
|
||||
if bui.app.classic is not None:
|
||||
bui.app.classic.show_online_score_ui()
|
||||
if bui.app.plus is not None:
|
||||
bui.app.plus.show_game_service_ui()
|
||||
else:
|
||||
logging.warning('classic is required to show game service ui')
|
||||
logging.warning(
|
||||
'plus feature-set is required to show game service ui'
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
# Don't do this for the in-game menu.
|
||||
@ -1282,35 +1320,50 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.gather import GatherWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(origin_widget=self._gather_button).get_root_widget()
|
||||
GatherWindow(origin_widget=self._gather_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _watch_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.watch import WatchWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
WatchWindow(origin_widget=self._watch_button).get_root_widget()
|
||||
WatchWindow(origin_widget=self._watch_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _play_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.selecting_private_party_playlist = False
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(origin_widget=self._start_button).get_root_widget()
|
||||
PlayWindow(origin_widget=self._start_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _resume(self) -> None:
|
||||
@ -1318,7 +1371,7 @@ class MainMenuWindow(bui.Window):
|
||||
bui.app.classic.resume()
|
||||
if self._root_widget:
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
bui.app.ui_v1.clear_main_menu_window()
|
||||
bui.app.ui_v1.clear_main_menu_window(transition='out_right')
|
||||
|
||||
# If there's callbacks waiting for this window to go away, call them.
|
||||
for call in bui.app.ui_v1.main_menu_resume_callbacks:
|
||||
|
||||
@ -40,6 +40,7 @@ class PartyWindow(bui.Window):
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else 600
|
||||
)
|
||||
self._display_old_msgs = True
|
||||
super().__init__(
|
||||
root_widget=bui.containerwidget(
|
||||
size=(self._width, self._height),
|
||||
@ -92,9 +93,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')
|
||||
|
||||
@ -142,12 +144,6 @@ class PartyWindow(bui.Window):
|
||||
)
|
||||
self._chat_texts: list[bui.Widget] = []
|
||||
|
||||
# add all existing messages if chat is not muted
|
||||
if not bui.app.config.resolve('Chat Muted'):
|
||||
msgs = bs.get_chat_messages()
|
||||
for msg in msgs:
|
||||
self._add_msg(msg)
|
||||
|
||||
self._text_field = txt = bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
editable=True,
|
||||
@ -233,6 +229,23 @@ class PartyWindow(bui.Window):
|
||||
is_muted = bui.app.config.resolve('Chat Muted')
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
|
||||
choices: list[str] = ['unmute' if is_muted else 'mute']
|
||||
choices_display: list[bui.Lstr] = [
|
||||
bui.Lstr(resource='chatUnMuteText' if is_muted else 'chatMuteText')
|
||||
]
|
||||
|
||||
# Allow the 'Add to Favorites' option only if we're actually
|
||||
# connected to a party and if it doesn't seem to be a private
|
||||
# party (those are dynamically assigned addresses and ports so
|
||||
# it makes no sense to save them).
|
||||
server_info = bs.get_connection_to_host_info_2()
|
||||
if server_info is not None and not server_info.name.startswith(
|
||||
'Private Party '
|
||||
):
|
||||
choices.append('add_to_favorites')
|
||||
choices_display.append(bui.Lstr(resource='addToFavoritesText'))
|
||||
|
||||
PopupMenuWindow(
|
||||
position=self._menu_button.get_screen_space_center(),
|
||||
scale=(
|
||||
@ -242,12 +255,8 @@ class PartyWindow(bui.Window):
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else 1.23
|
||||
),
|
||||
choices=['unmute' if is_muted else 'mute'],
|
||||
choices_display=[
|
||||
bui.Lstr(
|
||||
resource='chatUnMuteText' if is_muted else 'chatMuteText'
|
||||
)
|
||||
],
|
||||
choices=choices,
|
||||
choices_display=choices_display,
|
||||
current_choice='unmute' if is_muted else 'mute',
|
||||
delegate=self,
|
||||
)
|
||||
@ -269,6 +278,12 @@ class PartyWindow(bui.Window):
|
||||
first.delete()
|
||||
else:
|
||||
bui.textwidget(edit=self._muted_text, color=(1, 1, 1, 0.0))
|
||||
# add all existing messages if chat is not muted
|
||||
if self._display_old_msgs:
|
||||
msgs = bs.get_chat_messages()
|
||||
for msg in msgs:
|
||||
self._add_msg(msg)
|
||||
self._display_old_msgs = False
|
||||
|
||||
# update roster section
|
||||
roster = bs.get_game_roster()
|
||||
@ -466,10 +481,75 @@ class PartyWindow(bui.Window):
|
||||
cfg = bui.app.config
|
||||
cfg['Chat Muted'] = choice == 'mute'
|
||||
cfg.apply_and_commit()
|
||||
self._display_old_msgs = True
|
||||
self._update()
|
||||
if choice == 'add_to_favorites':
|
||||
info = bs.get_connection_to_host_info_2()
|
||||
if info is not None:
|
||||
self._add_to_favorites(
|
||||
name=info.name,
|
||||
address=info.address,
|
||||
port_num=info.port,
|
||||
)
|
||||
else:
|
||||
# We should not allow the user to see this option
|
||||
# if they aren't in a server; this is our bad.
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='errorText'), color=(1, 0, 0)
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
else:
|
||||
print(f'unhandled popup type: {self._popup_type}')
|
||||
|
||||
def _add_to_favorites(
|
||||
self, name: str, address: str | None, port_num: int | None
|
||||
) -> None:
|
||||
addr = address
|
||||
if addr == '':
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='internal.invalidAddressErrorText'),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
return
|
||||
port = port_num if port_num is not None else -1
|
||||
if port > 65535 or port < 0:
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='internal.invalidPortErrorText'),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
return
|
||||
|
||||
# Avoid empty names.
|
||||
if not name:
|
||||
name = f'{addr}@{port}'
|
||||
|
||||
config = bui.app.config
|
||||
|
||||
if addr:
|
||||
if not isinstance(config.get('Saved Servers'), dict):
|
||||
config['Saved Servers'] = {}
|
||||
config['Saved Servers'][f'{addr}@{port}'] = {
|
||||
'addr': addr,
|
||||
'port': port,
|
||||
'name': name,
|
||||
}
|
||||
config.commit()
|
||||
bui.getsound('gunCocking').play()
|
||||
bui.screenmessage(
|
||||
bui.Lstr(
|
||||
resource='addedToFavoritesText', subs=[('${NAME}', name)]
|
||||
),
|
||||
color=(0, 1, 0),
|
||||
)
|
||||
else:
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='internal.invalidAddressErrorText'),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
|
||||
def popup_menu_closing(self, popup_window: PopupWindow) -> None:
|
||||
"""Called when the popup is closing."""
|
||||
|
||||
@ -481,7 +561,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
|
||||
@ -510,9 +591,17 @@ class PartyWindow(bui.Window):
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close the window."""
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_scale')
|
||||
|
||||
def close_with_sound(self) -> None:
|
||||
"""Close the window and make a lovely sound."""
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.getsound('swish').play()
|
||||
self.close()
|
||||
|
||||
@ -32,8 +32,8 @@ class PlayWindow(bui.Window):
|
||||
self._is_main_menu = not bui.app.ui_v1.selecting_private_party_playlist
|
||||
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
width = 1000 if uiscale is bui.UIScale.SMALL else 800
|
||||
x_offs = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
width = 1100 if uiscale is bui.UIScale.SMALL else 800
|
||||
x_offs = 150 if uiscale is bui.UIScale.SMALL else 0
|
||||
height = 550
|
||||
button_width = 400
|
||||
|
||||
@ -521,13 +521,19 @@ class PlayWindow(bui.Window):
|
||||
|
||||
def _back(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._is_main_menu:
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
self._save_state()
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -538,7 +544,8 @@ class PlayWindow(bui.Window):
|
||||
self._save_state()
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(transition='in_left').get_root_widget()
|
||||
GatherWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -549,6 +556,10 @@ class PlayWindow(bui.Window):
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -559,26 +570,38 @@ class PlayWindow(bui.Window):
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
CoopBrowserWindow(origin_widget=self._coop_button).get_root_widget()
|
||||
CoopBrowserWindow(
|
||||
origin_widget=self._coop_button
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _team_tourney(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist.browser import PlaylistBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistBrowserWindow(
|
||||
origin_widget=self._teams_button, sessiontype=bs.DualTeamSession
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _free_for_all(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist.browser import PlaylistBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
@ -586,7 +609,8 @@ class PlayWindow(bui.Window):
|
||||
PlaylistBrowserWindow(
|
||||
origin_widget=self._free_for_all_button,
|
||||
sessiontype=bs.FreeForAllSession,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _draw_dude(
|
||||
|
||||
@ -198,7 +198,7 @@ class PlaylistAddGameWindow(bui.Window):
|
||||
txt = bui.textwidget(
|
||||
parent=self._column,
|
||||
position=(0, 0),
|
||||
size=(self._width - 88, 24),
|
||||
size=(self._scroll_width * 1.1, 24),
|
||||
text=gametype.get_display_string(),
|
||||
h_align='left',
|
||||
v_align='center',
|
||||
|
||||
@ -62,8 +62,8 @@ class PlaylistBrowserWindow(bui.Window):
|
||||
)
|
||||
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 900.0 if uiscale is bui.UIScale.SMALL else 800.0
|
||||
x_inset = 50 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._width = 1100.0 if uiscale is bui.UIScale.SMALL else 800.0
|
||||
x_inset = 150 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._height = (
|
||||
480
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -684,6 +684,10 @@ class PlaylistBrowserWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow,
|
||||
)
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
@ -691,13 +695,18 @@ class PlaylistBrowserWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow(
|
||||
origin_widget=self._customize_button,
|
||||
sessiontype=self._sessiontype,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_back_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.play import PlayWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Store our selected playlist if that's changed.
|
||||
if self._selected_playlist is not None:
|
||||
prev_sel = bui.app.config.get(
|
||||
@ -716,7 +725,8 @@ class PlaylistBrowserWindow(bui.Window):
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlayWindow(transition='in_left').get_root_widget()
|
||||
PlayWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -47,8 +47,8 @@ class PlaylistCustomizeBrowserWindow(bui.Window):
|
||||
self._r = 'gameListWindow'
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 750.0 if uiscale is bui.UIScale.SMALL else 650.0
|
||||
x_inset = 50.0 if uiscale is bui.UIScale.SMALL else 0.0
|
||||
self._width = 850.0 if uiscale is bui.UIScale.SMALL else 650.0
|
||||
x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
|
||||
self._height = (
|
||||
380.0
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -323,6 +323,10 @@ class PlaylistCustomizeBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist import browser
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._selected_playlist_name is not None:
|
||||
cfg = bui.app.config
|
||||
cfg[
|
||||
@ -337,7 +341,8 @@ class PlaylistCustomizeBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
browser.PlaylistBrowserWindow(
|
||||
transition='in_left', sessiontype=self._sessiontype
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _select(self, name: str, index: int) -> None:
|
||||
|
||||
@ -31,8 +31,8 @@ class PlaylistEditWindow(bui.Window):
|
||||
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 770 if uiscale is bui.UIScale.SMALL else 670
|
||||
x_inset = 50 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._width = 870 if uiscale is bui.UIScale.SMALL else 670
|
||||
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._height = (
|
||||
400
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -283,6 +283,10 @@ class PlaylistEditWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow,
|
||||
)
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.getsound('powerdown01').play()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
@ -293,7 +297,8 @@ class PlaylistEditWindow(bui.Window):
|
||||
select_playlist=(
|
||||
self._editcontroller.get_existing_playlist_name()
|
||||
),
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _add(self) -> None:
|
||||
@ -315,6 +320,10 @@ class PlaylistEditWindow(bui.Window):
|
||||
PlaylistCustomizeBrowserWindow,
|
||||
)
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -380,7 +389,8 @@ class PlaylistEditWindow(bui.Window):
|
||||
transition='in_left',
|
||||
sessiontype=self._editcontroller.get_session_type(),
|
||||
select_playlist=new_name,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_press_with_sound(self) -> None:
|
||||
|
||||
@ -92,7 +92,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition=transition
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=False, # Disable this check.
|
||||
)
|
||||
|
||||
def get_config_name(self) -> str:
|
||||
@ -150,7 +151,8 @@ class PlaylistEditController:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.clear_main_menu_window(transition='out_left')
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistAddGameWindow(editcontroller=self).get_root_widget()
|
||||
PlaylistAddGameWindow(editcontroller=self).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
def edit_game_pressed(self) -> None:
|
||||
@ -175,7 +177,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
def _show_edit_ui(
|
||||
@ -205,7 +208,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
# Otherwise we were adding; go back to the add type choice list.
|
||||
@ -214,7 +218,8 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistAddGameWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
else:
|
||||
# Make sure type is in there.
|
||||
@ -236,5 +241,6 @@ class PlaylistEditController:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PlaylistEditWindow(
|
||||
editcontroller=self, transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
|
||||
@ -103,8 +103,8 @@ class PlaylistEditGameWindow(bui.Window):
|
||||
self._choice_selections: dict[str, int] = {}
|
||||
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
width = 720 if uiscale is bui.UIScale.SMALL else 620
|
||||
x_inset = 50 if uiscale is bui.UIScale.SMALL else 0
|
||||
width = 820 if uiscale is bui.UIScale.SMALL else 620
|
||||
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
height = (
|
||||
365
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -514,6 +514,10 @@ class PlaylistEditGameWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.playlist.mapselect import PlaylistMapSelectWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Replace ourself with the map-select UI.
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
@ -524,7 +528,8 @@ class PlaylistEditGameWindow(bui.Window):
|
||||
copy.deepcopy(self._getconfig()),
|
||||
self._edit_info,
|
||||
self._completion_call,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _choice_inc(
|
||||
|
||||
@ -44,8 +44,8 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
width = 715 if uiscale is bui.UIScale.SMALL else 615
|
||||
x_inset = 50 if uiscale is bui.UIScale.SMALL else 0
|
||||
width = 815 if uiscale is bui.UIScale.SMALL else 615
|
||||
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
height = (
|
||||
400
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -273,6 +273,10 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
def _select(self, map_name: str) -> None:
|
||||
from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._config['settings']['map'] = map_name
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
@ -285,7 +289,8 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
default_selection='map',
|
||||
transition='in_left',
|
||||
edit_info=self._edit_info,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _select_with_delay(self, map_name: str) -> None:
|
||||
@ -296,6 +301,10 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
@ -307,5 +316,6 @@ class PlaylistMapSelectWindow(bui.Window):
|
||||
default_selection='map',
|
||||
transition='in_left',
|
||||
edit_info=self._edit_info,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -140,7 +140,6 @@ class PlayOptionsWindow(PopupWindow):
|
||||
if show_shuffle_check_box:
|
||||
self._height += 40
|
||||
|
||||
# Creates our _root_widget.
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
scale = (
|
||||
1.69
|
||||
@ -149,6 +148,7 @@ class PlayOptionsWindow(PopupWindow):
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else 0.85
|
||||
)
|
||||
# Creates our _root_widget.
|
||||
super().__init__(
|
||||
position=scale_origin, size=(self._width, self._height), scale=scale
|
||||
)
|
||||
@ -448,6 +448,10 @@ class PlayOptionsWindow(PopupWindow):
|
||||
self._transition_out()
|
||||
|
||||
def _on_ok_press(self) -> None:
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self.root_widget or self.root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Disallow if our playlist has disappeared.
|
||||
if not self._does_target_playlist_exist():
|
||||
return
|
||||
@ -478,8 +482,12 @@ class PlayOptionsWindow(PopupWindow):
|
||||
cfg['Private Party Host Session Type'] = typename
|
||||
bui.getsound('gunCocking').play()
|
||||
assert bui.app.classic is not None
|
||||
# Note: this is a wonky situation where we aren't actually
|
||||
# the main window but we set it on behalf of the main window
|
||||
# that popped us up.
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GatherWindow(transition='in_right').get_root_widget()
|
||||
GatherWindow(transition='in_right').get_root_widget(),
|
||||
from_window=False, # Disable this test.
|
||||
)
|
||||
self._transition_out(transition='out_left')
|
||||
if self._delegate is not None:
|
||||
|
||||
@ -33,8 +33,8 @@ class ProfileBrowserWindow(bui.Window):
|
||||
back_label = bui.Lstr(resource='doneText')
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 700.0 if uiscale is bui.UIScale.SMALL else 600.0
|
||||
x_inset = 50.0 if uiscale is bui.UIScale.SMALL else 0.0
|
||||
self._width = 800.0 if uiscale is bui.UIScale.SMALL else 600.0
|
||||
x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
|
||||
self._height = (
|
||||
360.0
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -197,8 +197,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, selected_child=self._scrollwidget
|
||||
)
|
||||
self._columnwidget = bui.columnwidget(
|
||||
parent=self._scrollwidget, border=2, margin=0
|
||||
self._subcontainer = bui.containerwidget(
|
||||
parent=self._scrollwidget,
|
||||
size=(self._scroll_width, 32),
|
||||
background=False,
|
||||
)
|
||||
v -= 255
|
||||
self._profiles: dict[str, dict[str, Any]] | None = None
|
||||
@ -212,6 +214,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
from bauiv1lib.profile.edit import EditProfileWindow
|
||||
from bauiv1lib.purchase import PurchaseWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -252,7 +258,8 @@ class ProfileBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
EditProfileWindow(
|
||||
existing_profile=None, in_main_menu=self._in_main_menu
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget if self._in_main_menu else False,
|
||||
)
|
||||
|
||||
def _delete_profile(self) -> None:
|
||||
@ -301,6 +308,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.profile.edit import EditProfileWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
if self._selected_profile is None:
|
||||
bui.getsound('error').play()
|
||||
bui.screenmessage(
|
||||
@ -313,7 +324,8 @@ class ProfileBrowserWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
EditProfileWindow(
|
||||
self._selected_profile, in_main_menu=self._in_main_menu
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget if self._in_main_menu else False,
|
||||
)
|
||||
|
||||
def _select(self, name: str, index: int) -> None:
|
||||
@ -324,6 +336,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account.settings import AccountSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
|
||||
self._save_state()
|
||||
@ -333,7 +349,8 @@ class ProfileBrowserWindow(bui.Window):
|
||||
if self._in_main_menu:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AccountSettingsWindow(transition='in_left').get_root_widget()
|
||||
AccountSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
# If we're being called up standalone, handle pause/resume ourself.
|
||||
@ -342,8 +359,10 @@ class ProfileBrowserWindow(bui.Window):
|
||||
|
||||
def _refresh(self) -> None:
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
from efro.util import asserttype
|
||||
from bascenev1 import PlayerProfilesChangedMessage
|
||||
from bascenev1lib.actor import spazappearance
|
||||
|
||||
assert bui.app.classic is not None
|
||||
|
||||
@ -359,14 +378,27 @@ class ProfileBrowserWindow(bui.Window):
|
||||
assert self._profiles is not None
|
||||
items = list(self._profiles.items())
|
||||
items.sort(key=lambda x: asserttype(x[0], str).lower())
|
||||
spazzes = spazappearance.get_appearances()
|
||||
spazzes.sort()
|
||||
icon_textures = [
|
||||
bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
|
||||
for s in spazzes
|
||||
]
|
||||
icon_tint_textures = [
|
||||
bui.gettexture(
|
||||
bui.app.classic.spaz_appearances[s].icon_mask_texture
|
||||
)
|
||||
for s in spazzes
|
||||
]
|
||||
index = 0
|
||||
y_val = 35 * (len(self._profiles) - 1)
|
||||
account_name: str | None
|
||||
if plus.get_v1_account_state() == 'signed_in':
|
||||
account_name = plus.get_v1_account_display_string()
|
||||
else:
|
||||
account_name = None
|
||||
widget_to_select = None
|
||||
for p_name, _ in items:
|
||||
for p_name, p_info in items:
|
||||
if p_name == '__account__' and account_name is None:
|
||||
continue
|
||||
color, _highlight = bui.app.classic.get_player_profile_colors(
|
||||
@ -378,16 +410,35 @@ class ProfileBrowserWindow(bui.Window):
|
||||
if p_name == '__account__'
|
||||
else bui.app.classic.get_player_profile_icon(p_name) + p_name
|
||||
)
|
||||
|
||||
try:
|
||||
char_index = spazzes.index(p_info['character'])
|
||||
except Exception:
|
||||
char_index = spazzes.index('Spaz')
|
||||
|
||||
assert isinstance(tval, str)
|
||||
character = bui.buttonwidget(
|
||||
parent=self._subcontainer,
|
||||
position=(0, y_val),
|
||||
size=(28, 28),
|
||||
label='',
|
||||
color=(1, 1, 1),
|
||||
mask_texture=bui.gettexture('characterIconMask'),
|
||||
tint_color=color,
|
||||
tint2_color=_highlight,
|
||||
texture=icon_textures[char_index],
|
||||
tint_texture=icon_tint_textures[char_index],
|
||||
selectable=False,
|
||||
)
|
||||
txtw = bui.textwidget(
|
||||
parent=self._columnwidget,
|
||||
position=(0, 32),
|
||||
size=((self._width - 40) / scl, 28),
|
||||
parent=self._subcontainer,
|
||||
position=(35, y_val),
|
||||
size=((self._width - 210) / scl, 28),
|
||||
text=bui.Lstr(value=tval),
|
||||
h_align='left',
|
||||
v_align='center',
|
||||
on_select_call=bui.WeakCall(self._select, p_name, index),
|
||||
maxwidth=self._scroll_width * 0.92,
|
||||
maxwidth=self._scroll_width * 0.86,
|
||||
corner_scale=scl,
|
||||
color=bui.safecolor(color, 0.4),
|
||||
always_highlight=True,
|
||||
@ -396,8 +447,11 @@ class ProfileBrowserWindow(bui.Window):
|
||||
)
|
||||
if index == 0:
|
||||
bui.widget(edit=txtw, up_widget=self._back_button)
|
||||
if self._selected_profile is None:
|
||||
self._selected_profile = p_name
|
||||
bui.widget(edit=txtw, show_buffer_top=40, show_buffer_bottom=40)
|
||||
self._profile_widgets.append(txtw)
|
||||
self._profile_widgets.append(character)
|
||||
|
||||
# Select/show this one if it was previously selected
|
||||
# (but defer till after this loop since our height is
|
||||
@ -406,10 +460,15 @@ class ProfileBrowserWindow(bui.Window):
|
||||
widget_to_select = txtw
|
||||
|
||||
index += 1
|
||||
y_val -= 35
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._subcontainer,
|
||||
size=(self._scroll_width, index * 35),
|
||||
)
|
||||
if widget_to_select is not None:
|
||||
bui.columnwidget(
|
||||
edit=self._columnwidget,
|
||||
bui.containerwidget(
|
||||
edit=self._subcontainer,
|
||||
selected_child=widget_to_select,
|
||||
visible_child=widget_to_select,
|
||||
)
|
||||
|
||||
@ -18,12 +18,18 @@ class EditProfileWindow(bui.Window):
|
||||
# FIXME: WILL NEED TO CHANGE THIS FOR UILOCATION.
|
||||
def reload_window(self) -> None:
|
||||
"""Transitions out and recreates ourself."""
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
EditProfileWindow(
|
||||
self.getname(), self._in_main_menu
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@ -54,8 +60,8 @@ class EditProfileWindow(bui.Window):
|
||||
self._highlight,
|
||||
) = bui.app.classic.get_player_profile_colors(existing_profile)
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = width = 780.0 if uiscale is bui.UIScale.SMALL else 680.0
|
||||
self._x_inset = x_inset = 50.0 if uiscale is bui.UIScale.SMALL else 0.0
|
||||
self._width = width = 880.0 if uiscale is bui.UIScale.SMALL else 680.0
|
||||
self._x_inset = x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
|
||||
self._height = height = (
|
||||
350.0
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -184,7 +190,7 @@ class EditProfileWindow(bui.Window):
|
||||
self._clipped_name_text = bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
text='',
|
||||
position=(540 + x_inset, v - 8),
|
||||
position=(580 + x_inset, v - 8),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
scale=0.55,
|
||||
@ -390,6 +396,16 @@ class EditProfileWindow(bui.Window):
|
||||
autoselect=True,
|
||||
on_activate_call=self.upgrade_profile,
|
||||
)
|
||||
self._random_name_button = bui.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
label=bui.Lstr(resource='randomText'),
|
||||
size=(30, 20),
|
||||
position=(495 + x_inset, v - 20),
|
||||
button_type='square',
|
||||
color=(0.6, 0.5, 0.65),
|
||||
autoselect=True,
|
||||
on_activate_call=self.assign_random_name,
|
||||
)
|
||||
|
||||
self._update_clipped_name()
|
||||
self._clipped_name_timer = bui.AppTimer(
|
||||
@ -498,8 +514,17 @@ class EditProfileWindow(bui.Window):
|
||||
)
|
||||
self._update_character()
|
||||
|
||||
def assign_random_name(self) -> None:
|
||||
"""Assigning a random name to the player."""
|
||||
names = bs.get_random_names()
|
||||
name = names[random.randrange(len(names))]
|
||||
bui.textwidget(
|
||||
edit=self._text_field,
|
||||
text=name,
|
||||
)
|
||||
|
||||
def upgrade_profile(self) -> None:
|
||||
"""Attempt to ugrade the profile to global."""
|
||||
"""Attempt to upgrade the profile to global."""
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.profile import upgrade as pupgrade
|
||||
|
||||
@ -653,6 +678,10 @@ class EditProfileWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.profile.browser import ProfileBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
@ -660,7 +689,8 @@ class EditProfileWindow(bui.Window):
|
||||
'in_left',
|
||||
selected_profile=self._existing_profile,
|
||||
in_main_menu=self._in_main_menu,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _set_color(self, color: tuple[float, float, float]) -> None:
|
||||
@ -759,6 +789,10 @@ class EditProfileWindow(bui.Window):
|
||||
"""Save has been selected."""
|
||||
from bauiv1lib.profile.browser import ProfileBrowserWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return False
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -808,6 +842,7 @@ class EditProfileWindow(bui.Window):
|
||||
'in_left',
|
||||
selected_profile=new_name,
|
||||
in_main_menu=self._in_main_menu,
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
return True
|
||||
|
||||
@ -26,7 +26,7 @@ class PromoCodeWindow(bui.Window):
|
||||
transition = 'in_right'
|
||||
|
||||
width = 450
|
||||
height = 230
|
||||
height = 330
|
||||
|
||||
self._modal = modal
|
||||
self._r = 'promoCodeWindow'
|
||||
@ -62,17 +62,50 @@ class PromoCodeWindow(bui.Window):
|
||||
iconscale=1.2,
|
||||
)
|
||||
|
||||
v = height - 74
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
text=bui.Lstr(resource='codesExplainText'),
|
||||
maxwidth=width * 0.9,
|
||||
position=(width * 0.5, v),
|
||||
color=(0.7, 0.7, 0.7, 1.0),
|
||||
size=(0, 0),
|
||||
scale=0.8,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
)
|
||||
v -= 60
|
||||
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
text=bui.Lstr(
|
||||
resource='supportEmailText',
|
||||
subs=[('${EMAIL}', 'support@froemling.net')],
|
||||
),
|
||||
maxwidth=width * 0.9,
|
||||
position=(width * 0.5, v),
|
||||
color=(0.7, 0.7, 0.7, 1.0),
|
||||
size=(0, 0),
|
||||
scale=0.65,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
)
|
||||
|
||||
v -= 80
|
||||
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
text=bui.Lstr(resource=self._r + '.codeText'),
|
||||
position=(22, height - 113),
|
||||
position=(22, v),
|
||||
color=(0.8, 0.8, 0.8, 1.0),
|
||||
size=(90, 30),
|
||||
h_align='right',
|
||||
)
|
||||
v -= 8
|
||||
|
||||
self._text_field = bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(125, height - 121),
|
||||
position=(125, v),
|
||||
size=(280, 46),
|
||||
text='',
|
||||
h_align='left',
|
||||
@ -86,10 +119,11 @@ class PromoCodeWindow(bui.Window):
|
||||
)
|
||||
bui.widget(edit=btn, down_widget=self._text_field)
|
||||
|
||||
v -= 79
|
||||
b_width = 200
|
||||
self._enter_button = btn2 = bui.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(width * 0.5 - b_width * 0.5, height - 200),
|
||||
position=(width * 0.5 - b_width * 0.5, v),
|
||||
size=(b_width, 60),
|
||||
scale=1.0,
|
||||
label=bui.Lstr(
|
||||
@ -108,13 +142,18 @@ class PromoCodeWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _activate_enter_button(self) -> None:
|
||||
@ -124,6 +163,10 @@ class PromoCodeWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -133,7 +176,8 @@ class PromoCodeWindow(bui.Window):
|
||||
if not self._modal:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget()
|
||||
AdvancedSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
plus.add_v1_account_transaction(
|
||||
{
|
||||
|
||||
@ -47,8 +47,8 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
scale_origin = None
|
||||
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
self._width = 870.0 if uiscale is bui.UIScale.SMALL else 670.0
|
||||
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._width = 970.0 if uiscale is bui.UIScale.SMALL else 670.0
|
||||
x_inset = 150 if uiscale is bui.UIScale.SMALL else 0
|
||||
self._height = (
|
||||
390.0
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
@ -682,11 +682,16 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
def _on_vr_test_press(self) -> None:
|
||||
from bauiv1lib.settings.vrtesting import VRTestingWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
VRTestingWindow(transition='in_right').get_root_widget()
|
||||
VRTestingWindow(transition='in_right').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_net_test_press(self) -> None:
|
||||
@ -694,6 +699,10 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
assert plus is not None
|
||||
from bauiv1lib.settings.nettesting import NetTestingWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Net-testing requires a signed in v1 account.
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
bui.screenmessage(
|
||||
@ -706,7 +715,8 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
NetTestingWindow(transition='in_right').get_root_widget()
|
||||
NetTestingWindow(transition='in_right').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_friend_promo_code_press(self) -> None:
|
||||
@ -724,17 +734,26 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
def _on_plugins_button_press(self) -> None:
|
||||
from bauiv1lib.settings.plugins import PluginWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PluginWindow(origin_widget=self._plugins_button).get_root_widget()
|
||||
PluginWindow(origin_widget=self._plugins_button).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_promo_code_press(self) -> None:
|
||||
from bauiv1lib.promocode import PromoCodeWindow
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -742,23 +761,30 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
PromoCodeWindow(
|
||||
origin_widget=self._promo_code_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _on_benchmark_press(self) -> None:
|
||||
from bauiv1lib.debug import DebugWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
DebugWindow(transition='in_right').get_root_widget()
|
||||
DebugWindow(transition='in_right').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
@ -807,6 +833,8 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
sel_name = 'ModdingGuide'
|
||||
elif sel == self._language_inform_checkbox:
|
||||
sel_name = 'LangInform'
|
||||
elif sel == self._show_dev_console_button_check_box.widget:
|
||||
sel_name = 'ShowDevConsole'
|
||||
else:
|
||||
raise ValueError(f'unrecognized selection \'{sel}\'')
|
||||
elif sel == self._back_button:
|
||||
@ -870,6 +898,8 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
sel = self._modding_guide_button
|
||||
elif sel_name == 'LangInform':
|
||||
sel = self._language_inform_checkbox
|
||||
elif sel_name == 'ShowDevConsole':
|
||||
sel = self._show_dev_console_button_check_box.widget
|
||||
else:
|
||||
sel = None
|
||||
if sel is not None:
|
||||
@ -904,11 +934,16 @@ class AdvancedSettingsWindow(bui.Window):
|
||||
def _do_back(self) -> None:
|
||||
from bauiv1lib.settings.allsettings import AllSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AllSettingsWindow(transition='in_left').get_root_widget()
|
||||
AllSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -40,8 +40,8 @@ class AllSettingsWindow(bui.Window):
|
||||
scale_origin = None
|
||||
assert bui.app.classic is not None
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
width = 900 if uiscale is bui.UIScale.SMALL else 580
|
||||
x_inset = 75 if uiscale is bui.UIScale.SMALL else 0
|
||||
width = 1000 if uiscale is bui.UIScale.SMALL else 580
|
||||
x_inset = 125 if uiscale is bui.UIScale.SMALL else 0
|
||||
height = 435
|
||||
self._r = 'settingsWindow'
|
||||
top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
|
||||
@ -235,65 +235,90 @@ class AllSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.mainmenu import MainMenuWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget()
|
||||
MainMenuWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_controllers(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(
|
||||
origin_widget=self._controllers_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_graphics(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.graphics import GraphicsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GraphicsSettingsWindow(
|
||||
origin_widget=self._graphics_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_audio(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.audio import AudioSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AudioSettingsWindow(
|
||||
origin_widget=self._audio_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_advanced(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.advanced import AdvancedSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AdvancedSettingsWindow(
|
||||
origin_widget=self._advanced_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -121,7 +121,8 @@ class AudioSettingsWindow(bui.Window):
|
||||
displayname=bui.Lstr(resource=self._r + '.soundVolumeText'),
|
||||
minval=0.0,
|
||||
maxval=1.0,
|
||||
increment=0.1,
|
||||
increment=0.05,
|
||||
as_percent=True,
|
||||
)
|
||||
if bui.app.ui_v1.use_toolbars:
|
||||
bui.widget(
|
||||
@ -137,9 +138,10 @@ class AudioSettingsWindow(bui.Window):
|
||||
displayname=bui.Lstr(resource=self._r + '.musicVolumeText'),
|
||||
minval=0.0,
|
||||
maxval=1.0,
|
||||
increment=0.1,
|
||||
increment=0.05,
|
||||
callback=music.music_volume_changed,
|
||||
changesound=False,
|
||||
as_percent=True,
|
||||
)
|
||||
|
||||
v -= 0.5 * spacing
|
||||
@ -235,6 +237,10 @@ class AudioSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.soundtrack import browser as stb
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# We require disk access for soundtracks;
|
||||
# if we don't have it, request it.
|
||||
if not bui.have_permission(bui.Permission.STORAGE):
|
||||
@ -254,13 +260,18 @@ class AudioSettingsWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
stb.SoundtrackBrowserWindow(
|
||||
origin_widget=self._soundtrack_button
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _back(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings import allsettings
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
@ -269,7 +280,8 @@ class AudioSettingsWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
allsettings.AllSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
|
||||
@ -98,9 +98,11 @@ class ControlsSettingsWindow(bui.Window):
|
||||
# made-for-iOS/Mac systems
|
||||
# (we can run into problems where devices register as one of each
|
||||
# type otherwise)..
|
||||
# UPDATE: We always use the apple system these days (which should
|
||||
# support older controllers). So no need for a switch.
|
||||
show_mac_controller_subsystem = False
|
||||
if platform == 'mac' and bui.is_xcode_build():
|
||||
show_mac_controller_subsystem = True
|
||||
# if platform == 'mac' and bui.is_xcode_build():
|
||||
# show_mac_controller_subsystem = True
|
||||
|
||||
if show_mac_controller_subsystem:
|
||||
height += spacing * 1.5
|
||||
@ -311,6 +313,7 @@ class ControlsSettingsWindow(bui.Window):
|
||||
maxwidth=width * 0.8,
|
||||
)
|
||||
v -= spacing
|
||||
|
||||
if show_mac_controller_subsystem:
|
||||
PopupMenu(
|
||||
parent=self._root_widget,
|
||||
@ -364,59 +367,84 @@ class ControlsSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ConfigKeyboardWindow(
|
||||
bs.getinputdevice('Keyboard', '#1')
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _config_keyboard2(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ConfigKeyboardWindow(
|
||||
bs.getinputdevice('Keyboard', '#2')
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_mobile_devices(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.remoteapp import RemoteAppSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
RemoteAppSettingsWindow().get_root_widget()
|
||||
RemoteAppSettingsWindow().get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_gamepads(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.gamepadselect import GamepadSelectWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
GamepadSelectWindow().get_root_widget()
|
||||
GamepadSelectWindow().get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _do_touchscreen(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.touchscreen import TouchscreenSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
TouchscreenSettingsWindow().get_root_widget()
|
||||
TouchscreenSettingsWindow().get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save_state(self) -> None:
|
||||
@ -463,11 +491,16 @@ class ControlsSettingsWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.settings.allsettings import AllSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
self._save_state()
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
AllSettingsWindow(transition='in_left').get_root_widget()
|
||||
AllSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -545,20 +545,24 @@ class GamepadSettingsWindow(bui.Window):
|
||||
if 'analogStickLR' + self._ext in self._settings
|
||||
else 5
|
||||
if self._is_secondary
|
||||
else 1
|
||||
else None
|
||||
)
|
||||
sval2 = (
|
||||
self._settings['analogStickUD' + self._ext]
|
||||
if 'analogStickUD' + self._ext in self._settings
|
||||
else 6
|
||||
if self._is_secondary
|
||||
else 2
|
||||
)
|
||||
return (
|
||||
self._input.get_axis_name(sval1)
|
||||
+ ' / '
|
||||
+ self._input.get_axis_name(sval2)
|
||||
else None
|
||||
)
|
||||
assert isinstance(sval1, (int, type(None)))
|
||||
assert isinstance(sval2, (int, type(None)))
|
||||
if sval1 is not None and sval2 is not None:
|
||||
return (
|
||||
self._input.get_axis_name(sval1)
|
||||
+ ' / '
|
||||
+ self._input.get_axis_name(sval2)
|
||||
)
|
||||
return bui.Lstr(resource=self._r + '.unsetText')
|
||||
|
||||
# If they're looking for triggers.
|
||||
if control in ['triggerRun1' + self._ext, 'triggerRun2' + self._ext]:
|
||||
@ -573,7 +577,7 @@ class GamepadSettingsWindow(bui.Window):
|
||||
return str(1.0)
|
||||
|
||||
# For dpad buttons: show individual buttons if any are set.
|
||||
# Otherwise show whichever dpad is set (defaulting to 1).
|
||||
# Otherwise show whichever dpad is set.
|
||||
dpad_buttons = [
|
||||
'buttonLeft' + self._ext,
|
||||
'buttonRight' + self._ext,
|
||||
@ -588,24 +592,28 @@ class GamepadSettingsWindow(bui.Window):
|
||||
return bui.Lstr(resource=self._r + '.unsetText')
|
||||
|
||||
# No dpad buttons - show the dpad number for all 4.
|
||||
return bui.Lstr(
|
||||
value='${A} ${B}',
|
||||
subs=[
|
||||
('${A}', bui.Lstr(resource=self._r + '.dpadText')),
|
||||
(
|
||||
'${B}',
|
||||
str(
|
||||
self._settings['dpad' + self._ext]
|
||||
if 'dpad' + self._ext in self._settings
|
||||
else 2
|
||||
if self._is_secondary
|
||||
else 1
|
||||
),
|
||||
),
|
||||
],
|
||||
dpadnum = (
|
||||
self._settings['dpad' + self._ext]
|
||||
if 'dpad' + self._ext in self._settings
|
||||
else 2
|
||||
if self._is_secondary
|
||||
else None
|
||||
)
|
||||
assert isinstance(dpadnum, (int, type(None)))
|
||||
if dpadnum is not None:
|
||||
return bui.Lstr(
|
||||
value='${A} ${B}',
|
||||
subs=[
|
||||
('${A}', bui.Lstr(resource=self._r + '.dpadText')),
|
||||
(
|
||||
'${B}',
|
||||
str(dpadnum),
|
||||
),
|
||||
],
|
||||
)
|
||||
return bui.Lstr(resource=self._r + '.unsetText')
|
||||
|
||||
# other buttons..
|
||||
# Other buttons.
|
||||
if control in self._settings:
|
||||
return self._input.get_button_name(self._settings[control])
|
||||
return bui.Lstr(resource=self._r + '.unsetText')
|
||||
@ -616,9 +624,7 @@ class GamepadSettingsWindow(bui.Window):
|
||||
event: dict[str, Any],
|
||||
dialog: AwaitGamepadInputWindow,
|
||||
) -> None:
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
assert self._settings is not None
|
||||
ext = self._ext
|
||||
|
||||
@ -648,10 +654,6 @@ class GamepadSettingsWindow(bui.Window):
|
||||
if btn in self._settings:
|
||||
del self._settings[btn]
|
||||
if event['hat'] == (2 if self._is_secondary else 1):
|
||||
# Exclude value in default case.
|
||||
if 'dpad' + ext in self._settings:
|
||||
del self._settings['dpad' + ext]
|
||||
else:
|
||||
self._settings['dpad' + ext] = event['hat']
|
||||
|
||||
# Update the 4 dpad button txt widgets.
|
||||
@ -680,10 +682,6 @@ class GamepadSettingsWindow(bui.Window):
|
||||
if abs(event['value']) > 0.5:
|
||||
axis = event['axis']
|
||||
if axis == (5 if self._is_secondary else 1):
|
||||
# Exclude value in default case.
|
||||
if 'analogStickLR' + ext in self._settings:
|
||||
del self._settings['analogStickLR' + ext]
|
||||
else:
|
||||
self._settings['analogStickLR' + ext] = axis
|
||||
bui.textwidget(
|
||||
edit=self._textwidgets['analogStickLR' + ext],
|
||||
@ -713,10 +711,6 @@ class GamepadSettingsWindow(bui.Window):
|
||||
lr_axis = 5 if self._is_secondary else 1
|
||||
if axis != lr_axis:
|
||||
if axis == (6 if self._is_secondary else 2):
|
||||
# Exclude value in default case.
|
||||
if 'analogStickUD' + ext in self._settings:
|
||||
del self._settings['analogStickUD' + ext]
|
||||
else:
|
||||
self._settings['analogStickUD' + ext] = axis
|
||||
bui.textwidget(
|
||||
edit=self._textwidgets['analogStickLR' + ext],
|
||||
@ -795,25 +789,34 @@ class GamepadSettingsWindow(bui.Window):
|
||||
),
|
||||
)
|
||||
|
||||
bui.apptimer(0, doit)
|
||||
bui.pushcall(doit)
|
||||
return btn
|
||||
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
if self._is_main_menu:
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save(self) -> None:
|
||||
classic = bui.app.classic
|
||||
assert classic is not None
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, transition=self._transition_out
|
||||
)
|
||||
@ -858,7 +861,8 @@ class GamepadSettingsWindow(bui.Window):
|
||||
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -452,7 +452,7 @@ class GamepadAdvancedSettingsWindow(bui.Window):
|
||||
),
|
||||
)
|
||||
|
||||
bui.apptimer(0, doit)
|
||||
bui.pushcall(doit)
|
||||
return btn, btn2
|
||||
|
||||
def _inc(
|
||||
|
||||
@ -29,16 +29,17 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
logging.exception('Error transitioning out main_menu_window.')
|
||||
bui.getsound('activateBeep').play()
|
||||
bui.getsound('swish').play()
|
||||
inputdevice = event['input_device']
|
||||
assert isinstance(inputdevice, bs.InputDevice)
|
||||
if inputdevice.allows_configuring:
|
||||
device = event['input_device']
|
||||
assert isinstance(device, bs.InputDevice)
|
||||
if device.allows_configuring:
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
gamepad.GamepadSettingsWindow(inputdevice).get_root_widget()
|
||||
gamepad.GamepadSettingsWindow(device).get_root_widget(),
|
||||
from_window=None,
|
||||
)
|
||||
else:
|
||||
width = 700
|
||||
height = 200
|
||||
button_width = 100
|
||||
button_width = 80
|
||||
uiscale = bui.app.ui_v1.uiscale
|
||||
dlg = bui.containerwidget(
|
||||
scale=(
|
||||
@ -51,9 +52,14 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
size=(width, height),
|
||||
transition='in_right',
|
||||
)
|
||||
bui.app.ui_v1.set_main_menu_window(dlg)
|
||||
device_name = inputdevice.name
|
||||
if device_name == 'iDevice':
|
||||
bui.app.ui_v1.set_main_menu_window(dlg, from_window=None)
|
||||
|
||||
if device.allows_configuring_in_system_settings:
|
||||
msg = bui.Lstr(
|
||||
resource='configureDeviceInSystemSettingsText',
|
||||
subs=[('${DEVICE}', device.name)],
|
||||
)
|
||||
elif device.is_controller_app:
|
||||
msg = bui.Lstr(
|
||||
resource='bsRemoteConfigureInAppText',
|
||||
subs=[('${REMOTE_APP_NAME}', bui.get_remote_app_name())],
|
||||
@ -61,7 +67,7 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
else:
|
||||
msg = bui.Lstr(
|
||||
resource='cantConfigureDeviceText',
|
||||
subs=[('${DEVICE}', device_name)],
|
||||
subs=[('${DEVICE}', device.name)],
|
||||
)
|
||||
bui.textwidget(
|
||||
parent=dlg,
|
||||
@ -76,12 +82,17 @@ def gamepad_configure_callback(event: dict[str, Any]) -> None:
|
||||
def _ok() -> None:
|
||||
from bauiv1lib.settings import controls
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not dlg or dlg.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=dlg, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
controls.ControlsSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=dlg,
|
||||
)
|
||||
|
||||
bui.buttonwidget(
|
||||
@ -186,11 +197,16 @@ class GamepadSelectWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.settings import controls
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bs.release_gamepad_input()
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
controls.ControlsSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
@ -52,7 +52,7 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
self._show_fullscreen = False
|
||||
fullscreen_spacing_top = spacing * 0.2
|
||||
fullscreen_spacing = spacing * 1.2
|
||||
if bui.can_toggle_fullscreen():
|
||||
if bui.fullscreen_control_available():
|
||||
self._show_fullscreen = True
|
||||
height += fullscreen_spacing + fullscreen_spacing_top
|
||||
|
||||
@ -122,21 +122,29 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
self._fullscreen_checkbox: bui.Widget | None = None
|
||||
if self._show_fullscreen:
|
||||
v -= fullscreen_spacing_top
|
||||
self._fullscreen_checkbox = ConfigCheckBox(
|
||||
# Fullscreen control does not necessarily talk to the
|
||||
# app config so we have to wrangle it manually instead of
|
||||
# using a config-checkbox.
|
||||
label = bui.Lstr(resource=f'{self._r}.fullScreenText')
|
||||
|
||||
# Show keyboard shortcut alongside the control if they
|
||||
# provide one.
|
||||
shortcut = bui.fullscreen_control_key_shortcut()
|
||||
if shortcut is not None:
|
||||
label = bui.Lstr(
|
||||
value='$(NAME) [$(SHORTCUT)]',
|
||||
subs=[('$(NAME)', label), ('$(SHORTCUT)', shortcut)],
|
||||
)
|
||||
self._fullscreen_checkbox = bui.checkboxwidget(
|
||||
parent=self._root_widget,
|
||||
position=(100, v),
|
||||
maxwidth=200,
|
||||
value=bui.fullscreen_control_get(),
|
||||
on_value_change_call=bui.fullscreen_control_set,
|
||||
maxwidth=250,
|
||||
size=(300, 30),
|
||||
configkey='Fullscreen',
|
||||
displayname=bui.Lstr(
|
||||
resource=self._r
|
||||
+ (
|
||||
'.fullScreenCmdText'
|
||||
if app.classic.platform == 'mac'
|
||||
else '.fullScreenCtrlText'
|
||||
)
|
||||
),
|
||||
).widget
|
||||
text=label,
|
||||
)
|
||||
|
||||
if not self._have_selected_child:
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget,
|
||||
@ -259,9 +267,7 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
bui.Lstr(resource='nativeText'),
|
||||
]
|
||||
for res in [1440, 1080, 960, 720, 480]:
|
||||
# Nav bar is 72px so lets allow for that in what
|
||||
# choices we show.
|
||||
if native_res[1] >= res - 72:
|
||||
if native_res[1] >= res:
|
||||
res_str = f'{res}p'
|
||||
choices.append(res_str)
|
||||
choices_display.append(bui.Lstr(value=res_str))
|
||||
@ -430,6 +436,10 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
def _back(self) -> None:
|
||||
from bauiv1lib.settings import allsettings
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
# Applying max-fps takes a few moments. Apply if it hasn't been
|
||||
# yet.
|
||||
self._apply_max_fps()
|
||||
@ -441,7 +451,8 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
allsettings.AllSettingsWindow(
|
||||
transition='in_left'
|
||||
).get_root_widget()
|
||||
).get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _set_quality(self, quality: str) -> None:
|
||||
@ -528,8 +539,10 @@ class GraphicsSettingsWindow(bui.Window):
|
||||
and bui.apptime() - self._last_max_fps_set_time > 1.0
|
||||
):
|
||||
self._apply_max_fps()
|
||||
|
||||
if self._show_fullscreen:
|
||||
# Keep the fullscreen checkbox up to date with the current value.
|
||||
bui.checkboxwidget(
|
||||
edit=self._fullscreen_checkbox,
|
||||
value=bui.app.config.resolve('Fullscreen'),
|
||||
value=bui.fullscreen_control_get(),
|
||||
)
|
||||
|
||||
@ -213,6 +213,12 @@ class ConfigKeyboardWindow(bui.Window):
|
||||
scale=1.0,
|
||||
)
|
||||
|
||||
def _pretty_button_name(self, button_name: str) -> bui.Lstr:
|
||||
button_id = self._settings[button_name]
|
||||
if button_id == -1:
|
||||
return bs.Lstr(resource='configGamepadWindow.unsetText')
|
||||
return self._input.get_button_name(button_id)
|
||||
|
||||
def _capture_button(
|
||||
self,
|
||||
pos: tuple[float, float],
|
||||
@ -250,7 +256,7 @@ class ConfigKeyboardWindow(bui.Window):
|
||||
v_align='top',
|
||||
scale=uiscale,
|
||||
maxwidth=maxwidth,
|
||||
text=self._input.get_button_name(self._settings[button]),
|
||||
text=self._pretty_button_name(button),
|
||||
)
|
||||
bui.buttonwidget(
|
||||
edit=btn,
|
||||
@ -265,15 +271,24 @@ class ConfigKeyboardWindow(bui.Window):
|
||||
def _cancel(self) -> None:
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
assert bui.app.classic is not None
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
def _save(self) -> None:
|
||||
from bauiv1lib.settings.controls import ControlsSettingsWindow
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
assert bui.app.classic is not None
|
||||
bui.containerwidget(edit=self._root_widget, transition='out_right')
|
||||
bui.getsound('gunCocking').play()
|
||||
@ -308,7 +323,8 @@ class ConfigKeyboardWindow(bui.Window):
|
||||
)
|
||||
bui.app.config.apply_and_commit()
|
||||
bui.app.ui_v1.set_main_menu_window(
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget()
|
||||
ControlsSettingsWindow(transition='in_left').get_root_widget(),
|
||||
from_window=self._root_widget,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user