mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 21:37:57 +08:00
Work on Google Play V2 Accounts
This commit is contained in:
parent
0cbaf28aae
commit
c79abc6a74
106
.efrocachemap
106
.efrocachemap
@ -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"
|
||||
}
|
||||
3
.idea/dictionaries/ericf.xml
generated
3
.idea/dictionaries/ericf.xml
generated
@ -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>
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -1 +1 @@
|
||||
144047512702782890975548019773691278470
|
||||
90228160524159856290785480177185335343
|
||||
@ -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)"""
|
||||
|
||||
@ -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.
|
||||
"""
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
298
assets/src/ba_data/python/ba/_login.py
Normal file
298
assets/src/ba_data/python/ba/_login.py
Normal 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)
|
||||
@ -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',
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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):
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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."""
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 */
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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.
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
21
tools/bacommon/login.py
Normal 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'
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user