syncing 1.7.61 v2 auth changes
Some checks failed
CI / run_server_binary (push) Has been cancelled

This commit is contained in:
Ayush Saini 2026-02-23 19:28:45 +05:30
parent f0fc46a51d
commit 9e0176e424
19 changed files with 374 additions and 72 deletions

View file

@ -24,7 +24,6 @@ from _babase import (
add_clean_frame_callback,
allows_ticket_sales,
android_get_external_files_dir,
app_instance_uuid,
appname,
appnameupper,
apptime,
@ -234,7 +233,6 @@ __all__ = [
'AppIntentExec',
'AppMode',
'AppState',
'app_instance_uuid',
'applog',
'appname',
'appnameupper',

View file

@ -4,9 +4,11 @@
from __future__ import annotations
import time
import hashlib
import logging
from functools import partial
from dataclasses import dataclass
from typing import TYPE_CHECKING, assert_never
from efro.error import CommunicationError
@ -19,6 +21,8 @@ import _babase
if TYPE_CHECKING:
from typing import Any, Callable
import bacommon.cloud
from babase._login import LoginAdapter, LoginInfo
@ -62,6 +66,9 @@ class AccountV2Subsystem:
Callable[[AccountV2Handle | None], None]
] = CallbackSet()
# Request state per global-app-instance-id
self._auth_requests: dict[str, _AuthRequest] = {}
adapter: LoginAdapter
if _babase.using_google_play_game_services():
adapter = LoginAdapterGPGS()
@ -104,6 +111,9 @@ class AccountV2Subsystem:
"""
assert _babase.in_logic_thread()
# Blow away any outstanding auth-requests.
self._auth_requests = {}
# Inform the base layer of new names/etc.
if account is not None:
_babase.set_account_sign_in_state(True, account.tag)
@ -201,6 +211,78 @@ class AccountV2Subsystem:
self._initial_sign_in_completed = True
_babase.app.on_initial_sign_in_complete()
def auth_request(
self, global_app_instance_id: str
) -> None | tuple[bool, str]:
"""Start/process an auth request."""
import bacommon.cloud
assert _babase.in_logic_thread()
plus = _babase.app.plus
assert plus is not None
now = time.monotonic()
# If there are any expired ones, do a prune pass.
if any(r.expire_time <= now for r in self._auth_requests.values()):
self._auth_requests = {
rid: r
for rid, r in self._auth_requests.items()
if r.expire_time > now
}
auth_request = self._auth_requests.get(global_app_instance_id)
# If we find no attempt in progress, kick one off.
if (
auth_request is None
and plus.cloud.connected
and self.primary is not None
):
# print('SENDING AUTH REQUEST')
auth_request = self._auth_requests[global_app_instance_id] = (
_AuthRequest(expire_time=now + 10.0, error=None, token=None)
)
with self.primary:
plus.cloud.send_message_cb(
bacommon.cloud.AuthRequestMessage(global_app_instance_id),
on_response=partial(
self._on_auth_request_response, auth_request
),
)
# If we found results, return them.
if auth_request is None:
return None
if auth_request.error is not None:
assert auth_request.token is None
return (False, auth_request.error)
if auth_request.token is not None:
assert auth_request.error is None
return (True, auth_request.token)
# No error or token; its still in flight.
return None
def _on_auth_request_response(
self,
auth_request: _AuthRequest,
response: bacommon.cloud.AuthRequestResponse | Exception,
) -> None:
assert _babase.in_logic_thread()
assert auth_request.error is None
assert auth_request.token is None
if isinstance(response, Exception):
auth_request.error = 'An error has occurred.'
else:
# print('SETTING AUTH RESPONSE')
auth_request.error = response.error
auth_request.token = response.token
# Make sure this sticks around for long enough to complete
# the connection.
auth_request.expire_time = time.monotonic() + 10.0
@staticmethod
def _hashstr(val: str) -> str:
md5 = hashlib.md5()
@ -501,3 +583,10 @@ class AccountV2Handle:
This allows cloud messages to be sent on our behalf.
"""
@dataclass
class _AuthRequest:
expire_time: float
error: str | None
token: str | None

View file

@ -242,7 +242,6 @@ class App:
loop. Hopefully this situation will be improved in the future
with a unified event loop.
"""
assert _babase.in_logic_thread()
assert self._asyncio_loop is not None
return self._asyncio_loop

View file

@ -11,7 +11,7 @@ import _babase
if TYPE_CHECKING:
from typing import Any
_g_pending_apply = False # pylint: disable=invalid-name
_g_pending_apply = False
class AppConfig(dict):

View file

@ -23,8 +23,8 @@ if TYPE_CHECKING:
import babase
# Our timer and event loop for the ballistica logic thread.
_asyncio_timer: babase.AppTimer | None = None
_asyncio_event_loop: asyncio.AbstractEventLoop | None = None
_g_asyncio_timer: babase.AppTimer | None = None
_g_asyncio_event_loop: asyncio.AbstractEventLoop | None = None
DEBUG_TIMING = os.environ.get('BA_DEBUG_TIMING') == '1'
@ -46,12 +46,12 @@ def setup_asyncio() -> asyncio.AbstractEventLoop:
except RuntimeError:
pass
global _asyncio_event_loop
_asyncio_event_loop = asyncio.new_event_loop()
_asyncio_event_loop.set_default_executor(babase.app.threadpool)
global _g_asyncio_event_loop
_g_asyncio_event_loop = asyncio.new_event_loop()
_g_asyncio_event_loop.set_default_executor(babase.app.threadpool)
# Try to avoid reference loops from exceptions.
_asyncio_event_loop.set_exception_handler(_exception_handler)
_g_asyncio_event_loop.set_exception_handler(_exception_handler)
# Ideally we should integrate asyncio into our C++ Thread class's
# low level event loop so that asyncio timers/sockets/etc. could
@ -62,10 +62,10 @@ def setup_asyncio() -> asyncio.AbstractEventLoop:
# See https://stackoverflow.com/questions/29782377/
# is-it-possible-to-run-only-a-single-step-of-the-asyncio-event-loop
def run_cycle() -> None:
assert _asyncio_event_loop is not None
_asyncio_event_loop.call_soon(_asyncio_event_loop.stop)
assert _g_asyncio_event_loop is not None
_g_asyncio_event_loop.call_soon(_g_asyncio_event_loop.stop)
starttime = time.monotonic() if DEBUG_TIMING else 0
_asyncio_event_loop.run_forever()
_g_asyncio_event_loop.run_forever()
endtime = time.monotonic() if DEBUG_TIMING else 0
# Let's aim to have nothing take longer than 1/120 of a second.
@ -79,21 +79,21 @@ def setup_asyncio() -> asyncio.AbstractEventLoop:
warn_time,
)
global _asyncio_timer
_asyncio_timer = _babase.AppTimer(1.0 / 30.0, run_cycle, repeat=True)
global _g_asyncio_timer
_g_asyncio_timer = _babase.AppTimer(1.0 / 30.0, run_cycle, repeat=True)
if bool(False):
async def aio_test() -> None:
print('TEST AIO TASK STARTING')
assert _asyncio_event_loop is not None
assert asyncio.get_running_loop() is _asyncio_event_loop
assert _g_asyncio_event_loop is not None
assert asyncio.get_running_loop() is _g_asyncio_event_loop
await asyncio.sleep(2.0)
print('TEST AIO TASK ENDING')
_testtask = _asyncio_event_loop.create_task(aio_test())
_testtask = _g_asyncio_event_loop.create_task(aio_test())
return _asyncio_event_loop
return _g_asyncio_event_loop
def _exception_handler(

View file

@ -462,3 +462,36 @@ def copy_dev_console_history() -> None:
_babase.clipboard_set_text('\n'.join(lines))
_babase.screenmessage(Lstr(resource='copyConfirmText'), color=(0, 1, 0))
_babase.getsimplesound('gunCocking').play()
def v2_auth_request(global_app_instance_id: str) -> None | tuple[bool, str]:
"""Kick off or process v2 auth requests.
Return None if no results or (success, error/token)
"""
assert _babase.app.plus is not None
out: None | tuple[bool, str] = _babase.app.plus.accounts.auth_request(
global_app_instance_id
)
return out
def v2_auth_data(token: str) -> None | tuple[str, str, dict]:
"""Look up autheneticated v2 account data via a token."""
assert _babase.in_logic_thread()
classic = _babase.app.classic
if classic is None:
return None
now = time.monotonic()
authdata = classic.v2_auth_datas.get(token)
if authdata is None or authdata.expire_time <= now:
return None
# Success!
return (
authdata.account_id,
authdata.account_tag,
authdata.player_profiles,
)