mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2026-08-15 13:04:30 +00:00
This commit is contained in:
parent
f0fc46a51d
commit
9e0176e424
19 changed files with 374 additions and 72 deletions
2
dist/ba_data/python/babase/__init__.py
vendored
2
dist/ba_data/python/babase/__init__.py
vendored
|
|
@ -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',
|
||||
|
|
|
|||
89
dist/ba_data/python/babase/_accountv2.py
vendored
89
dist/ba_data/python/babase/_accountv2.py
vendored
|
|
@ -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
|
||||
|
|
|
|||
1
dist/ba_data/python/babase/_app.py
vendored
1
dist/ba_data/python/babase/_app.py
vendored
|
|
@ -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
|
||||
|
||||
|
|
|
|||
2
dist/ba_data/python/babase/_appconfig.py
vendored
2
dist/ba_data/python/babase/_appconfig.py
vendored
|
|
@ -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):
|
||||
|
|
|
|||
30
dist/ba_data/python/babase/_asyncio.py
vendored
30
dist/ba_data/python/babase/_asyncio.py
vendored
|
|
@ -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(
|
||||
|
|
|
|||
33
dist/ba_data/python/babase/_hooks.py
vendored
33
dist/ba_data/python/babase/_hooks.py
vendored
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
158
dist/ba_data/python/baclassic/_appsubsystem.py
vendored
158
dist/ba_data/python/baclassic/_appsubsystem.py
vendored
|
|
@ -6,10 +6,12 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import random
|
||||
import logging
|
||||
import weakref
|
||||
from typing import TYPE_CHECKING, override, assert_never
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, override, assert_never, final
|
||||
|
||||
from efro.dataclassio import dataclass_from_dict
|
||||
import babase
|
||||
|
|
@ -26,7 +28,8 @@ from baclassic._store import StoreSubsystem
|
|||
from baclassic import _input
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable, Any, Sequence
|
||||
import datetime
|
||||
from typing import Callable, Any, Sequence, Awaitable
|
||||
|
||||
import bacommon.classic
|
||||
import bacommon.clienteffect as clfx
|
||||
|
|
@ -39,16 +42,72 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class ClassicAppSubsystem(babase.AppSubsystem):
|
||||
"""Subsystem for classic functionality in the app.
|
||||
"""Subsystem for classic bombsquad functionality in the app.
|
||||
|
||||
The single shared instance of this app can be accessed at
|
||||
babase.app.classic. Note that it is possible for babase.app.classic to
|
||||
be None if the classic package is not present, and code should handle
|
||||
that case gracefully.
|
||||
babase.app.classic. Note that it is possible for babase.app.classic
|
||||
to be None if the classic package is not present, and futureproof
|
||||
code should handle that case gracefully.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
@dataclass
|
||||
class V2AuthRequest:
|
||||
"""What is passed in to V2 auth handler."""
|
||||
|
||||
#: V2 account id of the connecting account (a-XXX).
|
||||
account_id: str
|
||||
|
||||
#: Globally unique tag of the connecting account.
|
||||
account_tag: str
|
||||
|
||||
#: When the connecting account was created.
|
||||
account_create_time: datetime.datetime
|
||||
|
||||
#: Total number of days the connecting account has been active.
|
||||
account_total_active_days: int
|
||||
|
||||
#: An abstract value generated from the connecting client's
|
||||
#: app-instance-id. Can be used to identify repeat connection
|
||||
#: attempts/etc. Note that this value changes each time the
|
||||
#: client re-launches the app.
|
||||
app_instance_signature: str
|
||||
|
||||
#: An abstract value generated from the connecting client's ip
|
||||
#: address. Can be used to identify repeat connection
|
||||
#: attempts/etc.
|
||||
address_signature: str
|
||||
|
||||
#: An abstract value generated from the connecting client's
|
||||
#: device. Can be used to identify repeat connection
|
||||
#: attempts/etc. This value may change over time for a given
|
||||
#: device but should be mostly constant.
|
||||
device_signature: str
|
||||
|
||||
@dataclass
|
||||
class V2AuthResponse:
|
||||
"""What a V2 auth handler returns."""
|
||||
|
||||
#: Whether to allow this client to enter the server.
|
||||
allow: bool
|
||||
|
||||
#: A message to be shown to the client if allow is False. A
|
||||
#: defualt rejection message will be shown if none is present
|
||||
#: here. This will be translated using the 'serverResponses'
|
||||
#: translation category so it can be good to use one of the
|
||||
#: entries there if your server has multilingual users.
|
||||
error_message: str | None = None
|
||||
|
||||
@dataclass
|
||||
class V2AuthData:
|
||||
"""Authenticated data we store for accepted clients."""
|
||||
|
||||
account_id: str
|
||||
account_tag: str
|
||||
player_profiles: dict
|
||||
expire_time: float
|
||||
|
||||
from baclassic._music import MusicPlayMode
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
|
@ -95,6 +154,21 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
# Server Mode.
|
||||
self.server: ServerController | None = None
|
||||
|
||||
#: V2 authentication handler.
|
||||
#:
|
||||
#: To customize who is allowed in your server, assign your own
|
||||
#: custom handler function to this attribute. This will be called
|
||||
#: for all connecting clients before they are allowed in the
|
||||
#: game. Note that protocol must be set to 36 or newer and
|
||||
#: authenticate_clients must be enabled. For servers you can set
|
||||
#: those values in the server config.
|
||||
self.v2_auth_handler: Callable[
|
||||
[ClassicAppSubsystem.V2AuthRequest],
|
||||
Awaitable[ClassicAppSubsystem.V2AuthResponse],
|
||||
] = self.default_v2_auth_handler
|
||||
self.v2_auth_datas: dict[str, ClassicAppSubsystem.V2AuthData] = {}
|
||||
|
||||
# Logging/debugging.
|
||||
self.log_have_new = False
|
||||
self.log_upload_timer_started = False
|
||||
self.printed_live_object_warning = False
|
||||
|
|
@ -129,8 +203,48 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
self.pro_sale_start_time: int | None = None
|
||||
self.pro_sale_start_val: int | None = None
|
||||
|
||||
async def default_v2_auth_handler(
|
||||
self, request: V2AuthRequest
|
||||
) -> V2AuthResponse:
|
||||
"""Default auth handler function. Just allows everyone."""
|
||||
|
||||
# A custom handler would look at request here to determine
|
||||
# whether to let this client in.
|
||||
del request # Unused.
|
||||
|
||||
return self.V2AuthResponse(allow=True)
|
||||
|
||||
@final
|
||||
async def run_v2_auth_handler(
|
||||
self, request: V2AuthRequest, player_profiles: Any, token: str
|
||||
) -> V2AuthResponse:
|
||||
""":meta private:"""
|
||||
assert babase.in_logic_thread()
|
||||
result = await self.v2_auth_handler(request)
|
||||
|
||||
# If it was accepted, keep their auth data around just long enough
|
||||
# for them to connect.
|
||||
if result.allow:
|
||||
now = time.monotonic()
|
||||
self.v2_auth_datas[token] = self.V2AuthData(
|
||||
account_id=request.account_id,
|
||||
account_tag=request.account_tag,
|
||||
player_profiles=player_profiles,
|
||||
expire_time=now + 30.0,
|
||||
)
|
||||
|
||||
# Lazily prune expired auth-data.
|
||||
if any(a.expire_time <= now for a in self.v2_auth_datas.values()):
|
||||
self.v2_auth_datas = {
|
||||
k: v
|
||||
for k, v in self.v2_auth_datas.items()
|
||||
if v.expire_time > now
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def add_main_menu_close_callback(self, call: Callable[[], Any]) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
|
||||
# If there's no main window up, just call immediately.
|
||||
if not babase.app.ui_v1.has_main_window():
|
||||
|
|
@ -174,7 +288,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
return self._env['platform']
|
||||
|
||||
def scene_v1_protocol_version(self) -> int:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
return bascenev1.protocol_version()
|
||||
|
||||
@property
|
||||
|
|
@ -464,7 +578,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
)
|
||||
|
||||
def game_begin_analytics(self) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from baclassic import _analytics
|
||||
|
||||
_analytics.game_begin_analytics()
|
||||
|
|
@ -656,11 +770,11 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
return bascenev1.get_player_profile_colors(profilename, profiles)
|
||||
|
||||
def get_foreground_host_session(self) -> bascenev1.Session | None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
return bascenev1.get_foreground_host_session()
|
||||
|
||||
def get_foreground_host_activity(self) -> bascenev1.Activity | None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
return bascenev1.get_foreground_host_activity()
|
||||
|
||||
def value_test(
|
||||
|
|
@ -669,26 +783,26 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
change: float | None = None,
|
||||
absolute: float | None = None,
|
||||
) -> float:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
return _baclassic.value_test(arg, change, absolute)
|
||||
|
||||
def set_master_server_source(self, source: int) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
bascenev1.set_master_server_source(source)
|
||||
|
||||
def get_game_port(self) -> int:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
return bascenev1.get_game_port()
|
||||
|
||||
def v2_upgrade_window(self, login_name: str, code: str) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
|
||||
from bauiv1lib.v2upgrade import V2UpgradeWindow
|
||||
|
||||
V2UpgradeWindow(login_name, code)
|
||||
|
||||
def server_dialog(self, delay: float, data: dict[str, Any]) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from bauiv1lib.serverdialog import (
|
||||
ServerDialogData,
|
||||
ServerDialogWindow,
|
||||
|
|
@ -709,13 +823,13 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
)
|
||||
|
||||
def show_url_window(self, address: str) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from bauiv1lib.url import ShowURLWindow
|
||||
|
||||
ShowURLWindow(address)
|
||||
|
||||
def quit_window(self, quit_type: babase.QuitType) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from bauiv1lib.confirm import QuitWindow
|
||||
|
||||
QuitWindow(quit_type)
|
||||
|
|
@ -731,7 +845,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
offset: tuple[float, float] = (0.0, 0.0),
|
||||
on_close_call: Callable[[], Any] | None = None,
|
||||
) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from bauiv1lib.tournamententry import TournamentEntryWindow
|
||||
|
||||
TournamentEntryWindow(
|
||||
|
|
@ -745,7 +859,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
)
|
||||
|
||||
def get_main_menu_session(self) -> type[bascenev1.Session]:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from bascenev1lib.mainmenu import MainMenuSession
|
||||
|
||||
return MainMenuSession
|
||||
|
|
@ -796,7 +910,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
logging.exception('Error preloading map preview media.')
|
||||
|
||||
def party_icon_activate(self, origin: Sequence[float]) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from bauiv1lib.party import PartyWindow
|
||||
from babase import app
|
||||
|
||||
|
|
@ -817,7 +931,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
|||
self.party_window = weakref.ref(PartyWindow(origin=origin))
|
||||
|
||||
def request_main_ui(self) -> None:
|
||||
"""(internal)"""
|
||||
""":meta private:"""
|
||||
from bauiv1lib.ingamemenu import InGameMenuWindow
|
||||
|
||||
assert babase.app is not None
|
||||
|
|
|
|||
11
dist/ba_data/python/baclassic/_servermode.py
vendored
11
dist/ba_data/python/baclassic/_servermode.py
vendored
|
|
@ -108,6 +108,12 @@ class ServerController:
|
|||
self._playlist_fetch_got_response = False
|
||||
self._playlist_fetch_code = -1
|
||||
|
||||
# We do most configuration *after* we've fetched playlists or
|
||||
# whatever other prep stuff we need to do, but this we should
|
||||
# set immediately; otherwise unauthenticated clients can sneak
|
||||
# into auth-enabled servers while they're bootstrapping.
|
||||
bascenev1.set_authenticate_clients(self._config.authenticate_clients)
|
||||
|
||||
# Now sit around doing any pre-launch prep such as waiting for
|
||||
# account sign-in or fetching playlists; this will kick off the
|
||||
# session once done.
|
||||
|
|
@ -428,8 +434,6 @@ class ServerController:
|
|||
classic.teams_series_length = self._config.teams_series_length
|
||||
classic.ffa_series_length = self._config.ffa_series_length
|
||||
|
||||
bascenev1.set_authenticate_clients(self._config.authenticate_clients)
|
||||
|
||||
bascenev1.set_enable_default_kick_voting(
|
||||
self._config.enable_default_kick_voting
|
||||
)
|
||||
|
|
@ -452,7 +456,6 @@ class ServerController:
|
|||
bascenev1.set_player_rejoin_cooldown(
|
||||
self._config.player_rejoin_cooldown
|
||||
)
|
||||
|
||||
bascenev1.set_max_players_override(
|
||||
self._config.session_max_players_override
|
||||
)
|
||||
|
|
@ -471,6 +474,6 @@ class ServerController:
|
|||
bascenev1.new_host_session(sessiontype)
|
||||
|
||||
# Run an access check if we're trying to make a public party.
|
||||
if not self._ran_access_check :
|
||||
if self._config.party_is_public and not self._ran_access_check:
|
||||
self._run_access_check()
|
||||
self._ran_access_check = True
|
||||
|
|
|
|||
22
dist/ba_data/python/bacommon/cloud.py
vendored
22
dist/ba_data/python/bacommon/cloud.py
vendored
|
|
@ -462,3 +462,25 @@ class AnalyticsEventMessage(Message):
|
|||
"""Have a nice analytics event!"""
|
||||
|
||||
event: Annotated[AnalyticsEvent, IOAttrs('e')]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class AuthRequestMessage(Message):
|
||||
"""Request access to a server for a current account."""
|
||||
|
||||
global_app_instance_uuid: Annotated[str, IOAttrs('a')]
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def get_response_types(cls) -> list[type[Response] | None]:
|
||||
return [AuthRequestResponse]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class AuthRequestResponse(Response):
|
||||
"""Here's that access ya asked for boss."""
|
||||
|
||||
error: Annotated[str | None, IOAttrs('e')]
|
||||
token: Annotated[str | None, IOAttrs('t')]
|
||||
|
|
|
|||
17
dist/ba_data/python/bacommon/servermanager.py
vendored
17
dist/ba_data/python/bacommon/servermanager.py
vendored
|
|
@ -30,13 +30,19 @@ class ServerConfig:
|
|||
# If True, all connecting clients will be authenticated through the
|
||||
# master server to screen for fake account info. Generally this
|
||||
# should always be enabled unless you are hosting on a LAN with no
|
||||
# internet connection.
|
||||
# internet connection. Note that if you set protocol_version to 36
|
||||
# or newer, client authentication uses V2 account info. This is
|
||||
# highly recommended as it does not have spoofing vulnerabilities
|
||||
# like the earlier V1 authentication.
|
||||
authenticate_clients: bool = True
|
||||
|
||||
# IDs of server admins. Server admins are not kickable through the
|
||||
# default kick vote system and they are able to kick players without
|
||||
# a vote. To get your account id, enter 'getaccountid' in
|
||||
# settings->advanced->enter-code.
|
||||
# a vote. If protocol_version is set to 36 or newer this will use V2
|
||||
# account ids (a-XXX); otherwise it will use V1 ids (pb-XXX). To get
|
||||
# your V2 account id, poke the 'manage account' button in the
|
||||
# account window in-game. To get your V1 account id, enter
|
||||
# 'getaccountid' in Settings->Advanced->Send Info
|
||||
admins: list[str] = field(default_factory=list)
|
||||
|
||||
# Whether the default kick-voting system is enabled.
|
||||
|
|
@ -176,7 +182,10 @@ class ServerConfig:
|
|||
# Protocol version we host with. Currently the default is 33 which
|
||||
# still allows older 1.4 game clients to connect. Explicitly setting
|
||||
# to 35 no longer allows those clients but adds/fixes a few things
|
||||
# such as making camera shake properly work in net games.
|
||||
# such as making camera shake properly work in net games. Protocol
|
||||
# 36 enables V2 account ids (a-XXX) for client authentication, which
|
||||
# does not suffer from spoofing vulnerabilities that V1 account ids
|
||||
# (pb-XXX) did.
|
||||
protocol_version: int | None = None
|
||||
|
||||
# (internal) stress-testing mode.
|
||||
|
|
|
|||
2
dist/ba_data/python/baenv.py
vendored
2
dist/ba_data/python/baenv.py
vendored
|
|
@ -57,7 +57,7 @@ logger = logging.getLogger('ba.env')
|
|||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 22712
|
||||
TARGET_BALLISTICA_BUILD = 22714
|
||||
TARGET_BALLISTICA_VERSION = '1.7.61'
|
||||
|
||||
|
||||
|
|
|
|||
10
dist/ba_data/python/baplus/_cloud.py
vendored
10
dist/ba_data/python/baplus/_cloud.py
vendored
|
|
@ -332,6 +332,16 @@ class CloudSubsystem(babase.AppSubsystem):
|
|||
],
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def send_message_cb(
|
||||
self,
|
||||
msg: bacommon.cloud.AuthRequestMessage,
|
||||
on_response: Callable[
|
||||
[bacommon.cloud.AuthRequestResponse | Exception],
|
||||
None,
|
||||
],
|
||||
) -> None: ...
|
||||
|
||||
def send_message_cb(
|
||||
self,
|
||||
msg: Message,
|
||||
|
|
|
|||
8
dist/ba_data/python/bascenev1/_lobby.py
vendored
8
dist/ba_data/python/bascenev1/_lobby.py
vendored
|
|
@ -339,10 +339,10 @@ class Chooser:
|
|||
if inputdevice.is_controller_app and '_random' in profilenames:
|
||||
return profilenames.index('_random')
|
||||
|
||||
# If its a client connection, for now just force
|
||||
# the account profile if possible.. (need to provide a
|
||||
# way for clients to specify/remember their default
|
||||
# profile on remote servers that do not already know them).
|
||||
# If its a client connection, for now just force the account
|
||||
# profile if possible. (need to provide a way for clients to
|
||||
# specify/remember their default profile on remote servers that
|
||||
# do not already know them).
|
||||
if inputdevice.is_remote_client and '__account__' in profilenames:
|
||||
return profilenames.index('__account__')
|
||||
|
||||
|
|
|
|||
10
dist/ba_data/python/bascenev1/_session.py
vendored
10
dist/ba_data/python/bascenev1/_session.py
vendored
|
|
@ -25,7 +25,7 @@ if TYPE_CHECKING:
|
|||
_g_player_rejoin_cooldown: float = 0.0
|
||||
|
||||
# overrides the session's decision of max_players
|
||||
_max_players_override: int | None = None
|
||||
_g_max_players_override: int | None = None
|
||||
|
||||
|
||||
def set_player_rejoin_cooldown(cooldown: float) -> None:
|
||||
|
|
@ -36,8 +36,8 @@ def set_player_rejoin_cooldown(cooldown: float) -> None:
|
|||
|
||||
def set_max_players_override(max_players: int | None) -> None:
|
||||
"""Set the override for how many players can join a session"""
|
||||
global _max_players_override # pylint: disable=global-statement
|
||||
_max_players_override = max_players
|
||||
global _g_max_players_override # pylint: disable=global-statement
|
||||
_g_max_players_override = max_players
|
||||
|
||||
|
||||
class Session:
|
||||
|
|
@ -176,8 +176,8 @@ class Session:
|
|||
self.min_players = min_players
|
||||
self.max_players = (
|
||||
max_players
|
||||
if _max_players_override is None
|
||||
else _max_players_override
|
||||
if _g_max_players_override is None
|
||||
else _g_max_players_override
|
||||
)
|
||||
self.submit_score = submit_score
|
||||
|
||||
|
|
|
|||
4
dist/ba_data/python/bauiv1/__init__.py
vendored
4
dist/ba_data/python/bauiv1/__init__.py
vendored
|
|
@ -14,8 +14,6 @@ from __future__ import annotations
|
|||
|
||||
# pylint: disable=redefined-builtin
|
||||
|
||||
import logging
|
||||
|
||||
from babase import (
|
||||
accountlog,
|
||||
AccountV2Handle,
|
||||
|
|
@ -319,7 +317,7 @@ __all__ = [
|
|||
if __debug__:
|
||||
for _mdl in 'babase', '_babase':
|
||||
if not hasattr(__import__(_mdl), '_REACHED_END_OF_MODULE'):
|
||||
logging.warning(
|
||||
balog.warning(
|
||||
'%s was imported before %s finished importing;'
|
||||
' should not happen.',
|
||||
__name__,
|
||||
|
|
|
|||
|
|
@ -45,14 +45,17 @@ class NetScanner:
|
|||
bui.widget(edit=self._columnwidget, up_widget=tab_button)
|
||||
self._width = width
|
||||
self._last_selected_host: dict[str, Any] | None = None
|
||||
self._last_scan: list[dict[str, str]] | None = None
|
||||
|
||||
self._update_timer = bui.AppTimer(
|
||||
1.0, bui.WeakCallStrict(self.update), repeat=True
|
||||
1.0, bui.WeakCallStrict(self._update), repeat=True
|
||||
)
|
||||
# Go ahead and run a few *almost* immediately so we don't
|
||||
# have to wait a second.
|
||||
self.update()
|
||||
bui.apptimer(0.25, bui.WeakCallStrict(self.update))
|
||||
# Run two cycles pretty immediately - this should send out a
|
||||
# "who's there" and update the list with any immediate-ish
|
||||
# results so we may not have to wait a second to see things
|
||||
# appear.
|
||||
self._update()
|
||||
bui.apptimer(0.25, bui.WeakCallStrict(self._update))
|
||||
|
||||
def __del__(self) -> None:
|
||||
bs.end_host_scanning()
|
||||
|
|
@ -74,23 +77,33 @@ class NetScanner:
|
|||
|
||||
bs.connect_to_party(host['address'])
|
||||
|
||||
def update(self) -> None:
|
||||
def _update(self) -> None:
|
||||
"""(internal)"""
|
||||
|
||||
# In case our UI was killed from under us.
|
||||
if not self._columnwidget:
|
||||
print(
|
||||
f'ERROR: NetScanner running without UI at time {bui.apptime()}.'
|
||||
bui.uilog.error(
|
||||
'nearbytab NetScanner running without UI at time %s.',
|
||||
bui.apptime(),
|
||||
)
|
||||
return
|
||||
|
||||
hosts = bs.host_scan_cycle()
|
||||
|
||||
# If nothing has changed since our last run, do nothing. If we
|
||||
# do redundant rebuilds then we are likely to lose some clicks
|
||||
# due to rebuilding after a click starts but before it ends.
|
||||
if hosts == self._last_scan:
|
||||
return
|
||||
|
||||
self._last_scan = hosts
|
||||
|
||||
t_scale = 1.6
|
||||
for child in self._columnwidget.get_children():
|
||||
child.delete()
|
||||
|
||||
# Grab this now this since adding widgets will change it.
|
||||
last_selected_host = self._last_selected_host
|
||||
hosts = bs.host_scan_cycle()
|
||||
for i, host in enumerate(hosts):
|
||||
txt3 = bui.textwidget(
|
||||
parent=self._columnwidget,
|
||||
|
|
|
|||
2
dist/ba_data/python/efro/terminal.py
vendored
2
dist/ba_data/python/efro/terminal.py
vendored
|
|
@ -315,7 +315,7 @@ class ClrNever(ClrBase):
|
|||
|
||||
|
||||
_envval = os.environ.get('EFRO_TERMCOLORS')
|
||||
color_enabled: bool = (
|
||||
color_enabled: bool = ( # pylint: disable=invalid-name
|
||||
True
|
||||
if _envval == '1'
|
||||
else False if _envval == '0' else _default_color_enabled()
|
||||
|
|
|
|||
14
dist/ba_data/python/efro/util.py
vendored
14
dist/ba_data/python/efro/util.py
vendored
|
|
@ -1089,3 +1089,17 @@ def strip_exception_tracebacks(exc: BaseException) -> None:
|
|||
cause = getattr(e, '__cause__', None)
|
||||
if cause is not None:
|
||||
stack.append(cause)
|
||||
|
||||
|
||||
def secure_id() -> str:
|
||||
"""Generate a 20 char cryptographically secure string.
|
||||
|
||||
Basically what firestore does for its random document ids.
|
||||
If its good enough for firestore its good enough for us.
|
||||
"""
|
||||
import secrets
|
||||
import string
|
||||
|
||||
alphabet = string.ascii_letters + string.digits # 62 chars
|
||||
|
||||
return ''.join(secrets.choice(alphabet) for _ in range(20))
|
||||
|
|
|
|||
BIN
dist/bombsquad_headless
vendored
BIN
dist/bombsquad_headless
vendored
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue