Work on Google Play V2 Accounts

This commit is contained in:
Eric 2022-11-15 12:19:59 -08:00
parent 0cbaf28aae
commit c79abc6a74
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
37 changed files with 884 additions and 243 deletions

View File

@ -420,28 +420,28 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/60/ad/38269b7f1c7dc20cb9a506cd0681",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/72/85/d6fc4d16b7081d91fba2850b5b10",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/e9/ae/1d674d0c086eaa0bd1c3b1db0505",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/25/11/cfcf4238fb55433521e26cce0d10",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/e2/24/5e7ea9ca5c9de4d3b7a28e53564d",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/89/ec/d472036fbb09f310891761beb39a",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/b0/05/e530acaba539f040ce61e22561dc",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/61/03/89070ca765e06da3a419a579f503",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/ea/22/bb0950095686a71030c67ac74b3b",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/1f/7f/af259ba9b41556e5e667ad4c646d",
"assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/3c/22/78a56fc40426ab19ad4e76924b78",
"assets/build/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/c9/73/01a1343af814131b1ee96af0b687",
"assets/build/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/61/20/01291c2cb72b22f204730c0d7574",
"assets/build/ba_data/data/languages/danish.json": "https://files.ballistica.net/cache/ba1/6a/fa/fcf4a804beaff927b0f12c179eaa",
"assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/68/93/da8e9874f41a786edf52ba4ccaad",
"assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/df/08/29edc91f648c34c3aa7960e04c13",
"assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/a0/1d/5fbc922d01521142c2a347b1b024",
"assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/4c/c7/0184b8178869d1a3827a1bfcd5bb",
"assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/c7/2e/e0520f58206da01b829e02ff4576",
"assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/e8/84/6c9f123e9a0d82fc595c8f55ac7c",
"assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/8a/09/3e0fa9e44913b53f4dab195d3fae",
"assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/08/15/13981ce51e1e9f974357a9e0a59c",
"assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/5f/51/c15d74d2fe4e88ee1e3db0986500",
"assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/aa/da/dfc8d710af960d7300c7090faeab",
"assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/91/98/42701cd595c2f70b7484614a8f49",
"assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/09/55/b50104638f60636af2263877bb7f",
"assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/d8/f2/aa16bc336bd7660cc86c3264bfc4",
"assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/0d/b4/e225e3838c4b5d9381dcc4594517",
"assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/3a/6b/34714586cb4e9f1b12f8ae54cac8",
"assets/build/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/12/62/862228b229057877e89fb195d41d",
"assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/7c/38/d4a44c481757d355836f292ede48",
"assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/df/b1/b2c9ebaad5e873ebedd365726d3d",
"assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/10/13/1228836444f7557211f0058ef9bd",
"assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/19/e9/59c891b1fb85f3ba9f19283c233d",
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/61/5b/847c03407d1c3a85866833323676",
"assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/d7/06/9d70642d0a4d1e3b1c2149d7a17c",
@ -4003,51 +4003,51 @@
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/1c/77/ac670a5118abdf8a7687af0e159b",
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5d/0f/3592238512deb417b34428b91e09",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/96/68/f540e5eefc5fc496f6531693f635",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a2/3e/3c403004cc2f9e6902bc21ed36e2",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/fb/63/67f2fbaadfd5dd9604c33d3be317",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a5/ec/375c414801b769b64c1848ee0331",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/99/a6/8b421200970caea159fa12be513f",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e3/64/45e980a2cabf12cb97d621fa4b66",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/67/5a/a6455e835d5fa38e66ce2e06c712",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ea/01/04ab6a0bc93eefa1523312420297",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/b6/61/bff5c094a4bc66cbcd3727422687",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2f/d1/780a1948216e46af050245f96d70",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/19/56/b64bef7218b736ceb7cf07a4073c",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f2/e7/7c9bd7adb70893d23b1bb7df31f8",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/9d/f7/3aef724ad3f821687c13edf1b7e0",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/67/41/9e4ab9f852b1cd8f386f2de5a7d3",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/4d/ed/39635f875286955f76eb90729998",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/e6/24/5a90398d108b1887d532cf525682",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/db/ab/7c9126758b5903ea15cd40bad187",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/aa/7e/f1f9ccfb744c00d97cba806bbe0f",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/99/fe/2ddc2fec5207e1a465acd307bc24",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/24/40/c2fb8cdbea5b58242c3b51b52d14",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/20/4e/60e18e1b04c88e1cdd4a0836d710",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a4/1e/dbb500fc6af85da634aeeb81af3e",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e1/4a/75c95d1e0a44e833b866f5a02cbd",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/58/f2/5997aa7d771a67246ba55be7538d",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/57/21/b34020e41aae91cd276efc77a9d2",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/08/c3/cb7fbc13bc7aa3026f4363f6f1d6",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f3/54/5c6f3bfaa7feb59faf2a44945d38",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ad/93/21d395043d633d70eef84502fd9f",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/33/83/67d6444edb230cfc3d9ad7c030be",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e6/81/aff0232cd83ec62aa0ec10716568",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5c/96/aa9f7ad96fa1f70ff80dbfaa6f7e",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/aa/56/194ab27e57149fb6b253bf8d754d",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7e/1d/f330b35ca15bd654333a778b100d",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9a/5b/bcf80b7e94eb7bb1e949c41c58f8",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5e/bc/1a9f73dc64f3ffcc489aa58b6251",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ea/4f/8e568645129f921392345e4b81a3",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/e6/0a/43f0c01db3245e332a6cbb2c04a6",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/7a/33/215327a641ff1d522d3554ec2577",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/46/59/69428486eb6bb6ba752b37a31db1",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/97/d3/716990aa94a4378f91afe137c773",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/c4/fd/7532b2a98bf8c1b0ea68485a9d8e",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/0d/06/a6b7a7ca3834c8a05b538c5f1934",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/46/54/fdb879571df92b8636e880f66207",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/20/fc/441aca31b3c8d5e9dcd0d71f6634",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/76/42/ebdcc0eda90030c41496c53885b4",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2a/c3/c1694a56da0e053d4dff2f203684",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0e/b8/9bbb04ec920dab1e32cbb752ab12",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/63/9a/85a2953ff4282728fac8bc9b92aa",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/da/6b/53941679695bef614fe94906f8c7",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/91/5b/5b4871e3e18e2cf046f8411ac41c",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b1/e8/b471efa43d7bb2be88ec4882d1ec",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/20/30/0567f04d0ed9662d84907d73ccff",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/61/3e/3bd9d4b36f8e7977902f5830eef7",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6f/55/f9e2afacd2d1e55622d42102f987",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/38/78/c94ca8ab70e99a64fe7d9b77b7fd",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/81/3e/ce018d8ffb1ce8b795461f177926",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4f/5e/2b753f317848edb49a1228b48669",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e6/09/5fb14bb97f99c12509d986482a9e",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/31/9d/c21fd262d8e8bf7357982d3919ab",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/b4/03/1a4dd6757d0f02f536b01446aa70",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a7/e5/ed98da5d3d1d101498cdb78a3e66",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/8e/be/4c06b72d6cfe9d7c918e49e5dade",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/d6/e0/7e696374bf682d8de0fa761fa280",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ab/36/ab405201f69f1396c7393d272145",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b1/f7/4d23bfb3b40e3abf944d03b293fb",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ea/32/46199c5adad2591f3335cc3788a9",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b8/9d/10dd3fee2be55f0fb05160208cc3",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d3/0c/dfd26af11b14dea706d03d32bc39",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/02/80/b5fde15cc6e79f9b243b1e6c7035",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f6/07/5cee0f1e1e5362d84f1af9ed2503",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3c/66/432193098a405483f64a78ad3139",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d3/56/804cd3f543b05891f4f7d85ca457",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2a/95/62f8f30e3829d95358f603344aca",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bd/c1/db89136718610fbab8ebeb29e62c",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/09/2a/71b05acd74b7f18af9b6eb0de3a3",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d2/37/08d73f46704304b24b38bf93b046",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d7/17/6220f7a35fbddb711c605903f491",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c0/73/ae5ad1b4170b2bba3f31a95a9b3d",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2f/d2/8aa3dc377561ad74448c7e3f5a44",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/8e/ff/159b3a7f288f6fc7bd5fb7091a52",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/2d/68/dc944091445ecff5ff5173acb17f",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/18/17/f5fe60e049a1bece575f6df1c4b2",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/09/43/a868bb9683aac2a9bec62c8f7c89",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/1d/47/d08b1b7383d837271fd1089f3b98",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/ca/f1/f82e547b5f72cfb2c082f88523f8",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/7b/5e/81fbec9eebd1137251df2dcc2953",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/ce/7f/f34ea80406a491e5c71080ea9639",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/87/8c/22c79120b24ffa5d790bb6f72120",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02",
"src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd"
}

