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,
|
add_clean_frame_callback,
|
||||||
allows_ticket_sales,
|
allows_ticket_sales,
|
||||||
android_get_external_files_dir,
|
android_get_external_files_dir,
|
||||||
app_instance_uuid,
|
|
||||||
appname,
|
appname,
|
||||||
appnameupper,
|
appnameupper,
|
||||||
apptime,
|
apptime,
|
||||||
|
|
@ -234,7 +233,6 @@ __all__ = [
|
||||||
'AppIntentExec',
|
'AppIntentExec',
|
||||||
'AppMode',
|
'AppMode',
|
||||||
'AppState',
|
'AppState',
|
||||||
'app_instance_uuid',
|
|
||||||
'applog',
|
'applog',
|
||||||
'appname',
|
'appname',
|
||||||
'appnameupper',
|
'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
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING, assert_never
|
from typing import TYPE_CHECKING, assert_never
|
||||||
|
|
||||||
from efro.error import CommunicationError
|
from efro.error import CommunicationError
|
||||||
|
|
@ -19,6 +21,8 @@ import _babase
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
import bacommon.cloud
|
||||||
|
|
||||||
from babase._login import LoginAdapter, LoginInfo
|
from babase._login import LoginAdapter, LoginInfo
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -62,6 +66,9 @@ class AccountV2Subsystem:
|
||||||
Callable[[AccountV2Handle | None], None]
|
Callable[[AccountV2Handle | None], None]
|
||||||
] = CallbackSet()
|
] = CallbackSet()
|
||||||
|
|
||||||
|
# Request state per global-app-instance-id
|
||||||
|
self._auth_requests: dict[str, _AuthRequest] = {}
|
||||||
|
|
||||||
adapter: LoginAdapter
|
adapter: LoginAdapter
|
||||||
if _babase.using_google_play_game_services():
|
if _babase.using_google_play_game_services():
|
||||||
adapter = LoginAdapterGPGS()
|
adapter = LoginAdapterGPGS()
|
||||||
|
|
@ -104,6 +111,9 @@ class AccountV2Subsystem:
|
||||||
"""
|
"""
|
||||||
assert _babase.in_logic_thread()
|
assert _babase.in_logic_thread()
|
||||||
|
|
||||||
|
# Blow away any outstanding auth-requests.
|
||||||
|
self._auth_requests = {}
|
||||||
|
|
||||||
# Inform the base layer of new names/etc.
|
# Inform the base layer of new names/etc.
|
||||||
if account is not None:
|
if account is not None:
|
||||||
_babase.set_account_sign_in_state(True, account.tag)
|
_babase.set_account_sign_in_state(True, account.tag)
|
||||||
|
|
@ -201,6 +211,78 @@ class AccountV2Subsystem:
|
||||||
self._initial_sign_in_completed = True
|
self._initial_sign_in_completed = True
|
||||||
_babase.app.on_initial_sign_in_complete()
|
_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
|
@staticmethod
|
||||||
def _hashstr(val: str) -> str:
|
def _hashstr(val: str) -> str:
|
||||||
md5 = hashlib.md5()
|
md5 = hashlib.md5()
|
||||||
|
|
@ -501,3 +583,10 @@ class AccountV2Handle:
|
||||||
|
|
||||||
This allows cloud messages to be sent on our behalf.
|
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
|
loop. Hopefully this situation will be improved in the future
|
||||||
with a unified event loop.
|
with a unified event loop.
|
||||||
"""
|
"""
|
||||||
assert _babase.in_logic_thread()
|
|
||||||
assert self._asyncio_loop is not None
|
assert self._asyncio_loop is not None
|
||||||
return self._asyncio_loop
|
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:
|
if TYPE_CHECKING:
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
_g_pending_apply = False # pylint: disable=invalid-name
|
_g_pending_apply = False
|
||||||
|
|
||||||
|
|
||||||
class AppConfig(dict):
|
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
|
import babase
|
||||||
|
|
||||||
# Our timer and event loop for the ballistica logic thread.
|
# Our timer and event loop for the ballistica logic thread.
|
||||||
_asyncio_timer: babase.AppTimer | None = None
|
_g_asyncio_timer: babase.AppTimer | None = None
|
||||||
_asyncio_event_loop: asyncio.AbstractEventLoop | None = None
|
_g_asyncio_event_loop: asyncio.AbstractEventLoop | None = None
|
||||||
|
|
||||||
DEBUG_TIMING = os.environ.get('BA_DEBUG_TIMING') == '1'
|
DEBUG_TIMING = os.environ.get('BA_DEBUG_TIMING') == '1'
|
||||||
|
|
||||||
|
|
@ -46,12 +46,12 @@ def setup_asyncio() -> asyncio.AbstractEventLoop:
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
global _asyncio_event_loop
|
global _g_asyncio_event_loop
|
||||||
_asyncio_event_loop = asyncio.new_event_loop()
|
_g_asyncio_event_loop = asyncio.new_event_loop()
|
||||||
_asyncio_event_loop.set_default_executor(babase.app.threadpool)
|
_g_asyncio_event_loop.set_default_executor(babase.app.threadpool)
|
||||||
|
|
||||||
# Try to avoid reference loops from exceptions.
|
# 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
|
# Ideally we should integrate asyncio into our C++ Thread class's
|
||||||
# low level event loop so that asyncio timers/sockets/etc. could
|
# 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/
|
# See https://stackoverflow.com/questions/29782377/
|
||||||
# is-it-possible-to-run-only-a-single-step-of-the-asyncio-event-loop
|
# is-it-possible-to-run-only-a-single-step-of-the-asyncio-event-loop
|
||||||
def run_cycle() -> None:
|
def run_cycle() -> None:
|
||||||
assert _asyncio_event_loop is not None
|
assert _g_asyncio_event_loop is not None
|
||||||
_asyncio_event_loop.call_soon(_asyncio_event_loop.stop)
|
_g_asyncio_event_loop.call_soon(_g_asyncio_event_loop.stop)
|
||||||
starttime = time.monotonic() if DEBUG_TIMING else 0
|
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
|
endtime = time.monotonic() if DEBUG_TIMING else 0
|
||||||
|
|
||||||
# Let's aim to have nothing take longer than 1/120 of a second.
|
# 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,
|
warn_time,
|
||||||
)
|
)
|
||||||
|
|
||||||
global _asyncio_timer
|
global _g_asyncio_timer
|
||||||
_asyncio_timer = _babase.AppTimer(1.0 / 30.0, run_cycle, repeat=True)
|
_g_asyncio_timer = _babase.AppTimer(1.0 / 30.0, run_cycle, repeat=True)
|
||||||
|
|
||||||
if bool(False):
|
if bool(False):
|
||||||
|
|
||||||
async def aio_test() -> None:
|
async def aio_test() -> None:
|
||||||
print('TEST AIO TASK STARTING')
|
print('TEST AIO TASK STARTING')
|
||||||
assert _asyncio_event_loop is not None
|
assert _g_asyncio_event_loop is not None
|
||||||
assert asyncio.get_running_loop() is _asyncio_event_loop
|
assert asyncio.get_running_loop() is _g_asyncio_event_loop
|
||||||
await asyncio.sleep(2.0)
|
await asyncio.sleep(2.0)
|
||||||
print('TEST AIO TASK ENDING')
|
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(
|
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.clipboard_set_text('\n'.join(lines))
|
||||||
_babase.screenmessage(Lstr(resource='copyConfirmText'), color=(0, 1, 0))
|
_babase.screenmessage(Lstr(resource='copyConfirmText'), color=(0, 1, 0))
|
||||||
_babase.getsimplesound('gunCocking').play()
|
_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
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import time
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
import weakref
|
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
|
from efro.dataclassio import dataclass_from_dict
|
||||||
import babase
|
import babase
|
||||||
|
|
@ -26,7 +28,8 @@ from baclassic._store import StoreSubsystem
|
||||||
from baclassic import _input
|
from baclassic import _input
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Callable, Any, Sequence
|
import datetime
|
||||||
|
from typing import Callable, Any, Sequence, Awaitable
|
||||||
|
|
||||||
import bacommon.classic
|
import bacommon.classic
|
||||||
import bacommon.clienteffect as clfx
|
import bacommon.clienteffect as clfx
|
||||||
|
|
@ -39,16 +42,72 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class ClassicAppSubsystem(babase.AppSubsystem):
|
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
|
The single shared instance of this app can be accessed at
|
||||||
babase.app.classic. Note that it is possible for babase.app.classic to
|
babase.app.classic. Note that it is possible for babase.app.classic
|
||||||
be None if the classic package is not present, and code should handle
|
to be None if the classic package is not present, and futureproof
|
||||||
that case gracefully.
|
code should handle that case gracefully.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# 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
|
from baclassic._music import MusicPlayMode
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
@ -95,6 +154,21 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
# Server Mode.
|
# Server Mode.
|
||||||
self.server: ServerController | None = None
|
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_have_new = False
|
||||||
self.log_upload_timer_started = False
|
self.log_upload_timer_started = False
|
||||||
self.printed_live_object_warning = 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_time: int | None = None
|
||||||
self.pro_sale_start_val: 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:
|
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 there's no main window up, just call immediately.
|
||||||
if not babase.app.ui_v1.has_main_window():
|
if not babase.app.ui_v1.has_main_window():
|
||||||
|
|
@ -174,7 +288,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
return self._env['platform']
|
return self._env['platform']
|
||||||
|
|
||||||
def scene_v1_protocol_version(self) -> int:
|
def scene_v1_protocol_version(self) -> int:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
return bascenev1.protocol_version()
|
return bascenev1.protocol_version()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -464,7 +578,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
)
|
)
|
||||||
|
|
||||||
def game_begin_analytics(self) -> None:
|
def game_begin_analytics(self) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from baclassic import _analytics
|
from baclassic import _analytics
|
||||||
|
|
||||||
_analytics.game_begin_analytics()
|
_analytics.game_begin_analytics()
|
||||||
|
|
@ -656,11 +770,11 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
return bascenev1.get_player_profile_colors(profilename, profiles)
|
return bascenev1.get_player_profile_colors(profilename, profiles)
|
||||||
|
|
||||||
def get_foreground_host_session(self) -> bascenev1.Session | None:
|
def get_foreground_host_session(self) -> bascenev1.Session | None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
return bascenev1.get_foreground_host_session()
|
return bascenev1.get_foreground_host_session()
|
||||||
|
|
||||||
def get_foreground_host_activity(self) -> bascenev1.Activity | None:
|
def get_foreground_host_activity(self) -> bascenev1.Activity | None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
return bascenev1.get_foreground_host_activity()
|
return bascenev1.get_foreground_host_activity()
|
||||||
|
|
||||||
def value_test(
|
def value_test(
|
||||||
|
|
@ -669,26 +783,26 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
change: float | None = None,
|
change: float | None = None,
|
||||||
absolute: float | None = None,
|
absolute: float | None = None,
|
||||||
) -> float:
|
) -> float:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
return _baclassic.value_test(arg, change, absolute)
|
return _baclassic.value_test(arg, change, absolute)
|
||||||
|
|
||||||
def set_master_server_source(self, source: int) -> None:
|
def set_master_server_source(self, source: int) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
bascenev1.set_master_server_source(source)
|
bascenev1.set_master_server_source(source)
|
||||||
|
|
||||||
def get_game_port(self) -> int:
|
def get_game_port(self) -> int:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
return bascenev1.get_game_port()
|
return bascenev1.get_game_port()
|
||||||
|
|
||||||
def v2_upgrade_window(self, login_name: str, code: str) -> None:
|
def v2_upgrade_window(self, login_name: str, code: str) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
|
|
||||||
from bauiv1lib.v2upgrade import V2UpgradeWindow
|
from bauiv1lib.v2upgrade import V2UpgradeWindow
|
||||||
|
|
||||||
V2UpgradeWindow(login_name, code)
|
V2UpgradeWindow(login_name, code)
|
||||||
|
|
||||||
def server_dialog(self, delay: float, data: dict[str, Any]) -> None:
|
def server_dialog(self, delay: float, data: dict[str, Any]) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from bauiv1lib.serverdialog import (
|
from bauiv1lib.serverdialog import (
|
||||||
ServerDialogData,
|
ServerDialogData,
|
||||||
ServerDialogWindow,
|
ServerDialogWindow,
|
||||||
|
|
@ -709,13 +823,13 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
)
|
)
|
||||||
|
|
||||||
def show_url_window(self, address: str) -> None:
|
def show_url_window(self, address: str) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from bauiv1lib.url import ShowURLWindow
|
from bauiv1lib.url import ShowURLWindow
|
||||||
|
|
||||||
ShowURLWindow(address)
|
ShowURLWindow(address)
|
||||||
|
|
||||||
def quit_window(self, quit_type: babase.QuitType) -> None:
|
def quit_window(self, quit_type: babase.QuitType) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from bauiv1lib.confirm import QuitWindow
|
from bauiv1lib.confirm import QuitWindow
|
||||||
|
|
||||||
QuitWindow(quit_type)
|
QuitWindow(quit_type)
|
||||||
|
|
@ -731,7 +845,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
offset: tuple[float, float] = (0.0, 0.0),
|
offset: tuple[float, float] = (0.0, 0.0),
|
||||||
on_close_call: Callable[[], Any] | None = None,
|
on_close_call: Callable[[], Any] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from bauiv1lib.tournamententry import TournamentEntryWindow
|
from bauiv1lib.tournamententry import TournamentEntryWindow
|
||||||
|
|
||||||
TournamentEntryWindow(
|
TournamentEntryWindow(
|
||||||
|
|
@ -745,7 +859,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_main_menu_session(self) -> type[bascenev1.Session]:
|
def get_main_menu_session(self) -> type[bascenev1.Session]:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from bascenev1lib.mainmenu import MainMenuSession
|
from bascenev1lib.mainmenu import MainMenuSession
|
||||||
|
|
||||||
return MainMenuSession
|
return MainMenuSession
|
||||||
|
|
@ -796,7 +910,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
logging.exception('Error preloading map preview media.')
|
logging.exception('Error preloading map preview media.')
|
||||||
|
|
||||||
def party_icon_activate(self, origin: Sequence[float]) -> None:
|
def party_icon_activate(self, origin: Sequence[float]) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from bauiv1lib.party import PartyWindow
|
from bauiv1lib.party import PartyWindow
|
||||||
from babase import app
|
from babase import app
|
||||||
|
|
||||||
|
|
@ -817,7 +931,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
self.party_window = weakref.ref(PartyWindow(origin=origin))
|
self.party_window = weakref.ref(PartyWindow(origin=origin))
|
||||||
|
|
||||||
def request_main_ui(self) -> None:
|
def request_main_ui(self) -> None:
|
||||||
"""(internal)"""
|
""":meta private:"""
|
||||||
from bauiv1lib.ingamemenu import InGameMenuWindow
|
from bauiv1lib.ingamemenu import InGameMenuWindow
|
||||||
|
|
||||||
assert babase.app is not None
|
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_got_response = False
|
||||||
self._playlist_fetch_code = -1
|
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
|
# Now sit around doing any pre-launch prep such as waiting for
|
||||||
# account sign-in or fetching playlists; this will kick off the
|
# account sign-in or fetching playlists; this will kick off the
|
||||||
# session once done.
|
# session once done.
|
||||||
|
|
@ -428,8 +434,6 @@ class ServerController:
|
||||||
classic.teams_series_length = self._config.teams_series_length
|
classic.teams_series_length = self._config.teams_series_length
|
||||||
classic.ffa_series_length = self._config.ffa_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(
|
bascenev1.set_enable_default_kick_voting(
|
||||||
self._config.enable_default_kick_voting
|
self._config.enable_default_kick_voting
|
||||||
)
|
)
|
||||||
|
|
@ -452,7 +456,6 @@ class ServerController:
|
||||||
bascenev1.set_player_rejoin_cooldown(
|
bascenev1.set_player_rejoin_cooldown(
|
||||||
self._config.player_rejoin_cooldown
|
self._config.player_rejoin_cooldown
|
||||||
)
|
)
|
||||||
|
|
||||||
bascenev1.set_max_players_override(
|
bascenev1.set_max_players_override(
|
||||||
self._config.session_max_players_override
|
self._config.session_max_players_override
|
||||||
)
|
)
|
||||||
|
|
@ -471,6 +474,6 @@ class ServerController:
|
||||||
bascenev1.new_host_session(sessiontype)
|
bascenev1.new_host_session(sessiontype)
|
||||||
|
|
||||||
# Run an access check if we're trying to make a public party.
|
# 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._run_access_check()
|
||||||
self._ran_access_check = True
|
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!"""
|
"""Have a nice analytics event!"""
|
||||||
|
|
||||||
event: Annotated[AnalyticsEvent, IOAttrs('e')]
|
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
|
# If True, all connecting clients will be authenticated through the
|
||||||
# master server to screen for fake account info. Generally this
|
# master server to screen for fake account info. Generally this
|
||||||
# should always be enabled unless you are hosting on a LAN with no
|
# 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
|
authenticate_clients: bool = True
|
||||||
|
|
||||||
# IDs of server admins. Server admins are not kickable through the
|
# IDs of server admins. Server admins are not kickable through the
|
||||||
# default kick vote system and they are able to kick players without
|
# default kick vote system and they are able to kick players without
|
||||||
# a vote. To get your account id, enter 'getaccountid' in
|
# a vote. If protocol_version is set to 36 or newer this will use V2
|
||||||
# settings->advanced->enter-code.
|
# 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)
|
admins: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
# Whether the default kick-voting system is enabled.
|
# 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
|
# Protocol version we host with. Currently the default is 33 which
|
||||||
# still allows older 1.4 game clients to connect. Explicitly setting
|
# still allows older 1.4 game clients to connect. Explicitly setting
|
||||||
# to 35 no longer allows those clients but adds/fixes a few things
|
# 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
|
protocol_version: int | None = None
|
||||||
|
|
||||||
# (internal) stress-testing mode.
|
# (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
|
# Build number and version of the ballistica binary we expect to be
|
||||||
# using.
|
# using.
|
||||||
TARGET_BALLISTICA_BUILD = 22712
|
TARGET_BALLISTICA_BUILD = 22714
|
||||||
TARGET_BALLISTICA_VERSION = '1.7.61'
|
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: ...
|
) -> None: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def send_message_cb(
|
||||||
|
self,
|
||||||
|
msg: bacommon.cloud.AuthRequestMessage,
|
||||||
|
on_response: Callable[
|
||||||
|
[bacommon.cloud.AuthRequestResponse | Exception],
|
||||||
|
None,
|
||||||
|
],
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: Message,
|
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:
|
if inputdevice.is_controller_app and '_random' in profilenames:
|
||||||
return profilenames.index('_random')
|
return profilenames.index('_random')
|
||||||
|
|
||||||
# If its a client connection, for now just force
|
# If its a client connection, for now just force the account
|
||||||
# the account profile if possible.. (need to provide a
|
# profile if possible. (need to provide a way for clients to
|
||||||
# way for clients to specify/remember their default
|
# specify/remember their default profile on remote servers that
|
||||||
# profile on remote servers that do not already know them).
|
# do not already know them).
|
||||||
if inputdevice.is_remote_client and '__account__' in profilenames:
|
if inputdevice.is_remote_client and '__account__' in profilenames:
|
||||||
return profilenames.index('__account__')
|
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
|
_g_player_rejoin_cooldown: float = 0.0
|
||||||
|
|
||||||
# overrides the session's decision of max_players
|
# 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:
|
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:
|
def set_max_players_override(max_players: int | None) -> None:
|
||||||
"""Set the override for how many players can join a session"""
|
"""Set the override for how many players can join a session"""
|
||||||
global _max_players_override # pylint: disable=global-statement
|
global _g_max_players_override # pylint: disable=global-statement
|
||||||
_max_players_override = max_players
|
_g_max_players_override = max_players
|
||||||
|
|
||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
|
|
@ -176,8 +176,8 @@ class Session:
|
||||||
self.min_players = min_players
|
self.min_players = min_players
|
||||||
self.max_players = (
|
self.max_players = (
|
||||||
max_players
|
max_players
|
||||||
if _max_players_override is None
|
if _g_max_players_override is None
|
||||||
else _max_players_override
|
else _g_max_players_override
|
||||||
)
|
)
|
||||||
self.submit_score = submit_score
|
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
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from babase import (
|
from babase import (
|
||||||
accountlog,
|
accountlog,
|
||||||
AccountV2Handle,
|
AccountV2Handle,
|
||||||
|
|
@ -319,7 +317,7 @@ __all__ = [
|
||||||
if __debug__:
|
if __debug__:
|
||||||
for _mdl in 'babase', '_babase':
|
for _mdl in 'babase', '_babase':
|
||||||
if not hasattr(__import__(_mdl), '_REACHED_END_OF_MODULE'):
|
if not hasattr(__import__(_mdl), '_REACHED_END_OF_MODULE'):
|
||||||
logging.warning(
|
balog.warning(
|
||||||
'%s was imported before %s finished importing;'
|
'%s was imported before %s finished importing;'
|
||||||
' should not happen.',
|
' should not happen.',
|
||||||
__name__,
|
__name__,
|
||||||
|
|
|
||||||
|
|
@ -45,14 +45,17 @@ class NetScanner:
|
||||||
bui.widget(edit=self._columnwidget, up_widget=tab_button)
|
bui.widget(edit=self._columnwidget, up_widget=tab_button)
|
||||||
self._width = width
|
self._width = width
|
||||||
self._last_selected_host: dict[str, Any] | None = None
|
self._last_selected_host: dict[str, Any] | None = None
|
||||||
|
self._last_scan: list[dict[str, str]] | None = None
|
||||||
|
|
||||||
self._update_timer = bui.AppTimer(
|
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
|
# Run two cycles pretty immediately - this should send out a
|
||||||
# have to wait a second.
|
# "who's there" and update the list with any immediate-ish
|
||||||
self.update()
|
# results so we may not have to wait a second to see things
|
||||||
bui.apptimer(0.25, bui.WeakCallStrict(self.update))
|
# appear.
|
||||||
|
self._update()
|
||||||
|
bui.apptimer(0.25, bui.WeakCallStrict(self._update))
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self) -> None:
|
||||||
bs.end_host_scanning()
|
bs.end_host_scanning()
|
||||||
|
|
@ -74,23 +77,33 @@ class NetScanner:
|
||||||
|
|
||||||
bs.connect_to_party(host['address'])
|
bs.connect_to_party(host['address'])
|
||||||
|
|
||||||
def update(self) -> None:
|
def _update(self) -> None:
|
||||||
"""(internal)"""
|
"""(internal)"""
|
||||||
|
|
||||||
# In case our UI was killed from under us.
|
# In case our UI was killed from under us.
|
||||||
if not self._columnwidget:
|
if not self._columnwidget:
|
||||||
print(
|
bui.uilog.error(
|
||||||
f'ERROR: NetScanner running without UI at time {bui.apptime()}.'
|
'nearbytab NetScanner running without UI at time %s.',
|
||||||
|
bui.apptime(),
|
||||||
)
|
)
|
||||||
return
|
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
|
t_scale = 1.6
|
||||||
for child in self._columnwidget.get_children():
|
for child in self._columnwidget.get_children():
|
||||||
child.delete()
|
child.delete()
|
||||||
|
|
||||||
# Grab this now this since adding widgets will change it.
|
# Grab this now this since adding widgets will change it.
|
||||||
last_selected_host = self._last_selected_host
|
last_selected_host = self._last_selected_host
|
||||||
hosts = bs.host_scan_cycle()
|
|
||||||
for i, host in enumerate(hosts):
|
for i, host in enumerate(hosts):
|
||||||
txt3 = bui.textwidget(
|
txt3 = bui.textwidget(
|
||||||
parent=self._columnwidget,
|
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')
|
_envval = os.environ.get('EFRO_TERMCOLORS')
|
||||||
color_enabled: bool = (
|
color_enabled: bool = ( # pylint: disable=invalid-name
|
||||||
True
|
True
|
||||||
if _envval == '1'
|
if _envval == '1'
|
||||||
else False if _envval == '0' else _default_color_enabled()
|
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)
|
cause = getattr(e, '__cause__', None)
|
||||||
if cause is not None:
|
if cause is not None:
|
||||||
stack.append(cause)
|
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