View File

@ -65,6 +65,7 @@
<w>aiomain</w>
<w>alarmsound</w>
<w>alibaba</w>
<w>alibname</w>
<w>allerrors</w>
<w>allobjc</w>
<w>allobjs</w>
@ -1103,6 +1104,7 @@
<w>googlevr</w>
<w>goosey</w>
<w>gotresponse</w>
<w>gpgs</w>
<w>gpio</w>
<w>gprev</w>
<w>gpsui</w>
@ -1866,6 +1868,7 @@
<w>pentry</w>
<w>perma</w>
<w>perrdetail</w>
<w>phandle</w>
<w>phasers</w>
<w>phasescriptexecution</w>
<w>phello</w>

View File

@ -1,4 +1,10 @@
### 1.7.14 (build 20921, api 7, 2022-11-03)
### 1.7.14 (build 20928, api 7, 2022-11-15)
- Android Google Play logins now provide V2 accounts with access to all V2 features such as a globally-unique account tag, cloud-console, and workspaces. They should still retain their V1 data as well.
- V2 accounts now have a 'Manage Account' button in the app account window which will sign you into a browser with your current account.
- Removed Google App Invite functionality which has been deprecated for a while now. Google Play users can still get tickets by sharing the app via codes (same as other platforms).
- Updated Android root-detection library to the latest version. Please holler if you are getting new false 'your device is rooted' errors when trying to play tournaments or anything like that.
- Removed a few obsolete internal functions: `_ba.is_ouya_build()`, `_ba.android_media_scan_file()`.
- Renaming some methods/data to disambiguate 'login' vs 'sign-in', both in the app and on ballistica.net. Those two terms are somewhat ambiguous and interchangeable in English and can either be a verb or a noun. I'd like to keep things clear in Ballistica by always using 'sign-in' for the verb form and 'login' for the noun. For example: 'You can now sign in to your account using your Google Play login'.
### 1.7.13 (build 20919, api 7, 2022-11-03)
- Android target-sdk has been updated to 33 (Android 13). Please holler if anything seems broken or is behaving differently than before on Android.

View File

@ -40,6 +40,7 @@
"ba_data/python/ba/__pycache__/_language.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_level.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_lobby.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_login.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_map.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_math.cpython-310.opt-1.pyc",
"ba_data/python/ba/__pycache__/_messages.cpython-310.opt-1.pyc",
@ -110,6 +111,7 @@
"ba_data/python/ba/_language.py",
"ba_data/python/ba/_level.py",
"ba_data/python/ba/_lobby.py",
"ba_data/python/ba/_login.py",
"ba_data/python/ba/_map.py",
"ba_data/python/ba/_math.py",
"ba_data/python/ba/_messages.py",
@ -147,6 +149,7 @@
"ba_data/python/bacommon/__pycache__/bacloud.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/build.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/cloud.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/login.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/net.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/servermanager.cpython-310.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/transfer.cpython-310.opt-1.pyc",
@ -154,6 +157,7 @@
"ba_data/python/bacommon/bacloud.py",
"ba_data/python/bacommon/build.py",
"ba_data/python/bacommon/cloud.py",
"ba_data/python/bacommon/login.py",
"ba_data/python/bacommon/net.py",
"ba_data/python/bacommon/servermanager.py",
"ba_data/python/bacommon/transfer.py",
@ -358,12 +362,12 @@
"ba_data/python/bastd/ui/account/__pycache__/link.cpython-310.opt-1.pyc",
"ba_data/python/bastd/ui/account/__pycache__/settings.cpython-310.opt-1.pyc",
"ba_data/python/bastd/ui/account/__pycache__/unlink.cpython-310.opt-1.pyc",
"ba_data/python/bastd/ui/account/__pycache__/v2.cpython-310.opt-1.pyc",
"ba_data/python/bastd/ui/account/__pycache__/v2proxy.cpython-310.opt-1.pyc",
"ba_data/python/bastd/ui/account/__pycache__/viewer.cpython-310.opt-1.pyc",
"ba_data/python/bastd/ui/account/link.py",
"ba_data/python/bastd/ui/account/settings.py",
"ba_data/python/bastd/ui/account/unlink.py",
"ba_data/python/bastd/ui/account/v2.py",
"ba_data/python/bastd/ui/account/v2proxy.py",
"ba_data/python/bastd/ui/account/viewer.py",
"ba_data/python/bastd/ui/achievements.py",
"ba_data/python/bastd/ui/appinvite.py",

View File

@ -174,6 +174,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/ba/_language.py \
build/ba_data/python/ba/_level.py \
build/ba_data/python/ba/_lobby.py \
build/ba_data/python/ba/_login.py \
build/ba_data/python/ba/_map.py \
build/ba_data/python/ba/_math.py \
build/ba_data/python/ba/_messages.py \
@ -286,7 +287,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/bastd/ui/account/link.py \
build/ba_data/python/bastd/ui/account/settings.py \
build/ba_data/python/bastd/ui/account/unlink.py \
build/ba_data/python/bastd/ui/account/v2.py \
build/ba_data/python/bastd/ui/account/v2proxy.py \
build/ba_data/python/bastd/ui/account/viewer.py \
build/ba_data/python/bastd/ui/achievements.py \
build/ba_data/python/bastd/ui/appinvite.py \
@ -425,6 +426,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/ba/__pycache__/_language.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_level.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_lobby.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_login.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_map.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_math.cpython-310.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_messages.cpython-310.opt-1.pyc \
@ -537,7 +539,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/bastd/ui/account/__pycache__/link.cpython-310.opt-1.pyc \
build/ba_data/python/bastd/ui/account/__pycache__/settings.cpython-310.opt-1.pyc \
build/ba_data/python/bastd/ui/account/__pycache__/unlink.cpython-310.opt-1.pyc \
build/ba_data/python/bastd/ui/account/__pycache__/v2.cpython-310.opt-1.pyc \
build/ba_data/python/bastd/ui/account/__pycache__/v2proxy.cpython-310.opt-1.pyc \
build/ba_data/python/bastd/ui/account/__pycache__/viewer.cpython-310.opt-1.pyc \
build/ba_data/python/bastd/ui/__pycache__/achievements.cpython-310.opt-1.pyc \
build/ba_data/python/bastd/ui/__pycache__/appinvite.cpython-310.opt-1.pyc \
@ -654,6 +656,7 @@ SCRIPT_TARGETS_PY_PUBLIC_TOOLS = \
build/ba_data/python/bacommon/bacloud.py \
build/ba_data/python/bacommon/build.py \
build/ba_data/python/bacommon/cloud.py \
build/ba_data/python/bacommon/login.py \
build/ba_data/python/bacommon/net.py \
build/ba_data/python/bacommon/servermanager.py \
build/ba_data/python/bacommon/transfer.py \
@ -686,6 +689,7 @@ SCRIPT_TARGETS_PYC_PUBLIC_TOOLS = \
build/ba_data/python/bacommon/__pycache__/bacloud.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/build.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/cloud.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/login.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/net.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/servermanager.cpython-310.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/transfer.cpython-310.opt-1.pyc \

View File

@ -1 +1 @@
144047512702782890975548019773691278470
90228160524159856290785480177185335343

View File

@ -1227,16 +1227,6 @@ def android_get_external_files_dir() -> str:
return str()
def android_media_scan_file(file_name: str) -> None:
"""(internal)
Refreshes Android MTP Index for a file; use this to get file
modifications to be reflected in Android File Transfer.
"""
return None
def android_show_wifi_settings() -> None:
"""(internal)"""
@ -2406,15 +2396,6 @@ def is_os_playing_music() -> bool:
return bool()
def is_ouya_build() -> bool:
"""(internal)
Returns whether we're running the ouya-specific version
"""
return bool()
def is_party_icon_visible() -> bool:
"""(internal)"""
@ -2448,6 +2429,12 @@ def lock_all_input() -> None:
return None
def login_adapter_get_sign_in_token(login_type: str, attempt_id: int) -> None:
"""(internal)"""
return None
def mac_music_app_get_library_source() -> None:
"""(internal)"""

View File

@ -4,6 +4,7 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
import _ba
@ -11,6 +12,9 @@ import _ba
if TYPE_CHECKING:
from typing import Any
from bacommon.login import LoginType
from ba._login import LoginAdapter
class AccountV2Subsystem:
"""Subsystem for modern account handling in the app.
@ -21,6 +25,7 @@ class AccountV2Subsystem:
"""
def __init__(self) -> None:
from bacommon.login import LoginType
# Whether or not everything related to an initial login
# (or lack thereof) has completed. This includes things like
@ -30,9 +35,24 @@ class AccountV2Subsystem:
self._kicked_off_workspace_load = False
self.login_adapters: dict[LoginType, LoginAdapter] = {}
self._implicit_signed_in_adapter: LoginAdapter | None = None
self._auto_signed_in = False
if _ba.app.platform == 'android' and _ba.app.subplatform == 'google':
from ba._login import LoginAdapterGPGS
self.login_adapters[LoginType.GPGS] = LoginAdapterGPGS(
LoginType.GPGS
)
def on_app_launch(self) -> None:
"""Should be called at standard on_app_launch time."""
for adapter in self.login_adapters.values():
adapter.on_app_launch()
def set_primary_credentials(self, credentials: str | None) -> None:
"""Set credentials for the primary app account."""
raise RuntimeError('This should be overridden.')
@ -49,7 +69,7 @@ class AccountV2Subsystem:
@property
def primary(self) -> AccountV2Handle | None:
"""The primary account for the app, or None if not logged in."""
return None
return self.do_get_primary()
def do_get_primary(self) -> AccountV2Handle | None:
"""Internal - should be overridden by subclass."""
@ -60,9 +80,11 @@ class AccountV2Subsystem:
) -> None:
"""Callback run after the primary account changes.
Will be called with None on log-outs or when new credentials
Will be called with None on log-outs and when new credentials
are set but have not yet been verified.
"""
assert _ba.in_logic_thread()
# Currently don't do anything special on sign-outs.
if account is None:
return
@ -99,6 +121,30 @@ class AccountV2Subsystem:
self._initial_login_completed = True
_ba.app.on_initial_login_completed()
def on_active_logins_changed(self, logins: dict[LoginType, str]) -> None:
"""Should be called when logins for the active account change."""
for adapter in self.login_adapters.values():
adapter.set_active_logins(logins)
def on_implicit_login(
self, login_type: LoginType, login_id: str, display_name: str
) -> None:
"""An implicit login happened."""
from ba._login import LoginAdapter
with _ba.Context('ui'):
self.login_adapters[login_type].set_implicit_login_state(
LoginAdapter.ImplicitLoginState(
login_id=login_id, display_name=display_name
)
)
def on_implicit_logout(self, login_type: LoginType) -> None:
"""An implicit logout happened."""
with _ba.Context('ui'):
self.login_adapters[login_type].set_implicit_login_state(None)
def on_no_initial_primary_account(self) -> None:
"""Callback run if the app has no primary account after launch.
@ -110,6 +156,63 @@ class AccountV2Subsystem:
self._initial_login_completed = True
_ba.app.on_initial_login_completed()
def on_implicit_login_state_changed(
self,
login_type: LoginType,
state: LoginAdapter.ImplicitLoginState | None,
) -> None:
"""Called when implicit login state changes.
Logins that tend to sign themselves in/out in the background are
considered implicit. We may choose to honor or ignore their states,
allowing the user to opt for other login types even if the default
implicit one can't be explicitly logged out or otherwise controlled.
"""
assert _ba.in_logic_thread()
# Store which (if any) adapter is currently implicitly signed in.
if state is None:
self._implicit_signed_in_adapter = None
else:
self._implicit_signed_in_adapter = self.login_adapters[login_type]
# We may want to auto-sign-in based on this new state.
self._update_auto_sign_in()
def on_cloud_connectivity_changed(self, connected: bool) -> None:
"""Should be called with cloud connectivity changes."""
del connected # Unused.
assert _ba.in_logic_thread()
# We may want to auto-sign-in based on this new state.
self._update_auto_sign_in()
def _update_auto_sign_in(self) -> None:
from ba._internal import get_v1_account_state
# We attempt auto-sign-in only once.
if self._auto_signed_in:
return
# If we're not currently signed in, we have connectivity, and
# we have an available implicit adapter, do an auto-sign-in.
connected = _ba.app.cloud.is_connected()
signed_in_v1 = get_v1_account_state() == 'signed_in'
signed_in_v2 = _ba.app.accounts_v2.have_primary_credentials()
if (
connected
and not signed_in_v1
and not signed_in_v2
and self._implicit_signed_in_adapter is not None
):
self._auto_signed_in = True
self._implicit_signed_in_adapter.sign_in(self._on_sign_in_completed)
def _on_sign_in_completed(
self, result: LoginAdapter.SignInResult | Exception
) -> None:
logging.debug('GOT SIGN-IN COMPLETED WITH %s', result)
def _on_set_active_workspace_completed(self) -> None:
if not self._initial_login_completed:
self._initial_login_completed = True
@ -129,8 +232,17 @@ class AccountV2Handle:
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] = {}
def __enter__(self) -> None:
"""Support for "with" statement."""
"""Support for "with" statement.
This allows cloud messages to be sent on our behalf.
"""
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any:
"""Support for "with" statement."""
"""Support for "with" statement.
This allows cloud messages to be sent on our behalf.
"""

View File

@ -47,7 +47,7 @@ def bootstrap() -> None:
# Give a soft warning if we're being used with a different binary
# version than we expect.
expected_build = 20921
expected_build = 20928
running_build: int = env['build_number']
if running_build != expected_build:
print(

View File

@ -4,6 +4,7 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, overload
import _ba
@ -14,6 +15,8 @@ if TYPE_CHECKING:
from efro.message import Message, Response
import bacommon.cloud
DEBUG_LOG = False
# TODO: Should make it possible to define a protocol in bacommon.cloud and
# autogenerate this. That would give us type safety between this and
# internal protocols.
@ -30,6 +33,15 @@ class CloudSubsystem:
"""
return False # Needs to be overridden
def on_connectivity_changed(self, connected: bool) -> None:
"""Called when cloud connectivity state changes."""
if DEBUG_LOG:
logging.debug('CloudSubsystem: Connectivity is now %s.', connected)
# Inform things that use this.
# (TODO: should generalize this into some sort of registration system)
_ba.app.accounts_v2.on_cloud_connectivity_changed(connected)
@overload
def send_message_cb(
self,
@ -66,6 +78,26 @@ class CloudSubsystem:
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.SignInMessage,
on_response: Callable[
[bacommon.cloud.SignInResponse | Exception], None
],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.ManageAccountMessage,
on_response: Callable[
[bacommon.cloud.ManageAccountResponse | Exception], None
],
) -> None:
...
def send_message_cb(
self,
msg: Message,
@ -110,7 +142,6 @@ class CloudSubsystem:
def cloud_console_exec(code: str) -> None:
"""Called by the cloud console to run code in the logic thread."""
import sys
import logging
import __main__
from ba._generated.enums import TimeType

View File

@ -278,7 +278,7 @@ def show_damage_count(
def timestring(
timeval: float,
timeval: float | int,
centi: bool = True,
timeformat: ba.TimeFormat = TimeFormat.SECONDS,
suppress_format_warning: bool = False,

View File

@ -247,6 +247,14 @@ def google_play_purchases_not_available_message() -> None:
)
def google_play_services_not_available_message() -> None:
from ba._language import Lstr
_ba.screenmessage(
Lstr(resource='googlePlayServicesNotAvailableText'), color=(1, 0, 0)
)
def empty_call() -> None:
pass
@ -424,3 +432,39 @@ def hash_strings(inputs: list[str]) -> str:
def have_account_v2_credentials() -> bool:
"""Do we have primary account-v2 credentials set?"""
return _ba.app.accounts_v2.have_primary_credentials()
def implicit_login(
login_type_str: str, login_id: str, display_name: str
) -> None:
"""An implicit login happened."""
from bacommon.login import LoginType
_ba.app.accounts_v2.on_implicit_login(
login_type=LoginType(login_type_str),
login_id=login_id,
display_name=display_name,
)
def implicit_logout(login_type_str: str) -> None:
"""An implicit logout happened."""
from bacommon.login import LoginType
_ba.app.accounts_v2.on_implicit_logout(login_type=LoginType(login_type_str))
def login_adapter_get_sign_in_token_response(
login_type_str: str, attempt_id_str: str, result_str: str
) -> None:
"""Login adapter do-sign-in completed."""
from bacommon.login import LoginType
from ba._login import LoginAdapterGPGS
login_type = LoginType(login_type_str)
attempt_id = int(attempt_id_str)
result = None if result_str == '' else result_str
with _ba.Context('ui'):
adapter = _ba.app.accounts_v2.login_adapters[login_type]
assert isinstance(adapter, LoginAdapterGPGS)
adapter.on_sign_in_complete(attempt_id=attempt_id, result=result)

View File

@ -321,7 +321,9 @@ def get_v1_account_state() -> str:
"""(internal)"""
if HAVE_INTERNAL:
return _bainternal.get_v1_account_state()
raise _no_bainternal_error()
# Without internal present just consider ourself always signed out.
return 'signed_out'
def get_v1_account_display_string(full: bool = True) -> str:

View File

@ -0,0 +1,298 @@
# Released under the MIT License. See LICENSE for details.
#
"""Login related functionality."""
from __future__ import annotations
import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, final
import _ba
if TYPE_CHECKING:
from typing import Callable
from bacommon.login import LoginType
DEBUG_LOG = True
class LoginAdapter:
"""Allows using implicit login types in an explicit way.
Some login types such as Google Play Game Services or Game Center are
basically always present and often do not provide a way to log out
from within a running app, so this adapter exists to use them in a
flexible manner by 'attaching' and 'detaching' from an always-present
login, allowing for its use alongside other login types. It also
provides common functionality for server-side account verification and
other handy bits.
"""
@dataclass
class SignInResult:
"""Describes the final result of a sign-in attempt."""
@dataclass
class ImplicitLoginState:
"""Describes the current state of an implicit login."""
login_id: str
display_name: str
def __init__(self, login_type: LoginType):
assert _ba.in_logic_thread()
self.login_type = login_type
self._implicit_login_state: LoginAdapter.ImplicitLoginState | None = (
None
)
self._on_app_launch_called = False
self._implicit_login_state_dirty = False
self._back_end_active = False
# Which login of our type (if any) is associated with the
# current active primary account.
self._active_login_id: str | None = None
def on_app_launch(self) -> None:
"""Should be called for each adapter in on_app_launch."""
assert not self._on_app_launch_called
self._on_app_launch_called = True
# Any implicit state we received up until now needs to be pushed
# to the app account subsystem.
self._update_implicit_login_state()
def set_implicit_login_state(
self, state: ImplicitLoginState | None
) -> None:
"""Keep the adapter informed of implicit login states.
This should be called by the adapter back-end when an account
of their associated type gets logged in or out.
"""
assert _ba.in_logic_thread()
# Ignore redundant sets.
if state == self._implicit_login_state:
return
if DEBUG_LOG:
if state is None:
logging.debug(
'LoginAdapter: %s implicit state changed;'
' now signed out.',
self.login_type.name,
)
else:
logging.debug(
'LoginAdapter: %s implicit state changed;'
' now signed in as %s.',
self.login_type.name,
state.display_name,
)
self._implicit_login_state = state
self._implicit_login_state_dirty = True
# (possibly) push it to the app for handling.
self._update_implicit_login_state()
def set_active_logins(self, logins: dict[LoginType, str]) -> None:
"""Keep the adapter informed of actively used logins.
This should be called by the app's account subsystem to
keep adapters up to date on the full set of logins attached
to the currently-in-use account.
Note that the logins dict passed in should be immutable as
only a reference to it is stored, not a copy.
"""
assert _ba.in_logic_thread()
if DEBUG_LOG:
logging.debug(
'LoginAdapter: %s adapter got active logins %s.',
self.login_type.name,
logins,
)
self._active_login_id = logins.get(self.login_type)
self._update_back_end_active()
def on_back_end_active_change(self, active: bool) -> None:
"""Called when active state for the back-end is (possibly) changing.
Meant to be overridden by subclasses.
Being active means that the implicit login provided by the back-end
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.
"""
assert _ba.in_logic_thread()
del active # Unused.
@final
def sign_in(
self, result_cb: Callable[[SignInResult | Exception], None]
) -> None:
"""Attempt an explicit 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
be returned if the sign-in attempt fails.
"""
assert _ba.in_logic_thread()
from ba._general import Call
del result_cb # Unused.
if DEBUG_LOG:
logging.debug(
'LoginAdapter: %s adapter sign_in() called;'
' fetching sign-in-token...',
self.login_type.name,
)
def _got_sign_in_token_result(result: str | None) -> None:
import bacommon.cloud
# Failed to get a sign-in-token.
if result is None:
if DEBUG_LOG:
logging.debug(
'LoginAdapter: %s adapter sign-in-token fetch failed;'
' aborting sign-in.',
self.login_type.name,
)
_ba.pushcall(
Call(result_cb, RuntimeError('fetch-sign-in-token failed'))
)
return
# Got a sign-in token! Now pass it to the cloud which will use
# it to verify our identity and give us app credentials on
# success.
if DEBUG_LOG:
logging.debug(
'LoginAdapter: %s adapter sign-in-token fetch succeeded;'
' passing to cloud for verification...',
self.login_type.name,
)
def _got_sign_in_response(
response: bacommon.cloud.SignInResponse | Exception,
) -> None:
from ba._language import Lstr
if DEBUG_LOG:
logging.debug(
'LoginAdapter: %s adapter got sign-in response %s...',
self.login_type.name,
response,
)
if isinstance(response, Exception):
_ba.screenmessage(
Lstr(resource='errorText'), color=(1, 0, 0)
)
_ba.playsound(_ba.getsound('error'))
else:
_ba.app.accounts_v2.set_primary_credentials(
response.credentials
)
_ba.app.cloud.send_message_cb(
bacommon.cloud.SignInMessage(self.login_type, result),
on_response=_got_sign_in_response,
)
self.get_sign_in_token(completion_cb=_got_sign_in_token_result)
def get_sign_in_token(
self, completion_cb: Callable[[str | None], None]
) -> None:
"""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.
"""
from ba._general import Call
# Default implementation simply fails immediately.
_ba.pushcall(Call(completion_cb, None))
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
# 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_launch_called:
from ba._general import Call
if DEBUG_LOG:
logging.debug(
'LoginAdapter: %s adapter sending'
' implicit-state-changed to app.',
self.login_type.name,
)
_ba.pushcall(
Call(
_ba.app.accounts_v2.on_implicit_login_state_changed,
self.login_type,
self._implicit_login_state,
)
)
self._implicit_login_state_dirty = False
def _update_back_end_active(self) -> None:
was_active = self._back_end_active
if self._implicit_login_state is None:
is_active = False
else:
is_active = (
self._implicit_login_state.login_id == self._active_login_id
)
if was_active != is_active:
if DEBUG_LOG:
logging.debug(
'LoginAdapter: %s adapter back-end-active is now %s.',
self.login_type.name,
is_active,
)
self.on_back_end_active_change(is_active)
self._back_end_active = is_active
class LoginAdapterGPGS(LoginAdapter):
"""Google Play Game Services adapter."""
def __init__(self, login_type: LoginType):
super().__init__(login_type)
# Store int ids for in-flight attempts since they may go through
# various platform layers and back.
self._sign_in_attempt_num = 123
self._sign_in_attempts: dict[int, Callable[[str | None], None]] = {}
def get_sign_in_token(
self, completion_cb: Callable[[str | None], None]
) -> None:
attempt_id = self._sign_in_attempt_num
self._sign_in_attempts[attempt_id] = completion_cb
self._sign_in_attempt_num += 1
_ba.login_adapter_get_sign_in_token(self.login_type.value, attempt_id)
def on_sign_in_complete(self, attempt_id: int, result: str | None) -> None:
"""Called by the native layer on a completed attempt."""
assert _ba.in_logic_thread()
if attempt_id not in self._sign_in_attempts:
logging.exception('sign-in attempt_id %d not found', attempt_id)
return
callback = self._sign_in_attempts.pop(attempt_id)
callback(result)

View File

@ -80,6 +80,7 @@ from _ba import (
get_replays_dir,
)
from ba._login import LoginAdapter
from ba._map import (
get_map_class,
register_map,
@ -178,6 +179,7 @@ from ba._internal import (
)
__all__ = [
'LoginAdapter',
'show_online_score_ui',
'set_ui_input_device',
'is_party_icon_visible',
@ -247,7 +249,6 @@ __all__ = [
'set_telnet_access_enabled',
'new_replay_session',
'get_replays_dir',
# DIVIDER
'get_unowned_maps',
'get_unowned_game_types',
'get_map_class',

View File

@ -87,7 +87,7 @@ def show_user_scripts() -> None:
' See settings/advanced'
' in the game for more info.'
)
_ba.android_media_scan_file(file_name)
except Exception:
from ba import _error

View File

@ -6,13 +6,16 @@
from __future__ import annotations
import time
import logging
from typing import TYPE_CHECKING
import bacommon.cloud
from bacommon.login import LoginType
import ba
import ba.internal
if TYPE_CHECKING:
pass
from ba.internal import LoginAdapter
class AccountSettingsWindow(ba.Window):
@ -27,7 +30,7 @@ class AccountSettingsWindow(ba.Window):
):
# pylint: disable=too-many-statements
self._sign_in_v2_button: ba.Widget | None = None
self._sign_in_v2_proxy_button: ba.Widget | None = None
self._sign_in_device_button: ba.Widget | None = None
self._close_once_signed_in = close_once_signed_in
@ -92,16 +95,15 @@ class AccountSettingsWindow(ba.Window):
# Determine which sign-in/sign-out buttons we should show.
self._show_sign_in_buttons: list[str] = []
if app.platform == 'android' and app.subplatform == 'google':
if LoginType.GPGS in ba.app.accounts_v2.login_adapters:
self._show_sign_in_buttons.append('Google Play')
# Local accounts are generally always available with a few key
# exceptions.
self._show_sign_in_buttons.append('Local')
# Always want to show our web-based v2 login option.
self._show_sign_in_buttons.append('V2Proxy')
# Ditto with shiny new V2 ones.
if bool(True):
self._show_sign_in_buttons.append('V2')
# Legacy v1 device accounts are currently always available
# (though we need to start phasing them out at some point).
self._show_sign_in_buttons.append('Local')
top_extra = 15 if uiscale is ba.UIScale.SMALL else 0
super().__init__(
@ -255,8 +257,9 @@ class AccountSettingsWindow(ba.Window):
account_state == 'signed_out'
and 'Local' in self._show_sign_in_buttons
)
show_v2_sign_in_button = (
account_state == 'signed_out' and 'V2' in self._show_sign_in_buttons
show_v2_proxy_sign_in_button = (
account_state == 'signed_out'
and 'V2Proxy' in self._show_sign_in_buttons
)
sign_in_button_space = 70.0
@ -275,9 +278,7 @@ class AccountSettingsWindow(ba.Window):
show_achievements_button = self._signed_in and account_type in (
'Google Play',
'Alibaba',
'Local',
'OUYA',
'V2',
)
achievements_button_space = 60.0
@ -299,9 +300,7 @@ class AccountSettingsWindow(ba.Window):
show_reset_progress_button = False
reset_progress_button_space = 70.0
show_manage_v2_account_button = (
self._signed_in and account_type == 'V2' and bool(False)
) # Disabled for now.
show_manage_v2_account_button = self._signed_in and account_type == 'V2'
manage_v2_account_button_space = 100.0
show_player_profiles_button = self._signed_in
@ -327,11 +326,11 @@ class AccountSettingsWindow(ba.Window):
]
sign_out_button_space = 70.0
show_cancel_v2_sign_in_button = (
show_cancel_sign_in_button = (
account_state == 'signing_in'
and ba.app.accounts_v2.have_primary_credentials()
)
cancel_v2_sign_in_button_space = 70.0
cancel_sign_in_button_space = 70.0
if self._subcontainer is not None:
self._subcontainer.delete()
@ -346,7 +345,7 @@ class AccountSettingsWindow(ba.Window):
self._sub_height += sign_in_button_space
if show_device_sign_in_button:
self._sub_height += sign_in_button_space
if show_v2_sign_in_button:
if show_v2_proxy_sign_in_button:
self._sub_height += sign_in_button_space
if show_game_service_button:
self._sub_height += game_service_button_space
@ -376,8 +375,8 @@ class AccountSettingsWindow(ba.Window):
self._sub_height += unlink_accounts_button_space
if show_sign_out_button:
self._sub_height += sign_out_button_space
if show_cancel_v2_sign_in_button:
self._sub_height += cancel_v2_sign_in_button_space
if show_cancel_sign_in_button:
self._sub_height += cancel_sign_in_button_space
self._subcontainer = ba.containerwidget(
parent=self._scrollwidget,
size=(self._sub_width, self._sub_height),
@ -528,7 +527,7 @@ class AccountSettingsWindow(ba.Window):
),
],
),
on_activate_call=lambda: self._sign_in_press('Google Play'),
on_activate_call=lambda: self._sign_in_press(LoginType.GPGS),
)
if first_selectable is None:
first_selectable = btn
@ -541,16 +540,16 @@ class AccountSettingsWindow(ba.Window):
ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
self._sign_in_text = None
if show_v2_sign_in_button:
if show_v2_proxy_sign_in_button:
button_width = 350
v -= sign_in_button_space
self._sign_in_v2_button = btn = ba.buttonwidget(
self._sign_in_v2_proxy_button = btn = ba.buttonwidget(
parent=self._subcontainer,
position=((self._sub_width - button_width) * 0.5, v - 20),
autoselect=True,
size=(button_width, 60),
label='',
on_activate_call=self._v2_sign_in_press,
on_activate_call=self._v2_proxy_sign_in_press,
)
ba.textwidget(
parent=self._subcontainer,
@ -663,9 +662,7 @@ class AccountSettingsWindow(ba.Window):
color=(0.55, 0.5, 0.6),
icon=ba.gettexture('settingsIcon'),
textcolor=(0.75, 0.7, 0.8),
on_activate_call=lambda: ba.open_url(
'https://ballistica.net/accountsettings'
),
on_activate_call=ba.WeakCall(self._on_manage_account_press),
)
if first_selectable is None:
first_selectable = btn
@ -1005,9 +1002,9 @@ class AccountSettingsWindow(ba.Window):
)
ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15)
if show_cancel_v2_sign_in_button:
v -= cancel_v2_sign_in_button_space
self._cancel_v2_sign_in_button = btn = ba.buttonwidget(
if show_cancel_sign_in_button:
v -= cancel_sign_in_button_space
self._cancel_sign_in_button = btn = ba.buttonwidget(
parent=self._subcontainer,
position=((self._sub_width - button_width) * 0.5, v),
size=(button_width, 60),
@ -1015,7 +1012,7 @@ class AccountSettingsWindow(ba.Window):
color=(0.55, 0.5, 0.6),
textcolor=(0.75, 0.7, 0.8),
autoselect=True,
on_activate_call=self._cancel_v2_sign_in_press,
on_activate_call=self._cancel_sign_in_press,
)
if first_selectable is None:
first_selectable = btn
@ -1066,6 +1063,33 @@ class AccountSettingsWindow(ba.Window):
account_type,
)
def _on_manage_account_press(self) -> None:
ba.screenmessage(ba.Lstr(resource='oneMomentText'))
# We expect to have a v2 account signed in if we get here.
if ba.app.accounts_v2.primary is None:
logging.exception(
'got manage-account press without v2 account present'
)
return
with ba.app.accounts_v2.primary:
ba.app.cloud.send_message_cb(
bacommon.cloud.ManageAccountMessage(),
on_response=ba.WeakCall(self._on_manage_account_response),
)
def _on_manage_account_response(
self, response: bacommon.cloud.ManageAccountResponse | Exception
) -> None:
if isinstance(response, Exception) or response.url is None:
ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
return
ba.open_url(response.url)
def _on_leaderboards_press(self) -> None:
ba.timer(
0.15,
@ -1214,7 +1238,7 @@ class AccountSettingsWindow(ba.Window):
origin_widget=self._player_profiles_button
)
def _cancel_v2_sign_in_press(self) -> None:
def _cancel_sign_in_press(self) -> None:
# Just say we don't wanna be signed in anymore.
ba.app.accounts_v2.set_primary_credentials(None)
@ -1242,25 +1266,42 @@ class AccountSettingsWindow(ba.Window):
# Speed UI updates along.
ba.timer(0.1, ba.WeakCall(self._update), timetype=ba.TimeType.REAL)
def _sign_in_press(
self, account_type: str, show_test_warning: bool = True
def _sign_in_press(self, login_type: str | LoginType) -> None:
# V1 login types are strings.
if isinstance(login_type, str):
ba.internal.sign_in_v1(login_type)
# Make note of the type account we're *wanting*
# to be signed in with.
cfg = ba.app.config
cfg['Auto Account State'] = login_type
cfg.commit()
self._needs_refresh = True
ba.timer(0.1, ba.WeakCall(self._update), timetype=ba.TimeType.REAL)
return
# V2 login sign-in buttons generally go through adapters.
adapter = ba.app.accounts_v2.login_adapters.get(login_type)
if adapter is not None:
adapter.sign_in(
result_cb=ba.WeakCall(self._on_adapter_sign_in_result)
)
else:
ba.screenmessage(f'Unsupported login_type: {login_type.name}')
def _on_adapter_sign_in_result(
self, result: LoginAdapter.SignInResult | Exception
) -> None:
del show_test_warning # unused
ba.internal.sign_in_v1(account_type)
ba.screenmessage('GOT SIGN IN RESULT')
logging.debug('GOT SIGN IN RESULT %s', result)
# Make note of the type account we're *wanting* to be signed in with.
cfg = ba.app.config
cfg['Auto Account State'] = account_type
cfg.commit()
self._needs_refresh = True
ba.timer(0.1, ba.WeakCall(self._update), timetype=ba.TimeType.REAL)
def _v2_sign_in_press(self) -> None:
def _v2_proxy_sign_in_press(self) -> None:
# pylint: disable=cyclic-import
from bastd.ui.account.v2 import V2SignInWindow
from bastd.ui.account.v2proxy import V2ProxySignInWindow
assert self._sign_in_v2_button is not None
V2SignInWindow(origin_widget=self._sign_in_v2_button)
assert self._sign_in_v2_proxy_button is not None
V2ProxySignInWindow(origin_widget=self._sign_in_v2_proxy_button)
def _reset_progress(self) -> None:
try:

View File

@ -19,7 +19,7 @@ if TYPE_CHECKING:
STATUS_CHECK_INTERVAL_SECONDS = 2.0
class V2SignInWindow(ba.Window):
class V2ProxySignInWindow(ba.Window):
"""A window allowing signing in to a v2 account."""
def __init__(self, origin_widget: ba.Widget):

View File

@ -413,6 +413,9 @@ def handle_app_invites_press(force_code: bool = False) -> None:
and ba.internal.get_v1_account_misc_read_val('enableAppInvites', False)
and not app.on_tv
)
# Update: google's app invites are deprecated.
do_app_invites = False
if force_code:
do_app_invites = False

View File

@ -44,6 +44,7 @@ class GatherTab:
The tab should create and return a container widget covering the
specified region.
"""
raise RuntimeError('Should not get here.')
def on_deactivate(self) -> None:
"""Called when the tab will no longer be the active one."""

View File

@ -63,7 +63,7 @@ class PlaylistBrowserWindow(ba.Window):
)
uiscale = ba.app.ui.uiscale
self._width = 900 if uiscale is ba.UIScale.SMALL else 800
self._width = 900.0 if uiscale is ba.UIScale.SMALL else 800.0
x_inset = 50 if uiscale is ba.UIScale.SMALL else 0
self._height = (
480
@ -365,7 +365,7 @@ class PlaylistBrowserWindow(ba.Window):
self._sub_width = self._scroll_width
self._sub_height = (
40 + rows * (button_height + 2 * button_buffer_v) + 90
40.0 + rows * (button_height + 2 * button_buffer_v) + 90
)
assert self._sub_width is not None
assert self._sub_height is not None

View File

@ -42,6 +42,7 @@
<w>airborn</w>
<w>alext</w>
<w>alibaba</w>
<w>alibname</w>
<w>allerrors</w>
<w>allobjc</w>
<w>allobjs</w>
@ -1005,6 +1006,7 @@
<w>pflag</w>
<w>pflags</w>
<w>pgmout</w>
<w>phandle</w>
<w>phasescriptexecution</w>
<w>piplist</w>
<w>pipvers</w>

View File

@ -50,11 +50,6 @@ void StressTest::Update() {
"time,averageFps,nodes,models,collide_models,textures,sounds,"
"pssMem,sharedDirtyMem,privateDirtyMem\n");
fflush(stress_test_stats_file_);
if (g_buildconfig.ostype_android()) {
// On android, let the OS know we've added or removed a file
// (limit to android or we'll get an unimplemented warning).
g_platform->AndroidRefreshFile(f_name);
}
}
}
if (stress_test_stats_file_ != nullptr) {

View File

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

View File

@ -155,17 +155,19 @@ static auto print_number(cJSON* item) -> char* {
double d = item->valuedouble;
if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX
&& d >= INT_MIN) {
str = (char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str, "%d", item->valueint);
size_t sz{21};
str = (char*)cJSON_malloc(sz); /* 2^64+1 can be represented in 21 chars. */
if (str) snprintf(str, sz, "%d", item->valueint);
} else {
str = (char*)cJSON_malloc(64); /* This is a nice tradeoff. */
size_t sz{64};
str = (char*)cJSON_malloc(sz); /* This is a nice tradeoff. */
if (str) {
if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60)
sprintf(str, "%.0f", d);
snprintf(str, sz, "%.0f", d);
else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
sprintf(str, "%e", d);
snprintf(str, sz, "%e", d);
else
sprintf(str, "%f", d);
snprintf(str, sz, "%f", d);
}
}
return str;
@ -379,7 +381,7 @@ static auto print_string_ptr(const char* str) -> char* {
*ptr2++ = 't';
break;
default:
sprintf(ptr2, "u%04x", token);
snprintf(ptr2, 20, "u%04x", token);
ptr2 += 5;
break; /* escape and print */
}

View File

@ -34,6 +34,7 @@
#include "ballistica/platform/sdl/sdl_app.h"
#include "ballistica/platform/stdio_console.h"
#include "ballistica/python/python.h"
#include "ballistica/python/python_sys.h"
#if BA_HEADLESS_BUILD
#include "ballistica/app/app_flavor_headless.h"
@ -183,6 +184,18 @@ auto Platform::GetLegacyDeviceUUID() -> const std::string& {
return legacy_device_uuid_;
}
auto Platform::LoginAdapterGetSignInToken(const std::string& login_type,
int attempt_id) -> void {
// Default implementation simply calls completion callback immediately.
g_logic->thread()->PushCall([login_type, attempt_id] {
PythonRef args(Py_BuildValue("(sss)", login_type.c_str(),
std::to_string(attempt_id).c_str(), ""),
PythonRef::kSteal);
g_python->obj(Python::ObjID::kLoginAdapterGetSignInTokenResponseCall)
.Call(args);
});
}
auto Platform::GetDeviceV1AccountUUIDPrefix() -> std::string {
Log(LogLevel::kError, "GetDeviceV1AccountUUIDPrefix() unimplemented");
return "u";
@ -1030,10 +1043,6 @@ void Platform::SubmitAnalyticsCounts() {}
void Platform::SetPlatformMiscReadVals(const std::string& vals) {}
void Platform::AndroidRefreshFile(const std::string& file) {
Log(LogLevel::kError, "AndroidRefreshFile() unimplemented");
}
void Platform::ShowAd(const std::string& purpose) {
Log(LogLevel::kError, "ShowAd() unimplemented");
}

View File

@ -267,7 +267,6 @@ class Platform {
virtual auto AndroidShowAppInvite(const std::string& title,
const std::string& message,
const std::string& code) -> void;
virtual auto AndroidRefreshFile(const std::string& file) -> void;
virtual auto AndroidShowWifiSettings() -> void;
virtual auto AndroidGetExternalFilesDir() -> std::string;
@ -337,6 +336,10 @@ class Platform {
/// Return the prefix to use for device-account ids on this platform.
virtual auto GetDeviceV1AccountUUIDPrefix() -> std::string;
/// Called when a Python LoginAdapter is requesting an explicit sign-in.
virtual auto LoginAdapterGetSignInToken(const std::string& login_type,
int attempt_id) -> void;
#pragma mark MUSIC PLAYBACK ----------------------------------------------------
// FIXME: currently these are wired up on Android; need to generalize

View File

@ -622,6 +622,22 @@ auto PySetAnalyticsScreen(PyObject* self, PyObject* args, PyObject* keywds)
BA_PYTHON_CATCH;
}
auto PyLoginAdapterGetSignInToken(PyObject* self, PyObject* args,
PyObject* keywds) -> PyObject* {
BA_PYTHON_TRY;
const char* login_type;
int attempt_id;
static const char* kwlist[] = {"login_type", "attempt_id", nullptr};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "si",
const_cast<char**>(kwlist), &login_type,
&attempt_id)) {
return nullptr;
}
g_platform->LoginAdapterGetSignInToken(login_type, attempt_id);
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
auto PySetInternalLanguageKeys(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_TRY;
PyObject* list_obj;
@ -659,26 +675,6 @@ auto PySetInternalLanguageKeys(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_CATCH;
}
auto PyIsOuyaBuild(PyObject* self, PyObject* args) -> PyObject* {
BA_PYTHON_TRY;
Py_RETURN_FALSE;
BA_PYTHON_CATCH;
}
auto PyAndroidMediaScanFile(PyObject* self, PyObject* args, PyObject* keywds)
-> PyObject* {
BA_PYTHON_TRY;
const char* file_name;
static const char* kwlist[] = {"file_name", nullptr};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "s",
const_cast<char**>(kwlist), &file_name)) {
return nullptr;
}
g_platform->AndroidRefreshFile(file_name);
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
#pragma clang diagnostic push
#pragma ide diagnostic ignored "ConstantFunctionResult"
@ -822,15 +818,6 @@ auto PythonMethodsSystem::GetMethods() -> std::vector<PyMethodDef> {
"\n"
"(internal)"},
{"android_media_scan_file", (PyCFunction)PyAndroidMediaScanFile,
METH_VARARGS | METH_KEYWORDS,
"android_media_scan_file(file_name: str) -> None\n"
"\n"
"(internal)\n"
"\n"
"Refreshes Android MTP Index for a file; use this to get file\n"
"modifications to be reflected in Android File Transfer."},
{"android_get_external_files_dir",
(PyCFunction)PyAndroidGetExternalFilesDir, METH_VARARGS | METH_KEYWORDS,
"android_get_external_files_dir() -> str\n"
@ -847,13 +834,6 @@ auto PythonMethodsSystem::GetMethods() -> std::vector<PyMethodDef> {
"\n"
"(internal)"},
{"is_ouya_build", PyIsOuyaBuild, METH_VARARGS,
"is_ouya_build() -> bool\n"
"\n"
"(internal)\n"
"\n"
"Returns whether we're running the ouya-specific version"},
{"set_internal_language_keys", PySetInternalLanguageKeys, METH_VARARGS,
"set_internal_language_keys(listobj: list[tuple[str, str]],\n"
" random_names_list: list[tuple[str, str]]) -> None\n"
@ -872,6 +852,13 @@ auto PythonMethodsSystem::GetMethods() -> std::vector<PyMethodDef> {
"'screen' should be a string description of an app location\n"
"('Main Menu', etc.)"},
{"login_adapter_get_sign_in_token",
(PyCFunction)PyLoginAdapterGetSignInToken, METH_VARARGS | METH_KEYWORDS,
"login_adapter_get_sign_in_token(login_type: str, attempt_id: int)"
" -> None\n"
"\n"
"(internal)"},
{"submit_analytics_counts", (PyCFunction)PySubmitAnalyticsCounts,
METH_VARARGS | METH_KEYWORDS,
"submit_analytics_counts() -> None\n"

View File

@ -318,6 +318,7 @@ class Python {
kSetLastAdNetworkCall,
kNoGameCircleMessageCall,
kGooglePlayPurchasesNotAvailableMessageCall,
kGooglePlayServicesNotAvailableMessageCall,
kEmptyCall,
kLevelIconPressCall,
kTrophyIconPressCall,
@ -365,6 +366,9 @@ class Python {
kLoggingWarningCall,
kLoggingErrorCall,
kLoggingCriticalCall,
kImplicitLoginCall,
kImplicitLogoutCall,
kLoginAdapterGetSignInTokenResponseCall,
kLast // Sentinel; must be at end.
};

View File

@ -66,6 +66,7 @@ def get_binding_values() -> tuple[Any, ...]:
_hooks.set_last_ad_network, # kSetLastAdNetworkCall
_hooks.no_game_circle_message, # kNoGameCircleMessageCall
_hooks.google_play_purchases_not_available_message, # kGooglePlayPurchasesNotAvailableMessageCall
_hooks.google_play_services_not_available_message, # kGooglePlayServicesNotAvailableMessageCall
_hooks.empty_call, # kEmptyCall
_hooks.level_icon_press, # kLevelIconPressCall
_hooks.trophy_icon_press, # kTrophyIconPressCall
@ -142,4 +143,7 @@ def get_binding_values() -> tuple[Any, ...]:
logging.warning, # kLoggingWarningCall
logging.error, # kLoggingErrorCall
logging.critical, # kLoggingCriticalCall
_hooks.implicit_login, # kImplicitLoginCall
_hooks.implicit_logout, # kImplicitLogoutCall
_hooks.login_adapter_get_sign_in_token_response, # kLoginAdapterGetSignInTokenResponseCall
) # yapf: disable

View File

@ -10,6 +10,7 @@ from enum import Enum
from efro.message import Message, Response
from efro.dataclassio import ioprepped, IOAttrs
from bacommon.transfer import DirectoryManifest
from bacommon.login import LoginType
if TYPE_CHECKING:
pass
@ -154,3 +155,42 @@ class WorkspaceFetchResponse(Response):
] = field(default_factory=dict)
done: Annotated[bool, IOAttrs('d')] = False
@ioprepped
@dataclass
class SignInMessage(Message):
"""Can I sign in please?"""
login_type: Annotated[LoginType, IOAttrs('l')]
sign_in_token: Annotated[str, IOAttrs('t')]
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [SignInResponse]
@ioprepped
@dataclass
class SignInResponse(Response):
"""Here's that sign-in result you asked for, boss."""
credentials: Annotated[str | None, IOAttrs('c')]
@ioprepped
@dataclass
class ManageAccountMessage(Message):
"""Message asking for a manage-account url."""
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [ManageAccountResponse]
@ioprepped
@dataclass
class ManageAccountResponse(Response):
"""Here's that sign-in result you asked for, boss."""
url: Annotated[str | None, IOAttrs('u')]

21
tools/bacommon/login.py Normal file
View File

@ -0,0 +1,21 @@
# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to cloud based assets."""
from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
if TYPE_CHECKING:
pass
class LoginType(Enum):
"""Types of logins available."""
# Email/password
EMAIL = 'email'
# Google Play Game Services
GPGS = 'gpgs'

View File

@ -334,11 +334,11 @@ def gen_fulltest_buildfile_linux() -> None:
batools.build.gen_fulltest_buildfile_linux()
def python_version_build_base() -> None:
def python_version_android_base() -> None:
"""Print built Python base version."""
from efrotools.pybuild import PY_VER
from efrotools.pybuild import PY_VER_ANDROID
print(PY_VER, end='')
print(PY_VER_ANDROID, end='')
def python_version_android() -> None:
@ -444,7 +444,25 @@ def python_gather() -> None:
from efrotools import pybuild
os.chdir(PROJROOT)
pybuild.gather()
pybuild.gather(do_android=True, do_apple=True)
def python_gather_android() -> None:
"""python_gather but only android bits."""
import os
from efrotools import pybuild
os.chdir(PROJROOT)
pybuild.gather(do_android=True, do_apple=False)
def python_gather_apple() -> None:
"""python_gather but only apple bits."""
import os
from efrotools import pybuild
os.chdir(PROJROOT)
pybuild.gather(do_android=False, do_apple=True)
def python_winprune() -> None:

View File

@ -188,6 +188,7 @@ def is_asyncio_streams_communication_error(exc: BaseException) -> bool:
firewall/connectivity issues, etc. These issues can often be safely
ignored or presented to the user as general 'connection-lost' events.
"""
# pylint: disable=too-many-return-statements
import ssl
if isinstance(
@ -227,4 +228,8 @@ def is_asyncio_streams_communication_error(exc: BaseException) -> bool:
if 'SSL: WRONG_VERSION_NUMBER' in excstr:
return True
# And seeing this very rarely; assuming its just data corruption?
if 'SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC' in excstr:
return True
return False

View File

@ -273,13 +273,14 @@ class DispatchMethodWrapper(Generic[ArgT, RetT]):
"""Type-aware standin for the dispatch func returned by dispatchmethod."""
def __call__(self, arg: ArgT) -> RetT:
pass
raise RuntimeError('Should not get here')
@staticmethod
def register(
func: Callable[[Any, Any], RetT]
) -> Callable[[Any, Any], RetT]:
"""Register a new dispatch handler for this dispatch-method."""
raise RuntimeError('Should not get here')
registry: dict[Any, Callable]

View File

@ -14,9 +14,10 @@ if TYPE_CHECKING:
from typing import Any
# Python version we build here (not necessarily same as we use in repo).
PY_VER = '3.10'
PY_VER_EXACT_ANDROID = '3.10.7'
PY_VER_EXACT_APPLE = '3.10.7'
PY_VER_ANDROID = '3.11'
PY_VER_EXACT_ANDROID = '3.11.0'
PY_VER_APPLE = '3.10'
PY_VER_EXACT_APPLE = '3.10.5'
ANDROID_PYTHON_REPO = 'https://github.com/GRRedWings/python3-android'
@ -91,7 +92,7 @@ def build_apple(arch: str, debug: bool = False) -> None:
# broke in the underlying build even on old commits so keeping it
# locked for now...
# run('git checkout bf1ed73d0d5ff46862ba69dd5eb2ffaeff6f19b6')
subprocess.run(['git', 'checkout', PY_VER], check=True)
subprocess.run(['git', 'checkout', PY_VER_APPLE], check=True)
txt = readfile('Makefile')
@ -240,7 +241,7 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
# after it is extracted.
ftxt = readfile('build.sh')
ftxt = replace_exact(ftxt, 'PYVER=3.10.5', f'PYVER={PY_VER_EXACT_ANDROID}')
ftxt = replace_exact(ftxt, 'PYVER=3.11.0', f'PYVER={PY_VER_EXACT_ANDROID}')
ftxt = replace_exact(
ftxt,
' popd\n',
@ -252,7 +253,10 @@ def build_android(rootdir: str, arch: str, debug: bool = False) -> None:
# Ok; let 'er rip!
exargs = ' --with-pydebug' if debug else ''
subprocess.run(
f'ARCH={arch} ANDROID_API=21 ./build.sh{exargs}', shell=True, check=True
f'ARCH={arch} ANDROID_API=21 ./build.sh{exargs} --without-ensurepip'
f' --with-build-python=/home/ubuntu/.py311/bin/python3.11',
shell=True,
check=True,
)
print('python build complete! (android/' + arch + ')')
@ -302,30 +306,34 @@ def android_patch_ssl() -> None:
)
writefile(fname, txt)
# Getting a lot of crashes in _armv7_tick, which seems to be a
# somewhat known issue with certain arm7 devices. Sounds like
# there are no major downsides to disabling this feature, so doing that.
# (Sounds like its possible to somehow disable it through an env var
# but let's just be sure and #ifdef it out in the source.
# see https://github.com/openssl/openssl/issues/17465
fname = 'crypto/armcap.c'
txt = readfile(fname)
txt = replace_exact(
txt,
' /* Things that getauxval didn\'t tell us */\n'
' if (sigsetjmp(ill_jmp, 1) == 0) {\n'
' _armv7_tick();\n'
' OPENSSL_armcap_P |= ARMV7_TICK;\n'
' }\n',
'# if 0 // ericf disabled; causing crashes on some android devices.\n'
' /* Things that getauxval didn\'t tell us */\n'
' if (sigsetjmp(ill_jmp, 1) == 0) {\n'
' _armv7_tick();\n'
' OPENSSL_armcap_P |= ARMV7_TICK;\n'
' }\n'
'# endif // 0\n',
)
writefile(fname, txt)
# Update: looks like this might have been disabled by default for
# newer SSL builds used by 3.11+
if bool(False):
# Getting a lot of crashes in _armv7_tick, which seems to be a
# somewhat known issue with certain arm7 devices. Sounds like
# there are no major downsides to disabling this feature, so doing that.
# (Sounds like its possible to somehow disable it through an env var
# but let's just be sure and #ifdef it out in the source.
# see https://github.com/openssl/openssl/issues/17465
fname = 'crypto/armcap.c'
txt = readfile(fname)
txt = replace_exact(
txt,
' /* Things that getauxval didn\'t tell us */\n'
' if (sigsetjmp(ill_jmp, 1) == 0) {\n'
' _armv7_tick();\n'
' OPENSSL_armcap_P |= ARMV7_TICK;\n'
' }\n',
'# if 0 // ericf disabled; causing crashes'
' on some android devices.\n'
' /* Things that getauxval didn\'t tell us */\n'
' if (sigsetjmp(ill_jmp, 1) == 0) {\n'
' _armv7_tick();\n'
' OPENSSL_armcap_P |= ARMV7_TICK;\n'
' }\n'
'# endif // 0\n',
)
writefile(fname, txt)
def _patch_py_ssl() -> None:
@ -373,6 +381,7 @@ def _patch_setup_file(platform: str, arch: str, slc: str) -> None:
if platform == 'android':
prefix = '$(srcdir)/Android/sysroot/usr'
uuid_ex = f' -L{prefix}/lib -luuid'
uuid_ex += f' -I{prefix}/include/uuid' # Testing
zlib_ex = f' -I{prefix}/include -L{prefix}/lib -lz'
bz2_ex = f' -I{prefix}/include -L{prefix}/lib -lbz2'
ssl_ex = f' -DUSE_SSL -I{prefix}/include -L{prefix}/lib -lssl -lcrypto'
@ -552,7 +561,7 @@ def _patch_setup_file(platform: str, arch: str, slc: str) -> None:
ftxt += (
f'_sqlite3'
f' _sqlite/cache.c'
f' _sqlite/blob.c'
f' _sqlite/connection.c'
f' _sqlite/cursor.c'
f' _sqlite/microprotocols.c'
@ -642,7 +651,7 @@ def winprune() -> None:
print('Win-prune successful.')
def gather() -> None:
def gather(do_android: bool, do_apple: bool) -> None:
"""Gather per-platform python headers, libs, and modules together.
This assumes all embeddable py builds have been run successfully,
@ -651,8 +660,6 @@ def gather() -> None:
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
do_android = True
# First off, clear out any existing output.
existing_dirs = [
os.path.join('src/external', d)
@ -675,7 +682,7 @@ def gather() -> None:
debug = buildtype == 'debug'
bsuffix = '_debug' if buildtype == 'debug' else ''
bsuffix2 = '-debug' if buildtype == 'debug' else ''
libname = 'python' + PY_VER + ('d' if debug else '')
alibname = 'python' + PY_VER_ANDROID + ('d' if debug else '')
bases = {
'mac': f'build/python_apple_mac{bsuffix}/build/macOS',
@ -756,9 +763,9 @@ def gather() -> None:
{
'name': 'android_arm',
'group': 'android',
'headers': bases['android_arm'] + f'/usr/include/{libname}',
'headers': bases['android_arm'] + f'/usr/include/{alibname}',
'libs': [
bases['android_arm'] + f'/usr/lib/lib{libname}.a',
bases['android_arm'] + f'/usr/lib/lib{alibname}.a',
bases2['android_arm'] + '/usr/lib/libssl.a',
bases2['android_arm'] + '/usr/lib/libcrypto.a',
bases2['android_arm'] + '/usr/lib/liblzma.a',
@ -767,14 +774,16 @@ def gather() -> None:
bases2['android_arm'] + '/usr/lib/libuuid.a',
],
'libinst': 'android_armeabi-v7a',
'pylib': (bases['android_arm'] + '/usr/lib/python' + PY_VER),
'pylib': (
bases['android_arm'] + '/usr/lib/python' + PY_VER_ANDROID
),
},
{
'name': 'android_arm64',
'group': 'android',
'headers': bases['android_arm64'] + f'/usr/include/{libname}',
'headers': bases['android_arm64'] + f'/usr/include/{alibname}',
'libs': [
bases['android_arm64'] + f'/usr/lib/lib{libname}.a',
bases['android_arm64'] + f'/usr/lib/lib{alibname}.a',
bases2['android_arm64'] + '/usr/lib/libssl.a',
bases2['android_arm64'] + '/usr/lib/libcrypto.a',
bases2['android_arm64'] + '/usr/lib/liblzma.a',
@ -787,9 +796,9 @@ def gather() -> None:
{
'name': 'android_x86',
'group': 'android',
'headers': bases['android_x86'] + f'/usr/include/{libname}',
'headers': bases['android_x86'] + f'/usr/include/{alibname}',
'libs': [
bases['android_x86'] + f'/usr/lib/lib{libname}.a',
bases['android_x86'] + f'/usr/lib/lib{alibname}.a',
bases2['android_x86'] + '/usr/lib/libssl.a',
bases2['android_x86'] + '/usr/lib/libcrypto.a',
bases2['android_x86'] + '/usr/lib/liblzma.a',
@ -802,9 +811,9 @@ def gather() -> None:
{
'name': 'android_x86_64',
'group': 'android',
'headers': bases['android_x86_64'] + f'/usr/include/{libname}',
'headers': bases['android_x86_64'] + f'/usr/include/{alibname}',
'libs': [
bases['android_x86_64'] + f'/usr/lib/lib{libname}.a',
bases['android_x86_64'] + f'/usr/lib/lib{alibname}.a',
bases2['android_x86_64'] + '/usr/lib/libssl.a',
bases2['android_x86_64'] + '/usr/lib/libcrypto.a',
bases2['android_x86_64'] + '/usr/lib/liblzma.a',
@ -820,6 +829,8 @@ def gather() -> None:
grp = build['group']
if not do_android and grp == 'android':
continue
if not do_apple and grp == 'apple':
continue
builddir = f'src/external/python-{grp}{bsuffix2}'
header_dst = os.path.join(builddir, 'include')
lib_dst = os.path.join(builddir, 'lib')

View File

@ -67,7 +67,7 @@ from batools.pcommand import (
python_version_android,
python_version_apple,
python_build_apple,
python_version_build_base,
python_version_android_base,
python_build_apple_debug,
python_build_android,
python_build_android_debug,
@ -75,6 +75,8 @@ from batools.pcommand import (
python_android_patch_ssl,
python_apple_patch,
python_gather,
python_gather_apple,
python_gather_android,
python_winprune,
capitalize,
upper,