mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2026-08-15 13:04:30 +00:00
syncing 1.7.60 ballistica binary
This commit is contained in:
parent
2870c850c4
commit
e02f1dbe52
241 changed files with 10185 additions and 1827 deletions
63
dist/ba_data/python/babase/__init__.py
vendored
63
dist/ba_data/python/babase/__init__.py
vendored
|
|
@ -7,6 +7,7 @@ directly. Instead one should use purpose-built packages such as
|
||||||
:mod:`bascenev1` or :mod:`bauiv1` which themselves import various
|
:mod:`bascenev1` or :mod:`bauiv1` which themselves import various
|
||||||
functionality from here and reexpose it in a more focused way.
|
functionality from here and reexpose it in a more focused way.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# pylint: disable=redefined-builtin
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
# ba_meta require api 9
|
# ba_meta require api 9
|
||||||
|
|
@ -15,7 +16,8 @@ functionality from here and reexpose it in a more focused way.
|
||||||
# from other modules/packages. Code *within* this package should import
|
# from other modules/packages. Code *within* this package should import
|
||||||
# things from this package's submodules directly to reduce the chance of
|
# things from this package's submodules directly to reduce the chance of
|
||||||
# dependency loops. The exception is TYPE_CHECKING blocks and
|
# dependency loops. The exception is TYPE_CHECKING blocks and
|
||||||
# annotations since those aren't evaluated at runtime.
|
# annotations - since those aren't evaluated at runtime, it is cleaner
|
||||||
|
# looking to use top level names directly.
|
||||||
|
|
||||||
import _babase
|
import _babase
|
||||||
from _babase import (
|
from _babase import (
|
||||||
|
|
@ -125,6 +127,7 @@ from _babase import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from babase._accountv2 import AccountV2Handle, AccountV2Subsystem
|
from babase._accountv2 import AccountV2Handle, AccountV2Subsystem
|
||||||
|
from babase._analytics import AnalyticsSubsystem
|
||||||
from babase._app import App, AppState
|
from babase._app import App, AppState
|
||||||
from babase._appcomponent import AppComponentSubsystem
|
from babase._appcomponent import AppComponentSubsystem
|
||||||
from babase._appconfig import commit_app_config
|
from babase._appconfig import commit_app_config
|
||||||
|
|
@ -134,68 +137,71 @@ from babase._appsubsystem import AppSubsystem
|
||||||
from babase._appmodeselector import AppModeSelector
|
from babase._appmodeselector import AppModeSelector
|
||||||
from babase._appconfig import AppConfig
|
from babase._appconfig import AppConfig
|
||||||
from babase._apputils import (
|
from babase._apputils import (
|
||||||
|
AppHealthSubsystem,
|
||||||
|
get_remote_app_name,
|
||||||
handle_leftover_v1_cloud_log_file,
|
handle_leftover_v1_cloud_log_file,
|
||||||
is_browser_likely_available,
|
is_browser_likely_available,
|
||||||
get_remote_app_name,
|
|
||||||
AppHealthSubsystem,
|
|
||||||
utc_now_cloud,
|
utc_now_cloud,
|
||||||
)
|
)
|
||||||
from babase._cloud import CloudSubscription
|
from babase._cloud import CloudSubscription
|
||||||
from babase._devconsole import (
|
from babase._devconsole import (
|
||||||
DevConsoleButtonDef,
|
DevConsoleButtonDef,
|
||||||
|
DevConsoleSubsystem,
|
||||||
DevConsoleTab,
|
DevConsoleTab,
|
||||||
DevConsoleTabEntry,
|
DevConsoleTabEntry,
|
||||||
DevConsoleSubsystem,
|
|
||||||
)
|
)
|
||||||
from babase._discord import DiscordSubsystem
|
from babase._discord import DiscordSubsystem
|
||||||
from babase._emptyappmode import EmptyAppMode
|
from babase._emptyappmode import EmptyAppMode
|
||||||
from babase._error import (
|
from babase._error import (
|
||||||
|
ActivityNotFoundError,
|
||||||
|
ActorNotFoundError,
|
||||||
ContextError,
|
ContextError,
|
||||||
|
DelegateNotFoundError,
|
||||||
|
InputDeviceNotFoundError,
|
||||||
|
MapNotFoundError,
|
||||||
|
NodeNotFoundError,
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
PlayerNotFoundError,
|
PlayerNotFoundError,
|
||||||
SessionPlayerNotFoundError,
|
|
||||||
NodeNotFoundError,
|
|
||||||
ActorNotFoundError,
|
|
||||||
InputDeviceNotFoundError,
|
|
||||||
WidgetNotFoundError,
|
|
||||||
ActivityNotFoundError,
|
|
||||||
TeamNotFoundError,
|
|
||||||
MapNotFoundError,
|
|
||||||
SessionTeamNotFoundError,
|
|
||||||
SessionNotFoundError,
|
SessionNotFoundError,
|
||||||
DelegateNotFoundError,
|
SessionPlayerNotFoundError,
|
||||||
|
SessionTeamNotFoundError,
|
||||||
|
TeamNotFoundError,
|
||||||
|
WidgetNotFoundError,
|
||||||
)
|
)
|
||||||
from babase._gc import GarbageCollectionSubsystem
|
from babase._gc import GarbageCollectionSubsystem
|
||||||
from babase._general import (
|
from babase._general import (
|
||||||
DisplayTime,
|
|
||||||
AppTime,
|
AppTime,
|
||||||
WeakCall,
|
|
||||||
Call,
|
Call,
|
||||||
existing,
|
CallPartial,
|
||||||
|
CallStrict,
|
||||||
|
DisplayTime,
|
||||||
Existable,
|
Existable,
|
||||||
verify_object_death,
|
WeakCall,
|
||||||
storagename,
|
WeakCallPartial,
|
||||||
getclass,
|
WeakCallStrict,
|
||||||
|
existing,
|
||||||
get_type_name,
|
get_type_name,
|
||||||
|
getclass,
|
||||||
|
storagename,
|
||||||
|
verify_object_death,
|
||||||
)
|
)
|
||||||
from babase._language import Lstr, LanguageSubsystem
|
from babase._language import LanguageSubsystem, Lstr
|
||||||
from babase._locale import LocaleSubsystem
|
from babase._locale import LocaleSubsystem
|
||||||
from babase._logging import (
|
from babase._logging import (
|
||||||
balog,
|
|
||||||
accountlog,
|
accountlog,
|
||||||
applog,
|
applog,
|
||||||
|
balog,
|
||||||
lifecyclelog,
|
lifecyclelog,
|
||||||
netlog,
|
netlog,
|
||||||
uilog,
|
uilog,
|
||||||
)
|
)
|
||||||
from babase._login import LoginAdapter, LoginInfo
|
from babase._login import LoginAdapter, LoginInfo
|
||||||
|
|
||||||
from babase._mgen.enums import (
|
from babase._mgen.enums import (
|
||||||
Permission,
|
|
||||||
SpecialChar,
|
|
||||||
InputType,
|
InputType,
|
||||||
UIScale,
|
Permission,
|
||||||
QuitType,
|
QuitType,
|
||||||
|
SpecialChar,
|
||||||
|
UIScale,
|
||||||
)
|
)
|
||||||
from babase._math import normalized_color, is_point_in_box, vec3validate
|
from babase._math import normalized_color, is_point_in_box, vec3validate
|
||||||
from babase._meta import MetadataSubsystem
|
from babase._meta import MetadataSubsystem
|
||||||
|
|
@ -216,6 +222,7 @@ __all__ = [
|
||||||
'ActorNotFoundError',
|
'ActorNotFoundError',
|
||||||
'allows_ticket_sales',
|
'allows_ticket_sales',
|
||||||
'add_clean_frame_callback',
|
'add_clean_frame_callback',
|
||||||
|
'AnalyticsSubsystem',
|
||||||
'android_get_external_files_dir',
|
'android_get_external_files_dir',
|
||||||
'app',
|
'app',
|
||||||
'App',
|
'App',
|
||||||
|
|
@ -242,6 +249,8 @@ __all__ = [
|
||||||
'atexit',
|
'atexit',
|
||||||
'balog',
|
'balog',
|
||||||
'Call',
|
'Call',
|
||||||
|
'CallPartial',
|
||||||
|
'CallStrict',
|
||||||
'fullscreen_control_available',
|
'fullscreen_control_available',
|
||||||
'fullscreen_control_get',
|
'fullscreen_control_get',
|
||||||
'fullscreen_control_key_shortcut',
|
'fullscreen_control_key_shortcut',
|
||||||
|
|
@ -391,6 +400,8 @@ __all__ = [
|
||||||
'vec3validate',
|
'vec3validate',
|
||||||
'verify_object_death',
|
'verify_object_death',
|
||||||
'WeakCall',
|
'WeakCall',
|
||||||
|
'WeakCallPartial',
|
||||||
|
'WeakCallStrict',
|
||||||
'WidgetNotFoundError',
|
'WidgetNotFoundError',
|
||||||
'workspaces_in_use',
|
'workspaces_in_use',
|
||||||
'WorkspaceSubsystem',
|
'WorkspaceSubsystem',
|
||||||
|
|
|
||||||
73
dist/ba_data/python/babase/_analytics.py
vendored
Normal file
73
dist/ba_data/python/babase/_analytics.py
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Analytics functionality."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import bacommon.cloud
|
||||||
|
import _babase
|
||||||
|
|
||||||
|
from babase._logging import balog
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bacommon.analytics import AnalyticsEvent
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticsSubsystem:
|
||||||
|
"""Subsystem for wrangling analytics.
|
||||||
|
|
||||||
|
Access the single shared instance of this class via the
|
||||||
|
:attr:`~babase.App.analytics` attr on the :class:`~babase.App`
|
||||||
|
class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.enabled: bool = True
|
||||||
|
|
||||||
|
def submit_event(self, event: AnalyticsEvent) -> None:
|
||||||
|
"""Submit an event.
|
||||||
|
|
||||||
|
Should only be called from the logic thread.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not _babase.in_logic_thread():
|
||||||
|
balog.error(
|
||||||
|
'submit_event() called outside logic thread.', stack_info=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# No-op if analytics are disabled or we don't have plus.
|
||||||
|
if not self.enabled:
|
||||||
|
return
|
||||||
|
|
||||||
|
plus = _babase.app.plus
|
||||||
|
if plus is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Currently just no-op if it seems we're not connected. Perhaps
|
||||||
|
# in the future we'd want to save these and submit later when we
|
||||||
|
# are.
|
||||||
|
if not plus.cloud.is_connected():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Just kick off an immediate send in the bg with or without
|
||||||
|
# account info.
|
||||||
|
account = plus.accounts.primary
|
||||||
|
if account is None:
|
||||||
|
plus.cloud.send_message_cb(
|
||||||
|
bacommon.cloud.AnalyticsEventMessage(event),
|
||||||
|
on_response=self._on_analytics_message_response,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
with account:
|
||||||
|
plus.cloud.send_message_cb(
|
||||||
|
bacommon.cloud.AnalyticsEventMessage(event),
|
||||||
|
on_response=self._on_analytics_message_response,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_analytics_message_response(
|
||||||
|
self, response: Exception | None
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
21
dist/ba_data/python/babase/_app.py
vendored
21
dist/ba_data/python/babase/_app.py
vendored
|
|
@ -2,10 +2,12 @@
|
||||||
#
|
#
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
"""Functionality related to the high level state of the app."""
|
"""Functionality related to the high level state of the app."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
@ -28,12 +30,12 @@ from babase._appmodeselector import AppModeSelector
|
||||||
from babase._appintent import AppIntentDefault, AppIntentExec
|
from babase._appintent import AppIntentDefault, AppIntentExec
|
||||||
from babase._stringedit import StringEditSubsystem
|
from babase._stringedit import StringEditSubsystem
|
||||||
from babase._devconsole import DevConsoleSubsystem
|
from babase._devconsole import DevConsoleSubsystem
|
||||||
|
from babase._analytics import AnalyticsSubsystem
|
||||||
from babase._appconfig import AppConfig
|
from babase._appconfig import AppConfig
|
||||||
from babase._logging import lifecyclelog, applog
|
from babase._logging import lifecyclelog, applog
|
||||||
from babase._gc import GarbageCollectionSubsystem
|
from babase._gc import GarbageCollectionSubsystem
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import asyncio
|
|
||||||
from typing import Any, Callable, Coroutine, Generator, Awaitable
|
from typing import Any, Callable, Coroutine, Generator, Awaitable
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
|
|
||||||
|
|
@ -152,6 +154,9 @@ class App:
|
||||||
#: Subsystem for wrangling the dev-console UI.
|
#: Subsystem for wrangling the dev-console UI.
|
||||||
self.devconsole: DevConsoleSubsystem = DevConsoleSubsystem()
|
self.devconsole: DevConsoleSubsystem = DevConsoleSubsystem()
|
||||||
|
|
||||||
|
#: Subsystem for wrangling analytics.
|
||||||
|
self.analytics: AnalyticsSubsystem = AnalyticsSubsystem()
|
||||||
|
|
||||||
#: Incremented each time the app leaves the
|
#: Incremented each time the app leaves the
|
||||||
#: :attr:`~babase.AppState.SUSPENDED` state. This can be a simple
|
#: :attr:`~babase.AppState.SUSPENDED` state. This can be a simple
|
||||||
#: way to determine if network data should be refreshed/etc.
|
#: way to determine if network data should be refreshed/etc.
|
||||||
|
|
@ -281,8 +286,9 @@ class App:
|
||||||
def mode_selector(self, selector: babase.AppModeSelector) -> None:
|
def mode_selector(self, selector: babase.AppModeSelector) -> None:
|
||||||
self._mode_selector = selector
|
self._mode_selector = selector
|
||||||
|
|
||||||
def _on_task_done(self, task: asyncio.Task) -> None:
|
def _on_task_done(self, task: Any) -> None:
|
||||||
# Report any errors that occurred.
|
# Report any errors that occurred.
|
||||||
|
assert isinstance(task, asyncio.Task)
|
||||||
try:
|
try:
|
||||||
exc = task.exception()
|
exc = task.exception()
|
||||||
if exc is not None:
|
if exc is not None:
|
||||||
|
|
@ -1028,7 +1034,6 @@ class App:
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _shutdown(self) -> None:
|
async def _shutdown(self) -> None:
|
||||||
import asyncio
|
|
||||||
|
|
||||||
_babase.lock_all_input()
|
_babase.lock_all_input()
|
||||||
try:
|
try:
|
||||||
|
|
@ -1054,13 +1059,16 @@ class App:
|
||||||
self, coro: Coroutine[None, None, None]
|
self, coro: Coroutine[None, None, None]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run a shutdown task; report errors and abort if taking too long."""
|
"""Run a shutdown task; report errors and abort if taking too long."""
|
||||||
import asyncio
|
|
||||||
|
|
||||||
task = asyncio.create_task(coro)
|
task = asyncio.create_task(coro)
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(task, self.SHUTDOWN_TASK_TIMEOUT_SECONDS)
|
await asyncio.wait_for(task, self.SHUTDOWN_TASK_TIMEOUT_SECONDS)
|
||||||
|
except TimeoutError:
|
||||||
|
# Log simple error message if it times out.
|
||||||
|
logging.error('Timed out waiting for shutdown task %s.', coro)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception('Error in shutdown task (%s).', coro)
|
# Go with full ugly stack trace for anything unexpected.
|
||||||
|
logging.exception('Error in shutdown task %s.', coro)
|
||||||
|
|
||||||
def _on_suspend(self) -> None:
|
def _on_suspend(self) -> None:
|
||||||
"""Called when the app goes to a suspended state."""
|
"""Called when the app goes to a suspended state."""
|
||||||
|
|
@ -1136,7 +1144,6 @@ class App:
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _wait_for_shutdown_suppressions(self) -> None:
|
async def _wait_for_shutdown_suppressions(self) -> None:
|
||||||
import asyncio
|
|
||||||
|
|
||||||
# Spin and wait for anything blocking shutdown to complete.
|
# Spin and wait for anything blocking shutdown to complete.
|
||||||
starttime = _babase.apptime()
|
starttime = _babase.apptime()
|
||||||
|
|
@ -1153,7 +1160,6 @@ class App:
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _fade_and_shutdown_graphics(self) -> None:
|
async def _fade_and_shutdown_graphics(self) -> None:
|
||||||
import asyncio
|
|
||||||
|
|
||||||
# Kick off a short fade and give it time to complete.
|
# Kick off a short fade and give it time to complete.
|
||||||
lifecyclelog.info('fade-and-shutdown-graphics begin')
|
lifecyclelog.info('fade-and-shutdown-graphics begin')
|
||||||
|
|
@ -1189,7 +1195,6 @@ class App:
|
||||||
lifecyclelog.info('fade-and-shutdown-graphics end')
|
lifecyclelog.info('fade-and-shutdown-graphics end')
|
||||||
|
|
||||||
async def _fade_and_shutdown_audio(self) -> None:
|
async def _fade_and_shutdown_audio(self) -> None:
|
||||||
import asyncio
|
|
||||||
|
|
||||||
# Tell the audio system to go down and give it a bit of
|
# Tell the audio system to go down and give it a bit of
|
||||||
# time to do so gracefully.
|
# time to do so gracefully.
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_appcomponent.py
vendored
1
dist/ba_data/python/babase/_appcomponent.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides the AppComponent class."""
|
"""Provides the AppComponent class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_appconfig.py
vendored
1
dist/ba_data/python/babase/_appconfig.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides the AppConfig class."""
|
"""Provides the AppConfig class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_appintent.py
vendored
1
dist/ba_data/python/babase/_appintent.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides AppIntent functionality."""
|
"""Provides AppIntent functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_appmode.py
vendored
1
dist/ba_data/python/babase/_appmode.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides AppMode functionality."""
|
"""Provides AppMode functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Contains AppModeSelector base class."""
|
"""Contains AppModeSelector base class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
2
dist/ba_data/python/babase/_appsubsystem.py
vendored
2
dist/ba_data/python/babase/_appsubsystem.py
vendored
|
|
@ -1,11 +1,11 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides the AppSubsystem base class."""
|
"""Provides the AppSubsystem base class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from babase import UIScale
|
from babase import UIScale
|
||||||
|
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_apputils.py
vendored
1
dist/ba_data/python/babase/_apputils.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Utility functionality related to the overall operation of the app."""
|
"""Utility functionality related to the overall operation of the app."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_cloud.py
vendored
1
dist/ba_data/python/babase/_cloud.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Cloud related functionality."""
|
"""Cloud related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_devconsole.py
vendored
1
dist/ba_data/python/babase/_devconsole.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Dev-Console functionality."""
|
"""Dev-Console functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Predefined tabs for the dev console."""
|
"""Predefined tabs for the dev console."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_discord.py
vendored
1
dist/ba_data/python/babase/_discord.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
|
||||||
"""Functionality related to discord sdk integration"""
|
"""Functionality related to discord sdk integration"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
|
||||||
3
dist/ba_data/python/babase/_emptyappmode.py
vendored
3
dist/ba_data/python/babase/_emptyappmode.py
vendored
|
|
@ -1,12 +1,11 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides AppMode functionality."""
|
"""Provides AppMode functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
||||||
# from bacommon.app import AppExperience
|
|
||||||
|
|
||||||
import _babase
|
import _babase
|
||||||
from babase._appmode import AppMode
|
from babase._appmode import AppMode
|
||||||
from babase._appintent import AppIntentExec, AppIntentDefault
|
from babase._appintent import AppIntentExec, AppIntentDefault
|
||||||
|
|
|
||||||
25
dist/ba_data/python/babase/_env.py
vendored
25
dist/ba_data/python/babase/_env.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Environment related functionality."""
|
"""Environment related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
@ -158,26 +159,6 @@ def on_main_thread_start_app() -> None:
|
||||||
# situations.
|
# situations.
|
||||||
__main__.__builtins__.help = _CustomHelper()
|
__main__.__builtins__.help = _CustomHelper()
|
||||||
|
|
||||||
# UPDATE: As of May 2025 I'm no longer seeing the below issue, so
|
|
||||||
# disabling this workaround for now and will remove it soon if no
|
|
||||||
# issues arise.
|
|
||||||
|
|
||||||
# On Windows I'm seeing the following error creating asyncio loops
|
|
||||||
# in background threads with the default proactor setup:
|
|
||||||
|
|
||||||
# ValueError: set_wakeup_fd only works in main thread of the main
|
|
||||||
# interpreter.
|
|
||||||
|
|
||||||
# So let's explicitly request selector loops. Interestingly this
|
|
||||||
# error only started showing up once I moved Python init to the main
|
|
||||||
# thread; previously the various asyncio bg thread loops were
|
|
||||||
# working fine (maybe something caused them to default to selector
|
|
||||||
# in that case?..
|
|
||||||
if sys.platform == 'win32' and bool(False):
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
||||||
|
|
||||||
# Kick off networking bootstrapping. We do this here instead of in
|
# Kick off networking bootstrapping. We do this here instead of in
|
||||||
# our app net-subsystem so that it can proceed in parallel with the
|
# our app net-subsystem so that it can proceed in parallel with the
|
||||||
# rest of our bootstrapping (as networking stuff is often an overall
|
# rest of our bootstrapping (as networking stuff is often an overall
|
||||||
|
|
@ -647,5 +628,5 @@ class _CustomHelper:
|
||||||
'Interactive help is not available in this environment.\n'
|
'Interactive help is not available in this environment.\n'
|
||||||
'Type help(object) for help about object.'
|
'Type help(object) for help about object.'
|
||||||
)
|
)
|
||||||
return None
|
return
|
||||||
return pydoc.help(*args, **kwds)
|
pydoc.help(*args, **kwds)
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_gc.py
vendored
1
dist/ba_data/python/babase/_gc.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Utility functionality related to the overall operation of the app."""
|
"""Utility functionality related to the overall operation of the app."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import gc
|
import gc
|
||||||
|
|
|
||||||
457
dist/ba_data/python/babase/_general.py
vendored
457
dist/ba_data/python/babase/_general.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Utility snippets applying to generic Python code."""
|
"""Utility snippets applying to generic Python code."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -9,6 +10,7 @@ import weakref
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
import inspect
|
import inspect
|
||||||
|
import warnings
|
||||||
from typing import TYPE_CHECKING, TypeVar, Protocol, NewType, override
|
from typing import TYPE_CHECKING, TypeVar, Protocol, NewType, override
|
||||||
|
|
||||||
from efro.terminal import Clr
|
from efro.terminal import Clr
|
||||||
|
|
@ -17,7 +19,7 @@ import _babase
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import functools
|
import functools
|
||||||
from typing import Any
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
|
||||||
# Declare distinct types for different time measurements we use so the
|
# Declare distinct types for different time measurements we use so the
|
||||||
|
|
@ -93,160 +95,351 @@ def get_type_name(cls: type) -> str:
|
||||||
return f'{cls.__module__}.{cls.__qualname__}'
|
return f'{cls.__module__}.{cls.__qualname__}'
|
||||||
|
|
||||||
|
|
||||||
class _WeakCall:
|
# Note: Something here is wonky with pylint, possibly related to our
|
||||||
"""Wrap a callable and arguments into a single callable object.
|
# custom pylint plugin. Disabling all checks seems to fix it.
|
||||||
|
# pylint: disable=all
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
# For type-checking, we point WeakCall and Call at
|
||||||
|
# functools.partial. This gives decent type-checking considering the
|
||||||
|
# open-ended nature of these calls (args being supplied at create
|
||||||
|
# time and/or at call time). Just remember that we're slightly lying
|
||||||
|
# to the type-checker here.
|
||||||
|
WeakCallPartial = functools.partial
|
||||||
|
CallPartial = functools.partial
|
||||||
|
WeakCall = functools.partial
|
||||||
|
Call = functools.partial
|
||||||
|
else:
|
||||||
|
|
||||||
When passed a bound method as the callable, the instance portion of
|
class WeakCallPartial:
|
||||||
it is weak-referenced, meaning the underlying instance is free to
|
"""Wrap a callable and arguments into a single callable object.
|
||||||
die if all other references to it go away. Should this occur,
|
|
||||||
calling the weak-call is simply a no-op.
|
|
||||||
|
|
||||||
Think of this as a handy way to tell an object to do something at
|
When passed a bound method as the callable, the instance portion of
|
||||||
some point in the future if it happens to still exist.
|
it is weak-referenced, meaning the underlying instance is free to
|
||||||
|
die if all other references to it go away. Should this occur,
|
||||||
|
calling the weak-call is simply a no-op.
|
||||||
|
|
||||||
**EXAMPLE A:** This code will create a ``FooClass`` instance and
|
Think of this as a handy way to tell an object to do something at
|
||||||
call its ``bar()`` method 5 seconds later; it will be kept alive
|
some point in the future if it happens to still exist.
|
||||||
even though we overwrite its variable with None because the bound
|
|
||||||
method we pass as a timer callback (``foo.bar``) strong-references
|
|
||||||
it::
|
|
||||||
|
|
||||||
foo = FooClass()
|
**EXAMPLE A:** This code will create a ``FooClass`` instance and
|
||||||
babase.apptimer(5.0, foo.bar)
|
call its ``bar()`` method 5 seconds later; it will be kept alive
|
||||||
foo = None
|
even though we overwrite its variable with None because the bound
|
||||||
|
method we pass as a timer callback (``foo.bar``) strong-references
|
||||||
|
it::
|
||||||
|
|
||||||
**EXAMPLE B:** This code will *not* keep our object alive; it will
|
foo = FooClass()
|
||||||
die when we overwrite it with ``None`` and the timer will be a no-op
|
babase.apptimer(5.0, foo.bar)
|
||||||
when it fires::
|
foo = None
|
||||||
|
|
||||||
foo = FooClass()
|
**EXAMPLE B:** This code will *not* keep our object alive; it will
|
||||||
babase.apptimer(5.0, ba.WeakCall(foo.bar))
|
die when we overwrite it with ``None`` and the timer will be a no-op
|
||||||
foo = None
|
when it fires::
|
||||||
|
|
||||||
**EXAMPLE C:** Wrap a method call with some positional and keyword
|
foo = FooClass()
|
||||||
args::
|
babase.apptimer(5.0, ba.WeakCall(foo.bar))
|
||||||
|
foo = None
|
||||||
|
|
||||||
myweakcall = babase.WeakCall(self.dostuff, argval1,
|
**EXAMPLE C:** Wrap a method call with some positional and keyword
|
||||||
namedarg=argval2)
|
args::
|
||||||
|
|
||||||
# Now we have a single callable to run that whole mess.
|
myweakcall = babase.WeakCall(self.dostuff, argval1,
|
||||||
# The same as calling myobj.dostuff(argval1, namedarg=argval2)
|
namedarg=argval2)
|
||||||
# (provided my_obj still exists; this will do nothing otherwise).
|
|
||||||
myweakcall()
|
|
||||||
|
|
||||||
Note: additional args and keywords you provide to the weak-call
|
# Now we have a single callable to run that whole mess.
|
||||||
constructor are stored as regular strong-references; you'll need to
|
# The same as calling myobj.dostuff(argval1, namedarg=argval2)
|
||||||
wrap them in weakrefs manually if desired.
|
# (provided my_obj still exists; this will do nothing otherwise).
|
||||||
|
myweakcall()
|
||||||
|
|
||||||
|
Note: additional args and keywords you provide to the weak-call
|
||||||
|
constructor are stored as regular strong-references; you'll need to
|
||||||
|
wrap them in weakrefs manually if desired.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
||||||
|
__slots__ = ['_call', '_args', '_keywds']
|
||||||
|
|
||||||
|
_did_invalid_call_warning = False
|
||||||
|
|
||||||
|
def __init__(self, call: Any, /, *args: Any, **keywds: Any) -> None:
|
||||||
|
# Note: keeping _call, _args, _keywds private in this case
|
||||||
|
# since we sub functools.partial for ourself in
|
||||||
|
# type-checking so they will be unrecognized anyway. Use
|
||||||
|
# non-partial versions if you want to access those.
|
||||||
|
if hasattr(call, '__func__'):
|
||||||
|
self._call = WeakMethod(call)
|
||||||
|
else:
|
||||||
|
app = _babase.app
|
||||||
|
if not self._did_invalid_call_warning:
|
||||||
|
logging.warning(
|
||||||
|
'Warning: callable passed to WeakCall() is not'
|
||||||
|
' weak-referencable (%s); use regular Call() instead'
|
||||||
|
' to avoid this warning.',
|
||||||
|
args[0],
|
||||||
|
stack_info=True,
|
||||||
|
)
|
||||||
|
type(self)._did_invalid_call_warning = True
|
||||||
|
self._call = call
|
||||||
|
self._args = args
|
||||||
|
self._keywds = keywds
|
||||||
|
|
||||||
|
def __call__(self, *args_extra: Any, **keywds_extra: Any) -> Any:
|
||||||
|
# Fast path: no extra args or kwargs.
|
||||||
|
if not args_extra and not keywds_extra:
|
||||||
|
return self._call(*self._args, **self._keywds)
|
||||||
|
|
||||||
|
# Slightly slower path: handle extra args.
|
||||||
|
if not keywds_extra:
|
||||||
|
# Only extra positional args; skip dict merge.
|
||||||
|
return self._call(*(self._args + args_extra), **self._keywds)
|
||||||
|
|
||||||
|
# Handle kw overrides (call-time kwargs overriding stored).
|
||||||
|
merged = {**self._keywds, **keywds_extra}
|
||||||
|
return self._call(*(self._args + args_extra), **merged)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f'<babase.WeakCall object; _call={self._call!r}'
|
||||||
|
f' _args={self._args!r} _keywds={self._keywds!r}>'
|
||||||
|
)
|
||||||
|
|
||||||
|
class CallPartial:
|
||||||
|
"""Wraps a callable and args into a single callable object.
|
||||||
|
|
||||||
|
The callable is strong-referenced so it won't die until this
|
||||||
|
object does.
|
||||||
|
|
||||||
|
Note that a bound method (ex: ``myobj.dosomething``) contains a
|
||||||
|
reference to ``self`` (``myobj`` in that case), so you will be
|
||||||
|
keeping that object alive too. Use babase.WeakCall if you want
|
||||||
|
to pass a method to a callback without keeping its object alive.
|
||||||
|
|
||||||
|
Example: Wrap a method call with 1 positional and 1 keyword arg::
|
||||||
|
|
||||||
|
mycall = babase.Call(myobj.dostuff, argval, namedarg=argval2)
|
||||||
|
|
||||||
|
# Now we have a single callable to run that whole mess.
|
||||||
|
# ..the same as calling myobj.dostuff(argval, namedarg=argval2)
|
||||||
|
mycall()
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
||||||
|
__slots__ = ['_call', '_args', '_keywds']
|
||||||
|
|
||||||
|
def __init__(self, call: Any, /, *args: Any, **keywds: Any):
|
||||||
|
# Note: keeping _call, _args, _keywds private in this case
|
||||||
|
# since we sub functools.partial for ourself in
|
||||||
|
# type-checking so they will be unrecognized anyway. Use
|
||||||
|
# non-partial versions if you want to access those.
|
||||||
|
self._call = call
|
||||||
|
self._args = args
|
||||||
|
self._keywds = keywds
|
||||||
|
|
||||||
|
def __call__(self, *args_extra: Any, **keywds_extra: Any) -> Any:
|
||||||
|
# Fast path: no extra args or kwargs.
|
||||||
|
if not args_extra and not keywds_extra:
|
||||||
|
return self._call(*self._args, **self._keywds)
|
||||||
|
|
||||||
|
# Slightly slower path: handle extra args.
|
||||||
|
if not keywds_extra:
|
||||||
|
# Only extra positional args; skip dict merge.
|
||||||
|
return self._call(*(self._args + args_extra), **self._keywds)
|
||||||
|
|
||||||
|
# Handle kw overrides (call-time kwargs overriding stored).
|
||||||
|
merged = {**self._keywds, **keywds_extra}
|
||||||
|
return self._call(*(self._args + args_extra), **merged)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f'<babase.Call object; _call={self.call!r}'
|
||||||
|
f' _args={self.args!r} _keywds={self.keywds!r}>'
|
||||||
|
)
|
||||||
|
|
||||||
|
class WeakCall:
|
||||||
|
"""Currently alias of :meth:`WeakCallPartial`."""
|
||||||
|
|
||||||
|
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
||||||
|
__slots__ = ['_call', '_args', '_keywds']
|
||||||
|
|
||||||
|
_did_invalid_call_warning = False
|
||||||
|
|
||||||
|
def __init__(self, call: Any, /, *args: Any, **keywds: Any) -> None:
|
||||||
|
warnings.warn(
|
||||||
|
'WeakCall should be replaced with either WeakCallPartial'
|
||||||
|
' (if passing extra args at call time) or WeakCallStrict'
|
||||||
|
' (it not). Once API 9 support ends, WeakCall can again be'
|
||||||
|
' used, but it will behave like WeakCallStrict instead of'
|
||||||
|
' WeakCallPartial.',
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
# Note: keeping _call, _args, _keywds private in this case
|
||||||
|
# since we sub functools.partial for ourself in
|
||||||
|
# type-checking so they will be unrecognized anyway. Use
|
||||||
|
# non-partial versions if you want to access those.
|
||||||
|
if hasattr(call, '__func__'):
|
||||||
|
self._call = WeakMethod(call)
|
||||||
|
else:
|
||||||
|
app = _babase.app
|
||||||
|
if not self._did_invalid_call_warning:
|
||||||
|
logging.warning(
|
||||||
|
'Warning: callable passed to WeakCall() is not'
|
||||||
|
' weak-referencable (%r); use regular Call() instead'
|
||||||
|
' to avoid this warning.',
|
||||||
|
args[0],
|
||||||
|
stack_info=True,
|
||||||
|
)
|
||||||
|
type(self)._did_invalid_call_warning = True
|
||||||
|
self._call = call
|
||||||
|
self._args = args
|
||||||
|
self._keywds = keywds
|
||||||
|
|
||||||
|
def __call__(self, *args_extra: Any, **keywds_extra: Any) -> Any:
|
||||||
|
# Fast path: no extra args or kwargs.
|
||||||
|
if not args_extra and not keywds_extra:
|
||||||
|
return self._call(*self._args, **self._keywds)
|
||||||
|
|
||||||
|
# Slightly slower path: handle extra args.
|
||||||
|
if not keywds_extra:
|
||||||
|
# Only extra positional args; skip dict merge.
|
||||||
|
return self._call(*(self._args + args_extra), **self._keywds)
|
||||||
|
|
||||||
|
# Handle kw overrides (call-time kwargs overriding stored).
|
||||||
|
merged = {**self._keywds, **keywds_extra}
|
||||||
|
return self._call(*(self._args + args_extra), **merged)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f'<babase.WeakCall object; _call={self._call!r}'
|
||||||
|
f' _args={self._args!r} _keywds={self._keywds!r}>'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Call:
|
||||||
|
"""Currently alias of :meth:`CallPartial`."""
|
||||||
|
|
||||||
|
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
||||||
|
__slots__ = ['_call', '_args', '_keywds']
|
||||||
|
|
||||||
|
def __init__(self, call: Any, /, *args: Any, **keywds: Any):
|
||||||
|
warnings.warn(
|
||||||
|
'Call should be replaced with either CallPartial'
|
||||||
|
' (if passing extra args at call time) or CallStrict'
|
||||||
|
' (it not). Once API 9 support ends, Call can again be'
|
||||||
|
' used, but it will behave like CallStrict instead'
|
||||||
|
' of CallPartial.',
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
# Note: keeping _call, _args, _keywds private in this case
|
||||||
|
# since we sub functools.partial for ourself in
|
||||||
|
# type-checking so they will be unrecognized anyway. Use
|
||||||
|
# non-partial versions if you want to access those.
|
||||||
|
self._call = call
|
||||||
|
self._args = args
|
||||||
|
self._keywds = keywds
|
||||||
|
|
||||||
|
def __call__(self, *args_extra: Any, **keywds_extra: Any) -> Any:
|
||||||
|
# Fast path: no extra args or kwargs.
|
||||||
|
if not args_extra and not keywds_extra:
|
||||||
|
return self._call(*self._args, **self._keywds)
|
||||||
|
|
||||||
|
# Slightly slower path: handle extra args.
|
||||||
|
if not keywds_extra:
|
||||||
|
# Only extra positional args; skip dict merge.
|
||||||
|
return self._call(*(self._args + args_extra), **self._keywds)
|
||||||
|
|
||||||
|
# Handle kw overrides (call-time kwargs overriding stored).
|
||||||
|
merged = {**self._keywds, **keywds_extra}
|
||||||
|
return self._call(*(self._args + args_extra), **merged)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f'<babase.Call object; _call={self.call!r}'
|
||||||
|
f' _args={self.args!r} _keywds={self.keywds!r}>'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: enable=all
|
||||||
|
|
||||||
|
|
||||||
|
class CallStrict[**P, T]:
|
||||||
|
"""Like :meth:`CallPartial()` but disallows extra args at call time.
|
||||||
|
|
||||||
|
This allows more complete type checking to occur, so this is
|
||||||
|
recommended if you do not need extra args at call time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
__slots__ = ('call', 'args', 'kwargs')
|
||||||
__slots__ = ['_call', '_args', '_keywds']
|
|
||||||
|
def __init__(
|
||||||
|
self, call: Callable[P, T], /, *args: P.args, **kwargs: P.kwargs
|
||||||
|
) -> None:
|
||||||
|
# Note: we allow access to these here since we don't use any
|
||||||
|
# tricks like pointing at functools.partial for type checking or
|
||||||
|
# whatnot that would break this.
|
||||||
|
self.call = call
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
def __call__(self) -> T:
|
||||||
|
return self.call(*self.args, **self.kwargs)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f'<babase.Call object; call={self.call!r},'
|
||||||
|
f' args={self.args!r}, kwargs={self.kwargs!r}>'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WeakCallStrict[**P, T]:
|
||||||
|
"""Like :meth:`WeakCallPartial()` but disallows extra args at call time.
|
||||||
|
|
||||||
|
This allows more complete type checking to occur, so this is
|
||||||
|
recommended if you do not need extra args at call time.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('call', 'args', 'kwargs')
|
||||||
|
|
||||||
_did_invalid_call_warning = False
|
_did_invalid_call_warning = False
|
||||||
|
|
||||||
def __init__(self, *args: Any, **keywds: Any) -> None:
|
def __init__(
|
||||||
if hasattr(args[0], '__func__'):
|
self, call: Callable[P, T], /, *args: P.args, **kwargs: P.kwargs
|
||||||
self._call = WeakMethod(args[0])
|
) -> None:
|
||||||
|
# Note: we allow access to these here since we don't use any
|
||||||
|
# tricks like pointing at functools.partial for type checking or
|
||||||
|
# whatnot that would break this.
|
||||||
|
if hasattr(call, '__func__'):
|
||||||
|
self.call: Any = WeakMethod(call) # type: ignore
|
||||||
else:
|
else:
|
||||||
app = _babase.app
|
app = _babase.app
|
||||||
if not self._did_invalid_call_warning:
|
if not self._did_invalid_call_warning:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
'Warning: callable passed to babase.WeakCall() is not'
|
'Warning: callable passed to WeakCallStrict() is not'
|
||||||
' weak-referencable (%s); use functools.partial instead'
|
' weak-referencable (%r); use regular CallStrict() instead'
|
||||||
' to avoid this warning.',
|
' to avoid this warning.',
|
||||||
args[0],
|
args[0],
|
||||||
stack_info=True,
|
stack_info=True,
|
||||||
)
|
)
|
||||||
type(self)._did_invalid_call_warning = True
|
type(self)._did_invalid_call_warning = True
|
||||||
self._call = args[0]
|
self.call = call
|
||||||
self._args = args[1:]
|
self.args = args
|
||||||
self._keywds = keywds
|
self.kwargs = kwargs
|
||||||
|
|
||||||
def __call__(self, *args_extra: Any) -> Any:
|
def __call__(self) -> T:
|
||||||
return self._call(*self._args + args_extra, **self._keywds)
|
return self.call(*self.args, **self.kwargs) # type: ignore
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def __str__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
'<ba.WeakCall object; _call='
|
f'<babase.WeakCall object; call={self.call!r},'
|
||||||
+ str(self._call)
|
f' args={self.args!r}, kwargs={self.kwargs!r}>'
|
||||||
+ ' _args='
|
|
||||||
+ str(self._args)
|
|
||||||
+ ' _keywds='
|
|
||||||
+ str(self._keywds)
|
|
||||||
+ '>'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class _Call:
|
|
||||||
"""Wraps a callable and arguments into a single callable object.
|
|
||||||
|
|
||||||
The callable is strong-referenced so it won't die until this
|
|
||||||
object does.
|
|
||||||
|
|
||||||
Note that a bound method (ex: ``myobj.dosomething``) contains a reference
|
|
||||||
to ``self`` (``myobj`` in that case), so you will be keeping that object
|
|
||||||
alive too. Use babase.WeakCall if you want to pass a method to a callback
|
|
||||||
without keeping its object alive.
|
|
||||||
|
|
||||||
Example: Wrap a method call with 1 positional and 1 keyword arg::
|
|
||||||
|
|
||||||
mycall = babase.Call(myobj.dostuff, argval, namedarg=argval2)
|
|
||||||
|
|
||||||
# Now we have a single callable to run that whole mess.
|
|
||||||
# ..the same as calling myobj.dostuff(argval, namedarg=argval2)
|
|
||||||
mycall()
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
|
||||||
__slots__ = ['_call', '_args', '_keywds']
|
|
||||||
|
|
||||||
def __init__(self, *args: Any, **keywds: Any):
|
|
||||||
self._call = args[0]
|
|
||||||
self._args = args[1:]
|
|
||||||
self._keywds = keywds
|
|
||||||
|
|
||||||
def __call__(self, *args_extra: Any) -> Any:
|
|
||||||
return self._call(*self._args + args_extra, **self._keywds)
|
|
||||||
|
|
||||||
@override
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return (
|
|
||||||
'<ba.Call object; _call='
|
|
||||||
+ str(self._call)
|
|
||||||
+ ' _args='
|
|
||||||
+ str(self._args)
|
|
||||||
+ ' _keywds='
|
|
||||||
+ str(self._keywds)
|
|
||||||
+ '>'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
# For type-checking, point at functools.partial which gives us full
|
|
||||||
# type checking on both positional and keyword arguments (as of mypy
|
|
||||||
# 1.11).
|
|
||||||
|
|
||||||
# FIXME: Actually, currently (as of Dec 2024) mypy doesn't fully
|
|
||||||
# type check partial. The partial() call itself is checked, but the
|
|
||||||
# resulting callable seems to be essentially untyped. We should
|
|
||||||
# probably revise this stuff so that Call and WeakCall are for 100%
|
|
||||||
# complete calls so we can fully type check them using ParamSpecs or
|
|
||||||
# whatnot. We could then write a weak_partial() call if we actually
|
|
||||||
# need that particular combination of functionality.
|
|
||||||
|
|
||||||
# Note: Something here is wonky with pylint, possibly related to our
|
|
||||||
# custom pylint plugin. Disabling all checks seems to fix it.
|
|
||||||
# pylint: disable=all
|
|
||||||
|
|
||||||
WeakCall = functools.partial
|
|
||||||
Call = functools.partial
|
|
||||||
else:
|
|
||||||
WeakCall = _WeakCall
|
|
||||||
WeakCall.__name__ = 'WeakCall'
|
|
||||||
Call = _Call
|
|
||||||
Call.__name__ = 'Call'
|
|
||||||
|
|
||||||
|
|
||||||
class WeakMethod:
|
class WeakMethod:
|
||||||
"""A weak-referenced bound method.
|
"""A weak-referenced bound method.
|
||||||
|
|
||||||
|
|
@ -255,22 +448,22 @@ class WeakMethod:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
# Optimize performance a bit; we shouldn't need to be super dynamic.
|
||||||
__slots__ = ['_func', '_obj']
|
__slots__ = ['func', 'obj']
|
||||||
|
|
||||||
def __init__(self, call: types.MethodType):
|
def __init__(self, call: types.MethodType):
|
||||||
assert isinstance(call, types.MethodType)
|
assert isinstance(call, types.MethodType)
|
||||||
self._func = call.__func__
|
self.func = call.__func__
|
||||||
self._obj = weakref.ref(call.__self__)
|
self.obj = weakref.ref(call.__self__)
|
||||||
|
|
||||||
def __call__(self, *args: Any, **keywds: Any) -> Any:
|
def __call__(self, *args: Any, **keywds: Any) -> Any:
|
||||||
obj = self._obj()
|
obj: Any = self.obj()
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return self._func(*((obj,) + args), **keywds)
|
return self.func(*((obj,) + args), **keywds)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def __str__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return '<ba.WeakMethod object; call=' + str(self._func) + '>'
|
return f'<babase.WeakMethod object; func={self.func!r}>'
|
||||||
|
|
||||||
|
|
||||||
def verify_object_death(obj: object) -> None:
|
def verify_object_death(obj: object) -> None:
|
||||||
|
|
@ -292,7 +485,7 @@ def verify_object_death(obj: object) -> None:
|
||||||
# Make this timer in an empty context; don't want it dying with the
|
# Make this timer in an empty context; don't want it dying with the
|
||||||
# scene/etc.
|
# scene/etc.
|
||||||
with _babase.ContextRef.empty():
|
with _babase.ContextRef.empty():
|
||||||
_babase.apptimer(delay, Call(_verify_object_death, ref))
|
_babase.apptimer(delay, CallStrict(_verify_object_death, ref))
|
||||||
|
|
||||||
|
|
||||||
def _verify_object_death(wref: weakref.ref) -> None:
|
def _verify_object_death(wref: weakref.ref) -> None:
|
||||||
|
|
|
||||||
3
dist/ba_data/python/babase/_hooks.py
vendored
3
dist/ba_data/python/babase/_hooks.py
vendored
|
|
@ -9,6 +9,7 @@ until it broke at runtime. By instead defining such snippets here and then
|
||||||
capturing references to them all at launch it is possible to allow linting
|
capturing references to them all at launch it is possible to allow linting
|
||||||
and type-checking magic to happen and most issues will be caught immediately.
|
and type-checking magic to happen and most issues will be caught immediately.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# (most of these are self-explanatory)
|
# (most of these are self-explanatory)
|
||||||
# pylint: disable=missing-function-docstring
|
# pylint: disable=missing-function-docstring
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -42,7 +43,7 @@ def get_v2_account_id() -> str | None:
|
||||||
if account is not None:
|
if account is not None:
|
||||||
accountid = account.accountid
|
accountid = account.accountid
|
||||||
# (Avoids mypy complaints when plus is not present)
|
# (Avoids mypy complaints when plus is not present)
|
||||||
assert isinstance(accountid, (str, type(None)))
|
assert isinstance(accountid, str | None)
|
||||||
return accountid
|
return accountid
|
||||||
return None
|
return None
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
||||||
22
dist/ba_data/python/babase/_language.py
vendored
22
dist/ba_data/python/babase/_language.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Language related functionality."""
|
"""Language related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
@ -561,7 +562,7 @@ class Lstr:
|
||||||
You should avoid doing this as much as possible and instead pass
|
You should avoid doing this as much as possible and instead pass
|
||||||
and store ``Lstr`` values.
|
and store ``Lstr`` values.
|
||||||
"""
|
"""
|
||||||
return _babase.evaluate_lstr(self._get_json())
|
return _babase.evaluate_lstr(self.as_json())
|
||||||
|
|
||||||
def is_flat_value(self) -> bool:
|
def is_flat_value(self) -> bool:
|
||||||
"""Return whether this instance represents a 'flat' value.
|
"""Return whether this instance represents a 'flat' value.
|
||||||
|
|
@ -573,22 +574,13 @@ class Lstr:
|
||||||
"""
|
"""
|
||||||
return bool('v' in self.args and not self.args.get('s', []))
|
return bool('v' in self.args and not self.args.get('s', []))
|
||||||
|
|
||||||
def _get_json(self) -> str:
|
def as_json(self) -> str:
|
||||||
try:
|
"""Return the json dict representation of the Lstr."""
|
||||||
return json.dumps(self.args, separators=(',', ':'))
|
return json.dumps(self.args, separators=(',', ':'))
|
||||||
except Exception:
|
|
||||||
from babase import _error
|
|
||||||
|
|
||||||
applog.exception('_get_json failed for %s.', self.args)
|
|
||||||
return 'JSON_ERR'
|
|
||||||
|
|
||||||
@override
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f'<ba.Lstr: {self._get_json()}>'
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'<ba.Lstr: {self._get_json()}>'
|
return f'<babase.Lstr: {self.as_json()}>'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(json_string: str) -> babase.Lstr:
|
def from_json(json_string: str) -> babase.Lstr:
|
||||||
|
|
@ -616,7 +608,7 @@ def _add_to_attr_dict(dst: AttrDict, src: dict) -> None:
|
||||||
)
|
)
|
||||||
_add_to_attr_dict(dst_dict, value)
|
_add_to_attr_dict(dst_dict, value)
|
||||||
else:
|
else:
|
||||||
if not isinstance(value, (float, int, bool, str, str, type(None))):
|
if not isinstance(value, float | int | bool | str | None):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"invalid value type for res '"
|
"invalid value type for res '"
|
||||||
+ key
|
+ key
|
||||||
|
|
|
||||||
2
dist/ba_data/python/babase/_locale.py
vendored
2
dist/ba_data/python/babase/_locale.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Locale related functionality."""
|
"""Locale related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override, assert_never
|
from typing import TYPE_CHECKING, override, assert_never
|
||||||
|
|
@ -144,6 +145,7 @@ class LocaleSubsystem(AppSubsystem):
|
||||||
or rlocale is cls.TAMIL
|
or rlocale is cls.TAMIL
|
||||||
or rlocale is cls.THAI
|
or rlocale is cls.THAI
|
||||||
or rlocale is cls.VIETNAMESE
|
or rlocale is cls.VIETNAMESE
|
||||||
|
or rlocale is cls.JAPANESE
|
||||||
):
|
):
|
||||||
# Return True only if we can display full unicode.
|
# Return True only if we can display full unicode.
|
||||||
return _babase.supports_unicode_display()
|
return _babase.supports_unicode_display()
|
||||||
|
|
|
||||||
8
dist/ba_data/python/babase/_mgen/enums.py
vendored
8
dist/ba_data/python/babase/_mgen/enums.py
vendored
|
|
@ -85,7 +85,9 @@ class Permission(Enum):
|
||||||
|
|
||||||
|
|
||||||
class SpecialChar(Enum):
|
class SpecialChar(Enum):
|
||||||
"""Special characters the game can print."""
|
"""Special characters the engine can diplay. Note that this currently
|
||||||
|
needs to be manually kept in sync with bacommon.text.SpecialChar.
|
||||||
|
"""
|
||||||
|
|
||||||
DOWN_ARROW = 0
|
DOWN_ARROW = 0
|
||||||
UP_ARROW = 1
|
UP_ARROW = 1
|
||||||
|
|
@ -185,3 +187,7 @@ class SpecialChar(Enum):
|
||||||
MIKIROG = 95
|
MIKIROG = 95
|
||||||
V2_LOGO = 96
|
V2_LOGO = 96
|
||||||
CLOSE = 97
|
CLOSE = 97
|
||||||
|
SANTA_HAT = 98
|
||||||
|
POTATO = 99
|
||||||
|
PALM_TREE = 100
|
||||||
|
BOXING_GLOVE = 101
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_net.py
vendored
1
dist/ba_data/python/babase/_net.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Networking related functionality."""
|
"""Networking related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/_ui.py
vendored
1
dist/ba_data/python/babase/_ui.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""UI related bits of babase."""
|
"""UI related bits of babase."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
|
||||||
1
dist/ba_data/python/babase/modutils.py
vendored
1
dist/ba_data/python/babase/modutils.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to modding."""
|
"""Functionality related to modding."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
12
dist/ba_data/python/baclassic/_achievement.py
vendored
12
dist/ba_data/python/baclassic/_achievement.py
vendored
|
|
@ -1,12 +1,13 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Various functionality related to achievements."""
|
"""Various functionality related to achievements."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from bacommon.bs import ClassicChestAppearance
|
from bacommon.classic import ClassicChestAppearance
|
||||||
from baclassic._chest import (
|
from baclassic._chest import (
|
||||||
CHEST_APPEARANCE_DISPLAY_INFOS,
|
CHEST_APPEARANCE_DISPLAY_INFOS,
|
||||||
CHEST_APPEARANCE_DISPLAY_INFO_DEFAULT,
|
CHEST_APPEARANCE_DISPLAY_INFO_DEFAULT,
|
||||||
|
|
@ -727,7 +728,10 @@ class Achievement:
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_award_chest_type(self) -> ClassicChestAppearance:
|
def get_award_chest_type(self) -> ClassicChestAppearance:
|
||||||
"""Return the type of chest given for this achievement."""
|
"""Return the type of chest given for this achievement.
|
||||||
|
|
||||||
|
:meta private:
|
||||||
|
"""
|
||||||
|
|
||||||
# For now just map our old ticket values to chest types.
|
# For now just map our old ticket values to chest types.
|
||||||
# Can add distinct values if need be later.
|
# Can add distinct values if need be later.
|
||||||
|
|
@ -1520,5 +1524,7 @@ class Achievement:
|
||||||
for actor in objs:
|
for actor in objs:
|
||||||
bascenev1.timer(
|
bascenev1.timer(
|
||||||
out_time + 1.000,
|
out_time + 1.000,
|
||||||
babase.WeakCall(actor.handlemessage, bascenev1.DieMessage()),
|
babase.WeakCallStrict(
|
||||||
|
actor.handlemessage, bascenev1.DieMessage()
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
2
dist/ba_data/python/baclassic/_analytics.py
vendored
2
dist/ba_data/python/baclassic/_analytics.py
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to analytics."""
|
"""Functionality related to classic analytics."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
196
dist/ba_data/python/baclassic/_appmode.py
vendored
196
dist/ba_data/python/baclassic/_appmode.py
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
|
# pylint: disable=too-many-lines
|
||||||
"""Contains ClassicAppMode."""
|
"""Contains ClassicAppMode."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -11,11 +12,11 @@ from functools import partial
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
||||||
from efro.error import CommunicationError
|
from efro.error import CommunicationError
|
||||||
import bacommon.bs
|
import bacommon.clienteffect as clfx
|
||||||
|
import bacommon.classic
|
||||||
from babase import AppMode
|
from babase import AppMode
|
||||||
import bauiv1 as bui
|
import bauiv1 as bui
|
||||||
from bauiv1lib.connectivity import wait_for_connectivity
|
from bauiv1lib.connectivity import wait_for_connectivity
|
||||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
|
||||||
|
|
||||||
import _baclassic
|
import _baclassic
|
||||||
|
|
||||||
|
|
@ -233,19 +234,19 @@ class ClassicAppMode(AppMode):
|
||||||
|
|
||||||
if item_id.startswith('tokens'):
|
if item_id.startswith('tokens'):
|
||||||
if item_id == 'tokens1':
|
if item_id == 'tokens1':
|
||||||
tokens = bacommon.bs.TOKENS1_COUNT
|
tokens = bacommon.classic.TOKENS1_COUNT
|
||||||
tokens_str = str(tokens)
|
tokens_str = str(tokens)
|
||||||
anim_time = 2.0
|
anim_time = 2.0
|
||||||
elif item_id == 'tokens2':
|
elif item_id == 'tokens2':
|
||||||
tokens = bacommon.bs.TOKENS2_COUNT
|
tokens = bacommon.classic.TOKENS2_COUNT
|
||||||
tokens_str = str(tokens)
|
tokens_str = str(tokens)
|
||||||
anim_time = 2.5
|
anim_time = 2.5
|
||||||
elif item_id == 'tokens3':
|
elif item_id == 'tokens3':
|
||||||
tokens = bacommon.bs.TOKENS3_COUNT
|
tokens = bacommon.classic.TOKENS3_COUNT
|
||||||
tokens_str = str(tokens)
|
tokens_str = str(tokens)
|
||||||
anim_time = 3.0
|
anim_time = 3.0
|
||||||
elif item_id == 'tokens4':
|
elif item_id == 'tokens4':
|
||||||
tokens = bacommon.bs.TOKENS4_COUNT
|
tokens = bacommon.classic.TOKENS4_COUNT
|
||||||
tokens_str = str(tokens)
|
tokens_str = str(tokens)
|
||||||
anim_time = 3.5
|
anim_time = 3.5
|
||||||
else:
|
else:
|
||||||
|
|
@ -257,21 +258,19 @@ class ClassicAppMode(AppMode):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert bui.app.classic is not None
|
assert bui.app.classic is not None
|
||||||
effects: list[bacommon.bs.ClientEffect] = [
|
effects: list[clfx.Effect] = [
|
||||||
bacommon.bs.ClientEffectTokensAnimation(
|
clfx.TokensAnimation(
|
||||||
duration=anim_time,
|
duration=anim_time,
|
||||||
startvalue=self._last_tokens_value,
|
startvalue=self._last_tokens_value,
|
||||||
endvalue=self._last_tokens_value + tokens,
|
endvalue=self._last_tokens_value + tokens,
|
||||||
),
|
),
|
||||||
bacommon.bs.ClientEffectDelay(anim_time),
|
clfx.Delay(anim_time),
|
||||||
bacommon.bs.ClientEffectScreenMessage(
|
clfx.LegacyScreenMessage(
|
||||||
message='You got ${COUNT} tokens!',
|
message='You got ${COUNT} tokens!',
|
||||||
subs=['${COUNT}', tokens_str],
|
subs=['${COUNT}', tokens_str],
|
||||||
color=(0, 1, 0),
|
color=(0, 1, 0),
|
||||||
),
|
),
|
||||||
bacommon.bs.ClientEffectSound(
|
clfx.PlaySound(clfx.Sound.CASH_REGISTER),
|
||||||
sound=bacommon.bs.ClientEffectSound.Sound.CASH_REGISTER
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
bui.app.classic.run_bs_client_effects(effects)
|
bui.app.classic.run_bs_client_effects(effects)
|
||||||
|
|
||||||
|
|
@ -345,14 +344,14 @@ class ClassicAppMode(AppMode):
|
||||||
|
|
||||||
with plus.accounts.primary:
|
with plus.accounts.primary:
|
||||||
plus.cloud.send_message_cb(
|
plus.cloud.send_message_cb(
|
||||||
bacommon.bs.GetClassicPurchasesMessage(),
|
bacommon.classic.GetClassicPurchasesMessage(),
|
||||||
on_response=bui.WeakCall(
|
on_response=bui.WeakCallPartial(
|
||||||
self._on_get_classic_purchases_response
|
self._on_get_classic_purchases_response
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _on_get_classic_purchases_response(
|
def _on_get_classic_purchases_response(
|
||||||
self, response: bacommon.bs.GetClassicPurchasesResponse | Exception
|
self, response: bacommon.classic.GetClassicPurchasesResponse | Exception
|
||||||
) -> None:
|
) -> None:
|
||||||
assert self._purchase_request_in_flight
|
assert self._purchase_request_in_flight
|
||||||
self._purchase_request_in_flight = False
|
self._purchase_request_in_flight = False
|
||||||
|
|
@ -471,6 +470,7 @@ class ClassicAppMode(AppMode):
|
||||||
chest_1_ad_allow_time=-1.0,
|
chest_1_ad_allow_time=-1.0,
|
||||||
chest_2_ad_allow_time=-1.0,
|
chest_2_ad_allow_time=-1.0,
|
||||||
chest_3_ad_allow_time=-1.0,
|
chest_3_ad_allow_time=-1.0,
|
||||||
|
store_style='',
|
||||||
)
|
)
|
||||||
self._have_account_values = False
|
self._have_account_values = False
|
||||||
self._update_ui_live_state()
|
self._update_ui_live_state()
|
||||||
|
|
@ -505,7 +505,7 @@ class ClassicAppMode(AppMode):
|
||||||
print(f'GOT SUB TEST UPDATE: {val}')
|
print(f'GOT SUB TEST UPDATE: {val}')
|
||||||
|
|
||||||
def _on_classic_account_data_change(
|
def _on_classic_account_data_change(
|
||||||
self, val: bacommon.bs.ClassicAccountLiveData
|
self, val: bacommon.classic.ClassicLiveAccountClientData
|
||||||
) -> None:
|
) -> None:
|
||||||
achp = round(val.achievements / max(val.achievements_total, 1) * 100.0)
|
achp = round(val.achievements / max(val.achievements_total, 1) * 100.0)
|
||||||
|
|
||||||
|
|
@ -666,6 +666,7 @@ class ClassicAppMode(AppMode):
|
||||||
if chest3 is None or chest3.ad_allow_time is None
|
if chest3 is None or chest3.ad_allow_time is None
|
||||||
else chest3.ad_allow_time.timestamp()
|
else chest3.ad_allow_time.timestamp()
|
||||||
),
|
),
|
||||||
|
store_style=val.store_style.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Note that we have values and updated faded state accordingly.
|
# Note that we have values and updated faded state accordingly.
|
||||||
|
|
@ -723,44 +724,56 @@ class ClassicAppMode(AppMode):
|
||||||
def _root_ui_achievements_press(self) -> None:
|
def _root_ui_achievements_press(self) -> None:
|
||||||
from bauiv1lib.achievements import AchievementsWindow
|
from bauiv1lib.achievements import AchievementsWindow
|
||||||
|
|
||||||
if not self._ensure_signed_in_v1():
|
btn = bui.get_special_widget('achievements_button')
|
||||||
|
|
||||||
|
if not self._ensure_signed_in(origin_widget=btn):
|
||||||
return
|
return
|
||||||
|
|
||||||
wait_for_connectivity(
|
wait_for_connectivity(
|
||||||
on_connected=lambda: bui.app.ui_v1.auxiliary_window_activate(
|
on_connected=lambda: bui.app.ui_v1.auxiliary_window_activate(
|
||||||
win_type=AchievementsWindow,
|
win_type=AchievementsWindow,
|
||||||
win_create_call=lambda: AchievementsWindow(
|
win_create_call=lambda: AchievementsWindow(origin_widget=btn),
|
||||||
origin_widget=bui.get_special_widget('achievements_button')
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _root_ui_inbox_press(self) -> None:
|
def _root_ui_inbox_press(self) -> None:
|
||||||
from bauiv1lib.inbox import InboxWindow
|
from bauiv1lib.inbox import InboxWindow
|
||||||
|
|
||||||
if not self._ensure_signed_in():
|
btn = bui.get_special_widget('inbox_button')
|
||||||
|
|
||||||
|
if not self._ensure_signed_in(origin_widget=btn):
|
||||||
return
|
return
|
||||||
|
|
||||||
wait_for_connectivity(
|
wait_for_connectivity(
|
||||||
on_connected=lambda: bui.app.ui_v1.auxiliary_window_activate(
|
on_connected=lambda: bui.app.ui_v1.auxiliary_window_activate(
|
||||||
win_type=InboxWindow,
|
win_type=InboxWindow,
|
||||||
win_create_call=lambda: InboxWindow(
|
win_create_call=lambda: InboxWindow(origin_widget=btn),
|
||||||
origin_widget=bui.get_special_widget('inbox_button')
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _root_ui_store_press(self) -> None:
|
def _root_ui_store_press(self) -> None:
|
||||||
from bauiv1lib.store.browser import StoreBrowserWindow
|
import bacommon.docui.v1 as dui1
|
||||||
|
|
||||||
if not self._ensure_signed_in_v1():
|
from bauiv1lib.docui import DocUIWindow
|
||||||
|
from bauiv1lib.store import StoreUIController
|
||||||
|
|
||||||
|
btn = bui.get_special_widget('store_button')
|
||||||
|
|
||||||
|
if not self._ensure_signed_in(origin_widget=btn):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Pop up an auxiliary window wherever we are in the nav stack.
|
||||||
wait_for_connectivity(
|
wait_for_connectivity(
|
||||||
on_connected=lambda: bui.app.ui_v1.auxiliary_window_activate(
|
on_connected=lambda: bui.app.ui_v1.auxiliary_window_activate(
|
||||||
win_type=StoreBrowserWindow,
|
win_type=DocUIWindow,
|
||||||
win_create_call=lambda: StoreBrowserWindow(
|
win_create_call=bui.CallStrict(
|
||||||
origin_widget=bui.get_special_widget('store_button')
|
StoreUIController().create_window,
|
||||||
|
dui1.Request('/'),
|
||||||
|
origin_widget=btn,
|
||||||
|
uiopenstateid='classicstore',
|
||||||
|
),
|
||||||
|
win_extra_type_id=(
|
||||||
|
StoreUIController.get_window_extra_type_id()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -782,80 +795,76 @@ class ClassicAppMode(AppMode):
|
||||||
def _root_ui_trophy_meter_press(self) -> None:
|
def _root_ui_trophy_meter_press(self) -> None:
|
||||||
from bauiv1lib.league.rankwindow import LeagueRankWindow
|
from bauiv1lib.league.rankwindow import LeagueRankWindow
|
||||||
|
|
||||||
if not self._ensure_signed_in_v1():
|
btn = bui.get_special_widget('trophy_meter')
|
||||||
|
|
||||||
|
if not self._ensure_signed_in(origin_widget=btn):
|
||||||
return
|
return
|
||||||
|
|
||||||
bui.app.ui_v1.auxiliary_window_activate(
|
bui.app.ui_v1.auxiliary_window_activate(
|
||||||
win_type=LeagueRankWindow,
|
win_type=LeagueRankWindow,
|
||||||
win_create_call=lambda: LeagueRankWindow(
|
win_create_call=lambda: LeagueRankWindow(origin_widget=btn),
|
||||||
origin_widget=bui.get_special_widget('trophy_meter')
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _root_ui_level_meter_press(self) -> None:
|
def _root_ui_level_meter_press(self) -> None:
|
||||||
from bauiv1lib.resourcetypeinfo import ResourceTypeInfoWindow
|
from bauiv1lib.resourcetypeinfo import ResourceTypeInfoWindow
|
||||||
|
|
||||||
ResourceTypeInfoWindow(
|
btn = bui.get_special_widget('level_meter')
|
||||||
'xp', origin_widget=bui.get_special_widget('level_meter')
|
|
||||||
)
|
|
||||||
|
|
||||||
def _root_ui_inventory_press(self) -> None:
|
if not self._ensure_signed_in(origin_widget=btn):
|
||||||
from bauiv1lib.inventory import InventoryWindow
|
|
||||||
|
|
||||||
if not self._ensure_signed_in_v1():
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
ResourceTypeInfoWindow('xp', origin_widget=btn)
|
||||||
|
|
||||||
|
def _root_ui_inventory_press(self) -> None:
|
||||||
|
import bacommon.docui.v1 as dui1
|
||||||
|
|
||||||
|
from bauiv1lib.docui import DocUIWindow
|
||||||
|
from bauiv1lib.inventory import InventoryUIController
|
||||||
|
|
||||||
|
# Pop up an auxiliary window wherever we are in the nav stack.
|
||||||
bui.app.ui_v1.auxiliary_window_activate(
|
bui.app.ui_v1.auxiliary_window_activate(
|
||||||
win_type=InventoryWindow,
|
win_type=DocUIWindow,
|
||||||
win_create_call=lambda: InventoryWindow(
|
win_create_call=bui.CallStrict(
|
||||||
origin_widget=bui.get_special_widget('inventory_button')
|
InventoryUIController().create_window,
|
||||||
|
dui1.Request('/'),
|
||||||
|
origin_widget=bui.get_special_widget('inventory_button'),
|
||||||
|
uiopenstateid='classicinventory',
|
||||||
),
|
),
|
||||||
|
win_extra_type_id=InventoryUIController.get_window_extra_type_id(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _ensure_signed_in(self) -> bool:
|
def _ensure_signed_in(self, *, origin_widget: bui.Widget | None) -> bool:
|
||||||
"""Make sure we're signed in (requiring modern v2 accounts)."""
|
"""Make sure we're signed in (requiring modern v2 accounts)."""
|
||||||
|
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||||
|
|
||||||
plus = bui.app.plus
|
plus = bui.app.plus
|
||||||
if plus is None:
|
if plus is None:
|
||||||
bui.screenmessage('This requires plus.', color=(1, 0, 0))
|
bui.screenmessage('This requires plus.', color=(1, 0, 0))
|
||||||
bui.getsound('error').play()
|
bui.getsound('error').play()
|
||||||
return False
|
return False
|
||||||
if plus.accounts.primary is None:
|
if plus.accounts.primary is None:
|
||||||
show_sign_in_prompt()
|
show_sign_in_prompt(origin_widget=origin_widget)
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _ensure_signed_in_v1(self) -> bool:
|
|
||||||
"""Make sure we're signed in (allowing legacy v1-only accounts)."""
|
|
||||||
plus = bui.app.plus
|
|
||||||
if plus is None:
|
|
||||||
bui.screenmessage('This requires plus.', color=(1, 0, 0))
|
|
||||||
bui.getsound('error').play()
|
|
||||||
return False
|
|
||||||
if plus.get_v1_account_state() != 'signed_in':
|
|
||||||
show_sign_in_prompt()
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _root_ui_get_tokens_press(self) -> None:
|
def _root_ui_get_tokens_press(self) -> None:
|
||||||
from bauiv1lib.gettokens import GetTokensWindow
|
from bauiv1lib.gettokens import GetTokensWindow, show_get_tokens_window
|
||||||
|
|
||||||
if not self._ensure_signed_in():
|
btn = bui.get_special_widget('get_tokens_button')
|
||||||
|
|
||||||
|
if not self._ensure_signed_in(origin_widget=btn):
|
||||||
return
|
return
|
||||||
|
|
||||||
bui.app.ui_v1.auxiliary_window_activate(
|
if bool(True):
|
||||||
win_type=GetTokensWindow,
|
show_get_tokens_window(origin_widget=btn, toggle=True)
|
||||||
win_create_call=lambda: GetTokensWindow(
|
else:
|
||||||
origin_widget=bui.get_special_widget('get_tokens_button')
|
bui.app.ui_v1.auxiliary_window_activate(
|
||||||
),
|
win_type=GetTokensWindow,
|
||||||
)
|
win_create_call=lambda: GetTokensWindow(origin_widget=btn),
|
||||||
|
)
|
||||||
|
|
||||||
def _root_ui_chest_slot_pressed(self, index: int) -> None:
|
def _root_ui_chest_slot_pressed(self, index: int) -> None:
|
||||||
from bauiv1lib.chest import (
|
from bauiv1lib.chest import ChestWindow
|
||||||
ChestWindow0,
|
|
||||||
ChestWindow1,
|
|
||||||
ChestWindow2,
|
|
||||||
ChestWindow3,
|
|
||||||
)
|
|
||||||
|
|
||||||
widgetid: Literal[
|
widgetid: Literal[
|
||||||
'chest_0_button',
|
'chest_0_button',
|
||||||
|
|
@ -866,16 +875,20 @@ class ClassicAppMode(AppMode):
|
||||||
winclass: type[ChestWindow]
|
winclass: type[ChestWindow]
|
||||||
if index == 0:
|
if index == 0:
|
||||||
widgetid = 'chest_0_button'
|
widgetid = 'chest_0_button'
|
||||||
winclass = ChestWindow0
|
winclass = ChestWindow
|
||||||
|
extratypeid = '0'
|
||||||
elif index == 1:
|
elif index == 1:
|
||||||
widgetid = 'chest_1_button'
|
widgetid = 'chest_1_button'
|
||||||
winclass = ChestWindow1
|
winclass = ChestWindow
|
||||||
|
extratypeid = '1'
|
||||||
elif index == 2:
|
elif index == 2:
|
||||||
widgetid = 'chest_2_button'
|
widgetid = 'chest_2_button'
|
||||||
winclass = ChestWindow2
|
winclass = ChestWindow
|
||||||
|
extratypeid = '2'
|
||||||
elif index == 3:
|
elif index == 3:
|
||||||
widgetid = 'chest_3_button'
|
widgetid = 'chest_3_button'
|
||||||
winclass = ChestWindow3
|
winclass = ChestWindow
|
||||||
|
extratypeid = '3'
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(f'Invalid index {index}')
|
raise RuntimeError(f'Invalid index {index}')
|
||||||
|
|
||||||
|
|
@ -886,6 +899,7 @@ class ClassicAppMode(AppMode):
|
||||||
index=index,
|
index=index,
|
||||||
origin_widget=bui.get_special_widget(widgetid),
|
origin_widget=bui.get_special_widget(widgetid),
|
||||||
),
|
),
|
||||||
|
win_extra_type_id=extratypeid,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -953,16 +967,26 @@ class ClassicAppMode(AppMode):
|
||||||
return [
|
return [
|
||||||
bui.DevConsoleButtonDef(
|
bui.DevConsoleButtonDef(
|
||||||
'MainWindow Template',
|
'MainWindow Template',
|
||||||
bui.WeakCall(self._main_win_template_press),
|
bui.WeakCallStrict(self._main_win_template_press),
|
||||||
),
|
),
|
||||||
bui.DevConsoleButtonDef(
|
bui.DevConsoleButtonDef(
|
||||||
'CloudUI Test', bui.WeakCall(self._cloud_ui_test_press)
|
'DocUI Test', bui.WeakCallStrict(self._doc_ui_test_press)
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def _main_win_template_press(self) -> None:
|
def _main_win_template_press(self) -> None:
|
||||||
from bauiv1lib.template import show_template_main_window
|
from bauiv1lib.template import show_template_main_window
|
||||||
|
|
||||||
|
# This only works if a main ui is up.
|
||||||
|
if bui.app.ui_v1.get_main_window() is None:
|
||||||
|
bui.screenmessage(
|
||||||
|
'This requires a main-window to be present.'
|
||||||
|
' Open a menu or whatnot first.',
|
||||||
|
color=(1, 0, 0),
|
||||||
|
)
|
||||||
|
bui.getsound('error').play()
|
||||||
|
return
|
||||||
|
|
||||||
# Unintuitively, swish sounds come from buttons, not windows.
|
# Unintuitively, swish sounds come from buttons, not windows.
|
||||||
# And dev-console buttons don't make sounds. So we need to
|
# And dev-console buttons don't make sounds. So we need to
|
||||||
# explicitly do so here.
|
# explicitly do so here.
|
||||||
|
|
@ -970,12 +994,22 @@ class ClassicAppMode(AppMode):
|
||||||
|
|
||||||
show_template_main_window()
|
show_template_main_window()
|
||||||
|
|
||||||
def _cloud_ui_test_press(self) -> None:
|
def _doc_ui_test_press(self) -> None:
|
||||||
from bauiv1 import show_cloud_ui_window
|
from bauiv1lib.docuitest import show_test_doc_ui_window
|
||||||
|
|
||||||
|
# This only works if a main ui is up.
|
||||||
|
if bui.app.ui_v1.get_main_window() is None:
|
||||||
|
bui.screenmessage(
|
||||||
|
'This requires a main-window to be present.'
|
||||||
|
' Open a menu or whatnot first.',
|
||||||
|
color=(1, 0, 0),
|
||||||
|
)
|
||||||
|
bui.getsound('error').play()
|
||||||
|
return
|
||||||
|
|
||||||
# Unintuitively, swish sounds come from buttons, not windows.
|
# Unintuitively, swish sounds come from buttons, not windows.
|
||||||
# And dev-console buttons don't make sounds. So we need to
|
# And dev-console buttons don't make sounds. So we need to
|
||||||
# explicitly do so here.
|
# explicitly do so here.
|
||||||
bui.getsound('swish').play()
|
bui.getsound('swish').play()
|
||||||
|
|
||||||
show_cloud_ui_window()
|
show_test_doc_ui_window()
|
||||||
|
|
|
||||||
49
dist/ba_data/python/baclassic/_appsubsystem.py
vendored
49
dist/ba_data/python/baclassic/_appsubsystem.py
vendored
|
|
@ -3,6 +3,7 @@
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
|
|
||||||
"""Provides classic app subsystem."""
|
"""Provides classic app subsystem."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
@ -27,7 +28,9 @@ from baclassic import _input
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Callable, Any, Sequence
|
from typing import Callable, Any, Sequence
|
||||||
|
|
||||||
import bacommon.bs
|
import bacommon.classic
|
||||||
|
import bacommon.clienteffect as clfx
|
||||||
|
import bacommon.clouddialog.basic as bcdlg
|
||||||
from bascenev1lib.actor import spazappearance
|
from bascenev1lib.actor import spazappearance
|
||||||
from bauiv1lib.party import PartyWindow
|
from bauiv1lib.party import PartyWindow
|
||||||
|
|
||||||
|
|
@ -408,7 +411,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
# Otherwise just force the issue.
|
# Otherwise just force the issue.
|
||||||
else:
|
else:
|
||||||
babase.pushcall(
|
babase.pushcall(
|
||||||
babase.Call(bascenev1.new_host_session, MainMenuSession)
|
babase.CallStrict(bascenev1.new_host_session, MainMenuSession)
|
||||||
)
|
)
|
||||||
|
|
||||||
def getmaps(self, playtype: str) -> list[str]:
|
def getmaps(self, playtype: str) -> list[str]:
|
||||||
|
|
@ -702,7 +705,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
if sddata is not None:
|
if sddata is not None:
|
||||||
babase.apptimer(
|
babase.apptimer(
|
||||||
delay,
|
delay,
|
||||||
babase.Call(ServerDialogWindow, sddata),
|
babase.CallStrict(ServerDialogWindow, sddata),
|
||||||
)
|
)
|
||||||
|
|
||||||
def show_url_window(self, address: str) -> None:
|
def show_url_window(self, address: str) -> None:
|
||||||
|
|
@ -751,10 +754,13 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
self,
|
self,
|
||||||
transition: str = 'in_right',
|
transition: str = 'in_right',
|
||||||
origin_widget: bauiv1.Widget | None = None,
|
origin_widget: bauiv1.Widget | None = None,
|
||||||
selected_profile: str | None = None,
|
# selected_profile: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Pop up a browser window from within a game."""
|
"""Pop up a browser window from within a game."""
|
||||||
from bauiv1lib.profile.browser import ProfileBrowserWindow
|
import bacommon.docui.v1 as dui1
|
||||||
|
|
||||||
|
# from bauiv1lib.profile.browser import ProfileBrowserWindow
|
||||||
|
from bauiv1lib.inventory import InventoryUIController
|
||||||
|
|
||||||
main_window = babase.app.ui_v1.get_main_window()
|
main_window = babase.app.ui_v1.get_main_window()
|
||||||
if main_window is not None:
|
if main_window is not None:
|
||||||
|
|
@ -765,15 +771,16 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
return
|
return
|
||||||
|
|
||||||
babase.app.ui_v1.set_main_window(
|
babase.app.ui_v1.set_main_window(
|
||||||
ProfileBrowserWindow(
|
InventoryUIController(player_profiles_only=True).create_window(
|
||||||
|
dui1.Request('/'),
|
||||||
|
uiopenstateid='classicinventory',
|
||||||
transition=transition,
|
transition=transition,
|
||||||
selected_profile=selected_profile,
|
|
||||||
origin_widget=origin_widget,
|
origin_widget=origin_widget,
|
||||||
minimal_toolbar=True,
|
|
||||||
),
|
),
|
||||||
is_top_level=True,
|
is_top_level=True,
|
||||||
back_state=None,
|
back_state=None,
|
||||||
suppress_warning=True,
|
suppress_warning=True,
|
||||||
|
extra_type_id=InventoryUIController.get_window_extra_type_id(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def preload_map_preview_media(self) -> None:
|
def preload_map_preview_media(self) -> None:
|
||||||
|
|
@ -835,6 +842,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
suppress_warning=True,
|
suppress_warning=True,
|
||||||
# Reset selections to default for consistency.
|
# Reset selections to default for consistency.
|
||||||
restore_shared_state=False,
|
restore_shared_state=False,
|
||||||
|
extra_type_id='',
|
||||||
)
|
)
|
||||||
|
|
||||||
def save_ui_state(self) -> None:
|
def save_ui_state(self) -> None:
|
||||||
|
|
@ -876,11 +884,17 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
is_top_level=True,
|
is_top_level=True,
|
||||||
back_state=None,
|
back_state=None,
|
||||||
suppress_warning=True,
|
suppress_warning=True,
|
||||||
|
extra_type_id='',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# If there's a saved ui state, restore that.
|
# If there's a saved ui state, restore that.
|
||||||
if self.saved_ui_state is not None:
|
if self.saved_ui_state is not None:
|
||||||
app.ui_v1.restore_main_window_state(self.saved_ui_state)
|
app.ui_v1.restore_main_window_state(self.saved_ui_state)
|
||||||
|
# Kill the state now that we're back; we'll
|
||||||
|
# generate a new one when we leave. This keeps
|
||||||
|
# UIOpenStates stored in the state doing the
|
||||||
|
# right thing.
|
||||||
|
self.saved_ui_state = None
|
||||||
else:
|
else:
|
||||||
# Otherwise start fresh at the main menu.
|
# Otherwise start fresh at the main menu.
|
||||||
from bauiv1lib.mainmenu import MainMenuWindow
|
from bauiv1lib.mainmenu import MainMenuWindow
|
||||||
|
|
@ -890,25 +904,32 @@ class ClassicAppSubsystem(babase.AppSubsystem):
|
||||||
is_top_level=True,
|
is_top_level=True,
|
||||||
back_state=None,
|
back_state=None,
|
||||||
suppress_warning=True,
|
suppress_warning=True,
|
||||||
|
extra_type_id='',
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run_bs_client_effects(
|
def run_bs_client_effects(
|
||||||
effects: list[bacommon.bs.ClientEffect], delay: float = 0.0
|
effects: list[clfx.Effect], delay: float = 0.0
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run client effects sent from the master server."""
|
"""Run client effects sent from the master server.
|
||||||
|
|
||||||
|
:meta private:
|
||||||
|
"""
|
||||||
from baclassic._clienteffect import run_bs_client_effects
|
from baclassic._clienteffect import run_bs_client_effects
|
||||||
|
|
||||||
run_bs_client_effects(effects, delay=delay)
|
run_bs_client_effects(effects, delay=delay)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def basic_client_ui_button_label_str(
|
def basic_client_ui_button_label_str(
|
||||||
label: bacommon.bs.BasicCloudDialog.ButtonLabel,
|
label: bcdlg.ButtonLabel,
|
||||||
) -> babase.Lstr:
|
) -> babase.Lstr:
|
||||||
"""Given a client-ui label, return an Lstr."""
|
"""Given a client-ui label, return an Lstr.
|
||||||
import bacommon.bs
|
|
||||||
|
|
||||||
cls = bacommon.bs.BasicCloudDialog.ButtonLabel
|
:meta private:
|
||||||
|
"""
|
||||||
|
import bacommon.clouddialog.basic as bcdlg
|
||||||
|
|
||||||
|
cls = bcdlg.ButtonLabel
|
||||||
if label is cls.UNKNOWN:
|
if label is cls.UNKNOWN:
|
||||||
# Server should not be sending us unknown stuff; make noise
|
# Server should not be sending us unknown stuff; make noise
|
||||||
# if they do.
|
# if they do.
|
||||||
|
|
|
||||||
23
dist/ba_data/python/baclassic/_benchmark.py
vendored
23
dist/ba_data/python/baclassic/_benchmark.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Benchmark/Stress-Test related functionality."""
|
"""Benchmark/Stress-Test related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
@ -130,9 +131,9 @@ def _start_stress_test(args: _StressTestArgs) -> None:
|
||||||
appconfig['Team Tournament Playlist Randomize'] = 1
|
appconfig['Team Tournament Playlist Randomize'] = 1
|
||||||
babase.apptimer(
|
babase.apptimer(
|
||||||
1.0,
|
1.0,
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
babase.pushcall,
|
babase.pushcall,
|
||||||
babase.Call(bascenev1.new_host_session, DualTeamSession),
|
babase.CallStrict(bascenev1.new_host_session, DualTeamSession),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
@ -140,18 +141,22 @@ def _start_stress_test(args: _StressTestArgs) -> None:
|
||||||
appconfig['Free-for-All Playlist Randomize'] = 1
|
appconfig['Free-for-All Playlist Randomize'] = 1
|
||||||
babase.apptimer(
|
babase.apptimer(
|
||||||
1.0,
|
1.0,
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
babase.pushcall,
|
babase.pushcall,
|
||||||
babase.Call(bascenev1.new_host_session, FreeForAllSession),
|
babase.CallStrict(
|
||||||
|
bascenev1.new_host_session, FreeForAllSession
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
_baclassic.set_stress_testing(True, args.player_count, args.attract_mode)
|
_baclassic.set_stress_testing(True, args.player_count, args.attract_mode)
|
||||||
classic.stress_test_update_timer = babase.AppTimer(
|
classic.stress_test_update_timer = babase.AppTimer(
|
||||||
args.round_duration, babase.Call(_reset_stress_test, args)
|
args.round_duration, babase.CallStrict(_reset_stress_test, args)
|
||||||
)
|
)
|
||||||
if args.attract_mode:
|
if args.attract_mode:
|
||||||
classic.stress_test_update_timer_2 = babase.AppTimer(
|
classic.stress_test_update_timer_2 = babase.AppTimer(
|
||||||
0.48, babase.Call(_update_attract_mode_test, args), repeat=True
|
0.48,
|
||||||
|
babase.CallStrict(_update_attract_mode_test, args),
|
||||||
|
repeat=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -176,7 +181,7 @@ def _reset_stress_test(args: _StressTestArgs) -> None:
|
||||||
# we just end back at the main menu. If things are idle there then
|
# we just end back at the main menu. If things are idle there then
|
||||||
# we'll get sent back to a new stress test.
|
# we'll get sent back to a new stress test.
|
||||||
if not args.attract_mode:
|
if not args.attract_mode:
|
||||||
babase.apptimer(1.0, babase.Call(_start_stress_test, args))
|
babase.apptimer(1.0, babase.CallStrict(_start_stress_test, args))
|
||||||
|
|
||||||
|
|
||||||
def run_media_reload_benchmark() -> None:
|
def run_media_reload_benchmark() -> None:
|
||||||
|
|
@ -200,8 +205,8 @@ def run_media_reload_benchmark() -> None:
|
||||||
color=(1, 1, 0),
|
color=(1, 1, 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
babase.add_clean_frame_callback(babase.Call(doit, start_time))
|
babase.add_clean_frame_callback(babase.CallStrict(doit, start_time))
|
||||||
|
|
||||||
# The reload starts (should add a completion callback to the reload
|
# The reload starts (should add a completion callback to the reload
|
||||||
# func to fix this).
|
# func to fix this).
|
||||||
babase.apptimer(0.05, babase.Call(delay_add, babase.apptime()))
|
babase.apptimer(0.05, babase.CallStrict(delay_add, babase.apptime()))
|
||||||
|
|
|
||||||
3
dist/ba_data/python/baclassic/_chest.py
vendored
3
dist/ba_data/python/baclassic/_chest.py
vendored
|
|
@ -1,12 +1,13 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Chest related functionality."""
|
"""Chest related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from bacommon.bs import ClassicChestAppearance
|
from bacommon.classic import ClassicChestAppearance
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
63
dist/ba_data/python/baclassic/_clienteffect.py
vendored
63
dist/ba_data/python/baclassic/_clienteffect.py
vendored
|
|
@ -9,26 +9,25 @@ from typing import TYPE_CHECKING, assert_never
|
||||||
|
|
||||||
from efro.util import strict_partial
|
from efro.util import strict_partial
|
||||||
|
|
||||||
import bacommon.bs
|
|
||||||
import bauiv1
|
import bauiv1
|
||||||
|
|
||||||
import _baclassic
|
import _baclassic
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
pass
|
import bacommon.clienteffect as clfx
|
||||||
|
|
||||||
|
|
||||||
def run_bs_client_effects(
|
def run_bs_client_effects(
|
||||||
effects: list[bacommon.bs.ClientEffect], delay: float = 0.0
|
effects: list[clfx.Effect], delay: float = 0.0
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run effects."""
|
"""Run effects."""
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
from bacommon.bs import ClientEffectTypeID
|
import bacommon.clienteffect as clfx
|
||||||
|
|
||||||
for effect in effects:
|
for effect in effects:
|
||||||
effecttype = effect.get_type_id()
|
effecttype = effect.get_type_id()
|
||||||
if effecttype is ClientEffectTypeID.SCREEN_MESSAGE:
|
if effecttype is clfx.EffectTypeID.LEGACY_SCREEN_MESSAGE:
|
||||||
assert isinstance(effect, bacommon.bs.ClientEffectScreenMessage)
|
assert isinstance(effect, clfx.LegacyScreenMessage)
|
||||||
textfin = bauiv1.Lstr(
|
textfin = bauiv1.Lstr(
|
||||||
translate=('serverResponses', effect.message)
|
translate=('serverResponses', effect.message)
|
||||||
).evaluate()
|
).evaluate()
|
||||||
|
|
@ -46,22 +45,33 @@ def run_bs_client_effects(
|
||||||
bauiv1.screenmessage, textfin, color=effect.color
|
bauiv1.screenmessage, textfin, color=effect.color
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
elif effecttype is clfx.EffectTypeID.SCREEN_MESSAGE:
|
||||||
|
assert isinstance(effect, clfx.ScreenMessage)
|
||||||
|
bauiv1.apptimer(
|
||||||
|
delay,
|
||||||
|
strict_partial(
|
||||||
|
bauiv1.screenmessage,
|
||||||
|
effect.message,
|
||||||
|
color=effect.color,
|
||||||
|
literal=not effect.is_lstr,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
elif effecttype is ClientEffectTypeID.SOUND:
|
elif effecttype is clfx.EffectTypeID.SOUND:
|
||||||
assert isinstance(effect, bacommon.bs.ClientEffectSound)
|
assert isinstance(effect, clfx.PlaySound)
|
||||||
smcls = bacommon.bs.ClientEffectSound.Sound
|
scls = clfx.Sound
|
||||||
soundfile: str | None = None
|
soundfile: str | None = None
|
||||||
if effect.sound is smcls.UNKNOWN:
|
if effect.sound is scls.UNKNOWN:
|
||||||
# Server should avoid sending us sounds we don't
|
# Server should avoid sending us sounds we don't
|
||||||
# support. Make some noise if it happens.
|
# support. Make some noise if it happens.
|
||||||
logging.error('Got unrecognized bacommon.bs.ClientEffectSound.')
|
logging.error('Got unrecognized bacommon.classic.Sound.')
|
||||||
elif effect.sound is smcls.CASH_REGISTER:
|
elif effect.sound is scls.CASH_REGISTER:
|
||||||
soundfile = 'cashRegister'
|
soundfile = 'cashRegister'
|
||||||
elif effect.sound is smcls.ERROR:
|
elif effect.sound is scls.ERROR:
|
||||||
soundfile = 'error'
|
soundfile = 'error'
|
||||||
elif effect.sound is smcls.POWER_DOWN:
|
elif effect.sound is scls.POWER_DOWN:
|
||||||
soundfile = 'powerdown01'
|
soundfile = 'powerdown01'
|
||||||
elif effect.sound is smcls.GUN_COCKING:
|
elif effect.sound is scls.GUN_COCKING:
|
||||||
soundfile = 'gunCocking'
|
soundfile = 'gunCocking'
|
||||||
else:
|
else:
|
||||||
assert_never(effect.sound)
|
assert_never(effect.sound)
|
||||||
|
|
@ -73,14 +83,12 @@ def run_bs_client_effects(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif effecttype is ClientEffectTypeID.DELAY:
|
elif effecttype is clfx.EffectTypeID.DELAY:
|
||||||
assert isinstance(effect, bacommon.bs.ClientEffectDelay)
|
assert isinstance(effect, clfx.Delay)
|
||||||
delay += effect.seconds
|
delay += effect.seconds
|
||||||
|
|
||||||
elif effecttype is ClientEffectTypeID.CHEST_WAIT_TIME_ANIMATION:
|
elif effecttype is clfx.EffectTypeID.CHEST_WAIT_TIME_ANIMATION:
|
||||||
assert isinstance(
|
assert isinstance(effect, clfx.ChestWaitTimeAnimation)
|
||||||
effect, bacommon.bs.ClientEffectChestWaitTimeAnimation
|
|
||||||
)
|
|
||||||
bauiv1.apptimer(
|
bauiv1.apptimer(
|
||||||
delay,
|
delay,
|
||||||
strict_partial(
|
strict_partial(
|
||||||
|
|
@ -92,8 +100,8 @@ def run_bs_client_effects(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif effecttype is ClientEffectTypeID.TICKETS_ANIMATION:
|
elif effecttype is clfx.EffectTypeID.TICKETS_ANIMATION:
|
||||||
assert isinstance(effect, bacommon.bs.ClientEffectTicketsAnimation)
|
assert isinstance(effect, clfx.TicketsAnimation)
|
||||||
bauiv1.apptimer(
|
bauiv1.apptimer(
|
||||||
delay,
|
delay,
|
||||||
strict_partial(
|
strict_partial(
|
||||||
|
|
@ -104,8 +112,8 @@ def run_bs_client_effects(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif effecttype is ClientEffectTypeID.TOKENS_ANIMATION:
|
elif effecttype is clfx.EffectTypeID.TOKENS_ANIMATION:
|
||||||
assert isinstance(effect, bacommon.bs.ClientEffectTokensAnimation)
|
assert isinstance(effect, clfx.TokensAnimation)
|
||||||
bauiv1.apptimer(
|
bauiv1.apptimer(
|
||||||
delay,
|
delay,
|
||||||
strict_partial(
|
strict_partial(
|
||||||
|
|
@ -116,12 +124,11 @@ def run_bs_client_effects(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif effecttype is ClientEffectTypeID.UNKNOWN:
|
elif effecttype is clfx.EffectTypeID.UNKNOWN:
|
||||||
# Server should not send us stuff we can't digest. Make
|
# Server should not send us stuff we can't digest. Make
|
||||||
# some noise if it happens.
|
# some noise if it happens.
|
||||||
logging.error(
|
logging.error(
|
||||||
'Got unrecognized bacommon.bs.ClientEffect;'
|
'Got unrecognized bacommon.classic.Effect; should not happen.'
|
||||||
' should not happen.'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
39
dist/ba_data/python/baclassic/_displayitem.py
vendored
39
dist/ba_data/python/baclassic/_displayitem.py
vendored
|
|
@ -1,28 +1,33 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Display-item related functionality."""
|
"""Display-item related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, assert_never
|
||||||
|
|
||||||
from efro.util import pairs_from_flat
|
from efro.util import pairs_from_flat
|
||||||
import bacommon.bs
|
import bacommon.displayitem as ditm
|
||||||
|
import bacommon.classic
|
||||||
import bauiv1
|
import bauiv1
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME - migrate to use the doc-ui rendering for these instead.
|
||||||
def show_display_item(
|
def show_display_item(
|
||||||
itemwrapper: bacommon.bs.DisplayItemWrapper,
|
itemwrapper: ditm.Wrapper,
|
||||||
parent: bauiv1.Widget,
|
parent: bauiv1.Widget,
|
||||||
pos: tuple[float, float],
|
pos: tuple[float, float],
|
||||||
width: float,
|
width: float,
|
||||||
|
debug: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Create ui to depict a display-item."""
|
"""Create ui to depict a display-item."""
|
||||||
|
# pylint: disable=too-many-locals
|
||||||
|
|
||||||
height = width * 0.666
|
# Let's go with 4:3 aspect ratio.
|
||||||
|
height = width * 0.75
|
||||||
|
|
||||||
# Silent no-op if our parent ui is dead.
|
# Silent no-op if our parent ui is dead.
|
||||||
if not parent:
|
if not parent:
|
||||||
|
|
@ -33,15 +38,24 @@ def show_display_item(
|
||||||
text_y_offs = 0.0
|
text_y_offs = 0.0
|
||||||
show_text = True
|
show_text = True
|
||||||
|
|
||||||
if isinstance(itemwrapper.item, bacommon.bs.TicketsDisplayItem):
|
itemtype = itemwrapper.item.get_type_id()
|
||||||
|
|
||||||
|
if itemtype is ditm.ItemTypeID.TICKETS:
|
||||||
img = 'tickets'
|
img = 'tickets'
|
||||||
img_y_offs = width * 0.11
|
img_y_offs = width * 0.11
|
||||||
text_y_offs = width * -0.15
|
text_y_offs = width * -0.15
|
||||||
elif isinstance(itemwrapper.item, bacommon.bs.TokensDisplayItem):
|
elif itemtype is ditm.ItemTypeID.TICKETS_PURPLE:
|
||||||
|
img = 'ticketsPurple'
|
||||||
|
img_y_offs = width * 0.11
|
||||||
|
text_y_offs = width * -0.15
|
||||||
|
elif itemtype is ditm.ItemTypeID.TOKENS:
|
||||||
img = 'coin'
|
img = 'coin'
|
||||||
img_y_offs = width * 0.11
|
img_y_offs = width * 0.11
|
||||||
text_y_offs = width * -0.15
|
text_y_offs = width * -0.15
|
||||||
elif isinstance(itemwrapper.item, bacommon.bs.ChestDisplayItem):
|
elif itemtype is ditm.ItemTypeID.CHEST:
|
||||||
|
assert isinstance(
|
||||||
|
itemwrapper.item, bacommon.classic.ClassicChestDisplayItem
|
||||||
|
)
|
||||||
from baclassic._chest import (
|
from baclassic._chest import (
|
||||||
CHEST_APPEARANCE_DISPLAY_INFOS,
|
CHEST_APPEARANCE_DISPLAY_INFOS,
|
||||||
CHEST_APPEARANCE_DISPLAY_INFO_DEFAULT,
|
CHEST_APPEARANCE_DISPLAY_INFO_DEFAULT,
|
||||||
|
|
@ -63,9 +77,14 @@ def show_display_item(
|
||||||
tint_color=c_info.tint,
|
tint_color=c_info.tint,
|
||||||
tint2_color=c_info.tint2,
|
tint2_color=c_info.tint2,
|
||||||
)
|
)
|
||||||
|
elif (
|
||||||
|
itemtype is ditm.ItemTypeID.TEST or itemtype is ditm.ItemTypeID.UNKNOWN
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
assert_never(itemtype)
|
||||||
|
|
||||||
# Enable this for testing spacing.
|
if debug:
|
||||||
if bool(False):
|
|
||||||
bauiv1.imagewidget(
|
bauiv1.imagewidget(
|
||||||
parent=parent,
|
parent=parent,
|
||||||
position=(
|
position=(
|
||||||
|
|
|
||||||
1
dist/ba_data/python/baclassic/_hooks.py
vendored
1
dist/ba_data/python/baclassic/_hooks.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Hooks for C++ layer to use for ClassicAppMode."""
|
"""Hooks for C++ layer to use for ClassicAppMode."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
|
||||||
1
dist/ba_data/python/baclassic/_input.py
vendored
1
dist/ba_data/python/baclassic/_input.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Input related functionality"""
|
"""Input related functionality"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
1
dist/ba_data/python/baclassic/_music.py
vendored
1
dist/ba_data/python/baclassic/_music.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Music related functionality."""
|
"""Music related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
|
||||||
7
dist/ba_data/python/baclassic/_net.py
vendored
7
dist/ba_data/python/baclassic/_net.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Networking related functionality."""
|
"""Networking related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import zlib
|
import zlib
|
||||||
|
|
@ -14,7 +15,7 @@ from typing import TYPE_CHECKING, override
|
||||||
|
|
||||||
from efro.error import CommunicationError
|
from efro.error import CommunicationError
|
||||||
from efro.util import strip_exception_tracebacks
|
from efro.util import strip_exception_tracebacks
|
||||||
import bacommon.bs
|
import bacommon.classic
|
||||||
import babase
|
import babase
|
||||||
import bascenev1
|
import bascenev1
|
||||||
|
|
||||||
|
|
@ -113,7 +114,7 @@ class MasterServerV1CallThread(threading.Thread):
|
||||||
dataenc = urllib.parse.urlencode(self._data)
|
dataenc = urllib.parse.urlencode(self._data)
|
||||||
|
|
||||||
mresponse = plus.cloud.send_message(
|
mresponse = plus.cloud.send_message(
|
||||||
bacommon.bs.LegacyRequest(
|
bacommon.classic.LegacyRequest(
|
||||||
self._request,
|
self._request,
|
||||||
self._request_type,
|
self._request_type,
|
||||||
classic.legacy_user_agent_string,
|
classic.legacy_user_agent_string,
|
||||||
|
|
@ -168,7 +169,7 @@ class MasterServerV1CallThread(threading.Thread):
|
||||||
|
|
||||||
if self._callback is not None:
|
if self._callback is not None:
|
||||||
babase.pushcall(
|
babase.pushcall(
|
||||||
babase.Call(self._run_callback, response_data),
|
babase.CallStrict(self._run_callback, response_data),
|
||||||
from_other_thread=True,
|
from_other_thread=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
1
dist/ba_data/python/baclassic/_servermode.py
vendored
1
dist/ba_data/python/baclassic/_servermode.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to running the game in server-mode."""
|
"""Functionality related to running the game in server-mode."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
|
||||||
18
dist/ba_data/python/baclassic/_store.py
vendored
18
dist/ba_data/python/baclassic/_store.py
vendored
|
|
@ -292,6 +292,18 @@ class StoreSubsystem:
|
||||||
'icons.explodinary': {
|
'icons.explodinary': {
|
||||||
'icon': babase.charstr(babase.SpecialChar.EXPLODINARY_LOGO)
|
'icon': babase.charstr(babase.SpecialChar.EXPLODINARY_LOGO)
|
||||||
},
|
},
|
||||||
|
'icons.santa_hat': {
|
||||||
|
'icon': babase.charstr(babase.SpecialChar.SANTA_HAT)
|
||||||
|
},
|
||||||
|
'icons.potato': {
|
||||||
|
'icon': babase.charstr(babase.SpecialChar.POTATO)
|
||||||
|
},
|
||||||
|
'icons.palm_tree': {
|
||||||
|
'icon': babase.charstr(babase.SpecialChar.PALM_TREE)
|
||||||
|
},
|
||||||
|
'icons.boxing_glove': {
|
||||||
|
'icon': babase.charstr(babase.SpecialChar.BOXING_GLOVE)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return babase.app.classic.store_items
|
return babase.app.classic.store_items
|
||||||
|
|
||||||
|
|
@ -569,7 +581,7 @@ class StoreSubsystem:
|
||||||
def get_unowned_maps(self) -> list[str]:
|
def get_unowned_maps(self) -> list[str]:
|
||||||
"""Return the list of local maps not owned by the current account."""
|
"""Return the list of local maps not owned by the current account."""
|
||||||
classic = babase.app.classic
|
classic = babase.app.classic
|
||||||
purchases = classic.purchases if classic is not None else set()
|
purchases = classic.purchases if classic is not None else frozenset()
|
||||||
unowned_maps: set[str] = set()
|
unowned_maps: set[str] = set()
|
||||||
if babase.app.env.gui:
|
if babase.app.env.gui:
|
||||||
for map_section in self.get_store_layout()['maps']:
|
for map_section in self.get_store_layout()['maps']:
|
||||||
|
|
@ -583,7 +595,9 @@ class StoreSubsystem:
|
||||||
"""Return present game types not owned by the current account."""
|
"""Return present game types not owned by the current account."""
|
||||||
try:
|
try:
|
||||||
classic = babase.app.classic
|
classic = babase.app.classic
|
||||||
purchases = classic.purchases if classic is not None else set()
|
purchases = (
|
||||||
|
classic.purchases if classic is not None else frozenset()
|
||||||
|
)
|
||||||
unowned_games: set[type[bascenev1.GameActivity]] = set()
|
unowned_games: set[type[bascenev1.GameActivity]] = set()
|
||||||
if babase.app.env.gui:
|
if babase.app.env.gui:
|
||||||
for section in self.get_store_layout()['minigames']:
|
for section in self.get_store_layout()['minigames']:
|
||||||
|
|
|
||||||
1
dist/ba_data/python/baclassic/_tips.py
vendored
1
dist/ba_data/python/baclassic/_tips.py
vendored
|
|
@ -3,6 +3,7 @@
|
||||||
"""Functionality related to classic game tips.
|
"""Functionality related to classic game tips.
|
||||||
|
|
||||||
These can be shown at opportune times such as between rounds."""
|
These can be shown at opportune times such as between rounds."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
2
dist/ba_data/python/baclassic/_tournament.py
vendored
2
dist/ba_data/python/baclassic/_tournament.py
vendored
|
|
@ -6,7 +6,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from bacommon.bs import ClassicChestAppearance
|
from bacommon.classic import ClassicChestAppearance
|
||||||
import babase
|
import babase
|
||||||
import bauiv1
|
import bauiv1
|
||||||
import bascenev1
|
import bascenev1
|
||||||
|
|
|
||||||
9
dist/ba_data/python/baclassic/macmusicapp.py
vendored
9
dist/ba_data/python/baclassic/macmusicapp.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Music playback functionality using the Mac Music (formerly iTunes) app."""
|
"""Music playback functionality using the Mac Music (formerly iTunes) app."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -95,7 +96,7 @@ class _MacMusicAppThread(threading.Thread):
|
||||||
def do_print() -> None:
|
def do_print() -> None:
|
||||||
babase.apptimer(
|
babase.apptimer(
|
||||||
0.5,
|
0.5,
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
babase.screenmessage,
|
babase.screenmessage,
|
||||||
babase.Lstr(resource='usingItunesText'),
|
babase.Lstr(resource='usingItunesText'),
|
||||||
(0, 1, 0),
|
(0, 1, 0),
|
||||||
|
|
@ -198,7 +199,9 @@ class _MacMusicAppThread(threading.Thread):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print('Error getting iTunes playlists:', exc)
|
print('Error getting iTunes playlists:', exc)
|
||||||
playlists = []
|
playlists = []
|
||||||
babase.pushcall(babase.Call(target, playlists), from_other_thread=True)
|
babase.pushcall(
|
||||||
|
babase.CallStrict(target, playlists), from_other_thread=True
|
||||||
|
)
|
||||||
|
|
||||||
def _handle_play_command(self, target: str | None) -> None:
|
def _handle_play_command(self, target: str | None) -> None:
|
||||||
if target is None:
|
if target is None:
|
||||||
|
|
@ -246,7 +249,7 @@ class _MacMusicAppThread(threading.Thread):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
babase.pushcall(
|
babase.pushcall(
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
babase.screenmessage,
|
babase.screenmessage,
|
||||||
babase.app.lang.get_resource('playlistNotFoundText')
|
babase.app.lang.get_resource('playlistNotFoundText')
|
||||||
+ ': \''
|
+ ': \''
|
||||||
|
|
|
||||||
5
dist/ba_data/python/baclassic/osmusic.py
vendored
5
dist/ba_data/python/baclassic/osmusic.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Music playback using OS functionality exposed through the C++ layer."""
|
"""Music playback using OS functionality exposed through the C++ layer."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
@ -151,7 +152,7 @@ class _PickFolderSongThread(threading.Thread):
|
||||||
).evaluate()
|
).evaluate()
|
||||||
)
|
)
|
||||||
babase.pushcall(
|
babase.pushcall(
|
||||||
babase.Call(self._callback, all_files, None),
|
babase.CallStrict(self._callback, all_files, None),
|
||||||
from_other_thread=True,
|
from_other_thread=True,
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|
@ -162,6 +163,6 @@ class _PickFolderSongThread(threading.Thread):
|
||||||
except Exception:
|
except Exception:
|
||||||
err_str = '<ENCERR4523>'
|
err_str = '<ENCERR4523>'
|
||||||
babase.pushcall(
|
babase.pushcall(
|
||||||
babase.Call(self._callback, self._path, err_str),
|
babase.CallStrict(self._callback, self._path, err_str),
|
||||||
from_other_thread=True,
|
from_other_thread=True,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
75
dist/ba_data/python/bacommon/analytics.py
vendored
Normal file
75
dist/ba_data/python/bacommon/analytics.py
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Analytics support."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import assert_never, override, Annotated
|
||||||
|
|
||||||
|
from enum import Enum, unique
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOMultiType, IOAttrs
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticsEventTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
CLASSIC = 'c'
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticsEvent(IOMultiType[AnalyticsEventTypeID]):
|
||||||
|
"""Top level class for our multitype."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> AnalyticsEventTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: AnalyticsEventTypeID) -> type[AnalyticsEvent]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = AnalyticsEventTypeID
|
||||||
|
if type_id is t.CLASSIC:
|
||||||
|
return ClassicAnalyticsEvent
|
||||||
|
|
||||||
|
# Important to make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id_storage_name(cls) -> str:
|
||||||
|
return '_t'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ClassicAnalyticsEvent(AnalyticsEvent):
|
||||||
|
"""Analytics event related to classic."""
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class EventType(Enum):
|
||||||
|
"""Types of classic events."""
|
||||||
|
|
||||||
|
JOIN_PUBLIC_PARTY = 'jpb'
|
||||||
|
JOIN_PRIVATE_PARTY = 'jpr'
|
||||||
|
JOIN_PARTY_BY_ADDRESS = 'ja'
|
||||||
|
JOIN_NEARBY_PARTY = 'jn'
|
||||||
|
START_TEAMS_SESSION = 'st'
|
||||||
|
START_FFA_SESSION = 'sf'
|
||||||
|
START_COOP_SESSION = 'sc'
|
||||||
|
START_TOURNEY_COOP_SESSION = 'stc'
|
||||||
|
|
||||||
|
eventtype: Annotated[EventType, IOAttrs('t')]
|
||||||
|
extra: Annotated[str | None, IOAttrs('e', store_default=False)] = None
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> AnalyticsEventTypeID:
|
||||||
|
return AnalyticsEventTypeID.CLASSIC
|
||||||
8
dist/ba_data/python/bacommon/assets.py
vendored
8
dist/ba_data/python/bacommon/assets.py
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to cloud based assets."""
|
"""Functionality related to cloud based assets.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
43
dist/ba_data/python/bacommon/bacloud.py
vendored
43
dist/ba_data/python/bacommon/bacloud.py
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to the bacloud tool."""
|
"""Functionality related to the bacloud tool.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -14,7 +20,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
# Version is sent to the master-server with all commands. Can be incremented
|
# Version is sent to the master-server with all commands. Can be incremented
|
||||||
# if we need to change behavior server-side to go along with client changes.
|
# if we need to change behavior server-side to go along with client changes.
|
||||||
BACLOUD_VERSION = 13
|
BACLOUD_VERSION = 14
|
||||||
|
|
||||||
|
|
||||||
def asset_file_cache_path(filehash: str) -> str:
|
def asset_file_cache_path(filehash: str) -> str:
|
||||||
|
|
@ -91,12 +97,30 @@ class ResponseData:
|
||||||
#: response processing (including error handling) occurs.
|
#: response processing (including error handling) occurs.
|
||||||
message: Annotated[str | None, IOAttrs('m', store_default=False)] = None
|
message: Annotated[str | None, IOAttrs('m', store_default=False)] = None
|
||||||
|
|
||||||
#: End arg for message print() call.
|
#: Value for the 'end' arg of the message print() call.
|
||||||
message_end: Annotated[str, IOAttrs('m_end', store_default=False)] = '\n'
|
message_end: Annotated[str, IOAttrs('m_end', store_default=False)] = '\n'
|
||||||
|
|
||||||
#: If present, client should abort with this error message.
|
#: If present, client should print this message before any other
|
||||||
|
#: response processing (including error handling) occurs.
|
||||||
|
message_stderr: Annotated[
|
||||||
|
str | None, IOAttrs('m2', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
#: Value for the 'end' arg of the message print() call.
|
||||||
|
message_stderr_end: Annotated[
|
||||||
|
str, IOAttrs('m2_end', store_default=False)
|
||||||
|
] = '\n'
|
||||||
|
|
||||||
|
#: If present, client should abort with this error message and
|
||||||
|
#: return-code 2.
|
||||||
error: Annotated[str | None, IOAttrs('e', store_default=False)] = None
|
error: Annotated[str | None, IOAttrs('e', store_default=False)] = None
|
||||||
|
|
||||||
|
#: If present for an interactive command, specifies the return code
|
||||||
|
#: for the process. Note that this only applies if error is not set.
|
||||||
|
#: Standard return codes are 0 for success, 1 for a successful run
|
||||||
|
#: but negative result, and 2 for errors.
|
||||||
|
return_code: Annotated[int | None, IOAttrs('r', store_default=False)] = None
|
||||||
|
|
||||||
#: How long to wait before proceeding with remaining response (can
|
#: How long to wait before proceeding with remaining response (can
|
||||||
#: be useful when waiting for server progress in a loop).
|
#: be useful when waiting for server progress in a loop).
|
||||||
delay_seconds: Annotated[float, IOAttrs('d', store_default=False)] = 0.0
|
delay_seconds: Annotated[float, IOAttrs('d', store_default=False)] = 0.0
|
||||||
|
|
@ -170,6 +194,17 @@ class ResponseData:
|
||||||
#: End arg for end_message print() call.
|
#: End arg for end_message print() call.
|
||||||
end_message_end: Annotated[str, IOAttrs('eme', store_default=False)] = '\n'
|
end_message_end: Annotated[str, IOAttrs('eme', store_default=False)] = '\n'
|
||||||
|
|
||||||
|
#: If present, a message that should be printed after all other
|
||||||
|
#: response processing is done.
|
||||||
|
end_message_stderr: Annotated[
|
||||||
|
str | None, IOAttrs('em2', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
#: End arg for end_message print() call.
|
||||||
|
end_message_stderr_end: Annotated[
|
||||||
|
str, IOAttrs('em2e', store_default=False)
|
||||||
|
] = '\n'
|
||||||
|
|
||||||
#: If present, this command is run with these args at the end of
|
#: If present, this command is run with these args at the end of
|
||||||
#: response processing.
|
#: response processing.
|
||||||
end_command: Annotated[
|
end_command: Annotated[
|
||||||
|
|
|
||||||
8
dist/ba_data/python/bacommon/build.py
vendored
8
dist/ba_data/python/bacommon/build.py
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to game builds."""
|
"""Functionality related to game builds.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
71
dist/ba_data/python/bacommon/classic/__init__.py
vendored
Normal file
71
dist/ba_data/python/bacommon/classic/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Functionality related to bombsquad classic.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from bacommon.classic._account import (
|
||||||
|
ClassicLiveAccountClientData,
|
||||||
|
)
|
||||||
|
from bacommon.classic._classic import (
|
||||||
|
TOKENS1_COUNT,
|
||||||
|
TOKENS2_COUNT,
|
||||||
|
TOKENS3_COUNT,
|
||||||
|
TOKENS4_COUNT,
|
||||||
|
)
|
||||||
|
from bacommon.classic._chest import (
|
||||||
|
ClassicChestAppearance,
|
||||||
|
ClassicChestDisplayItem,
|
||||||
|
)
|
||||||
|
from bacommon.classic._msg import (
|
||||||
|
GetClassicLeaguePresidentButtonInfoMessage,
|
||||||
|
GetClassicLeaguePresidentButtonInfoResponse,
|
||||||
|
ChestInfoMessage,
|
||||||
|
ChestInfoResponse,
|
||||||
|
GetClassicPurchasesMessage,
|
||||||
|
GetClassicPurchasesResponse,
|
||||||
|
GlobalProfileCheckMessage,
|
||||||
|
GlobalProfileCheckResponse,
|
||||||
|
InboxRequestMessage,
|
||||||
|
InboxRequestResponse,
|
||||||
|
LegacyRequest,
|
||||||
|
LegacyResponse,
|
||||||
|
PrivatePartyMessage,
|
||||||
|
PrivatePartyResponse,
|
||||||
|
ScoreSubmitMessage,
|
||||||
|
ScoreSubmitResponse,
|
||||||
|
SendInfoMessage,
|
||||||
|
SendInfoResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'ChestInfoMessage',
|
||||||
|
'ChestInfoResponse',
|
||||||
|
'ClassicLiveAccountClientData',
|
||||||
|
'ClassicChestAppearance',
|
||||||
|
'ClassicChestDisplayItem',
|
||||||
|
'GetClassicLeaguePresidentButtonInfoMessage',
|
||||||
|
'GetClassicLeaguePresidentButtonInfoResponse',
|
||||||
|
'GetClassicPurchasesMessage',
|
||||||
|
'GetClassicPurchasesResponse',
|
||||||
|
'GlobalProfileCheckMessage',
|
||||||
|
'GlobalProfileCheckResponse',
|
||||||
|
'InboxRequestMessage',
|
||||||
|
'InboxRequestResponse',
|
||||||
|
'LegacyRequest',
|
||||||
|
'LegacyResponse',
|
||||||
|
'PrivatePartyMessage',
|
||||||
|
'PrivatePartyResponse',
|
||||||
|
'ScoreSubmitMessage',
|
||||||
|
'ScoreSubmitResponse',
|
||||||
|
'SendInfoMessage',
|
||||||
|
'SendInfoResponse',
|
||||||
|
'TOKENS1_COUNT',
|
||||||
|
'TOKENS2_COUNT',
|
||||||
|
'TOKENS3_COUNT',
|
||||||
|
'TOKENS4_COUNT',
|
||||||
|
]
|
||||||
83
dist/ba_data/python/bacommon/classic/_account.py
vendored
Normal file
83
dist/ba_data/python/bacommon/classic/_account.py
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""BombSquad specific bits."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs
|
||||||
|
from bacommon.classic._chest import ClassicChestAppearance
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ClassicLiveAccountClientData:
|
||||||
|
"""Live account data fed to the client in the bs classic app mode."""
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Chest:
|
||||||
|
"""A lovely chest."""
|
||||||
|
|
||||||
|
appearance: Annotated[
|
||||||
|
ClassicChestAppearance,
|
||||||
|
IOAttrs('a', enum_fallback=ClassicChestAppearance.UNKNOWN),
|
||||||
|
]
|
||||||
|
create_time: Annotated[datetime.datetime, IOAttrs('c')]
|
||||||
|
unlock_time: Annotated[datetime.datetime, IOAttrs('t')]
|
||||||
|
unlock_tokens: Annotated[int, IOAttrs('k')]
|
||||||
|
ad_allow_time: Annotated[datetime.datetime | None, IOAttrs('at')]
|
||||||
|
|
||||||
|
class LeagueType(Enum):
|
||||||
|
"""Type of league we are in."""
|
||||||
|
|
||||||
|
BRONZE = 'b'
|
||||||
|
SILVER = 's'
|
||||||
|
GOLD = 'g'
|
||||||
|
DIAMOND = 'd'
|
||||||
|
|
||||||
|
class Flag(Enum):
|
||||||
|
"""Flags set for our account."""
|
||||||
|
|
||||||
|
ASK_FOR_REVIEW = 'r'
|
||||||
|
|
||||||
|
class StoreStyle(Enum):
|
||||||
|
"""Special looks for the store."""
|
||||||
|
|
||||||
|
NORMAL = 'n'
|
||||||
|
SANTA = 's'
|
||||||
|
|
||||||
|
tickets: Annotated[int, IOAttrs('ti')]
|
||||||
|
|
||||||
|
tokens: Annotated[int, IOAttrs('to')]
|
||||||
|
gold_pass: Annotated[bool, IOAttrs('g')]
|
||||||
|
remove_ads: Annotated[bool, IOAttrs('r')]
|
||||||
|
|
||||||
|
achievements: Annotated[int, IOAttrs('a')]
|
||||||
|
achievements_total: Annotated[int, IOAttrs('at')]
|
||||||
|
|
||||||
|
league_type: Annotated[LeagueType | None, IOAttrs('lt')]
|
||||||
|
league_num: Annotated[int | None, IOAttrs('ln')]
|
||||||
|
league_rank: Annotated[int | None, IOAttrs('lr')]
|
||||||
|
|
||||||
|
level: Annotated[int, IOAttrs('lv')]
|
||||||
|
xp: Annotated[int, IOAttrs('xp')]
|
||||||
|
xpmax: Annotated[int, IOAttrs('xpm')]
|
||||||
|
|
||||||
|
inbox_count: Annotated[int, IOAttrs('ibc')]
|
||||||
|
inbox_count_is_max: Annotated[bool, IOAttrs('ibcm')]
|
||||||
|
inbox_contains_prize: Annotated[bool, IOAttrs('icp')]
|
||||||
|
|
||||||
|
chests: Annotated[dict[str, Chest], IOAttrs('c')]
|
||||||
|
|
||||||
|
# State id of our purchases for builds 22459+.
|
||||||
|
purchases_state: Annotated[str | None, IOAttrs('p')]
|
||||||
|
|
||||||
|
flags: Annotated[set[Flag], IOAttrs('f', soft_default_factory=set)]
|
||||||
|
|
||||||
|
store_style: Annotated[
|
||||||
|
StoreStyle, IOAttrs('s', enum_fallback=StoreStyle.NORMAL)
|
||||||
|
]
|
||||||
67
dist/ba_data/python/bacommon/classic/_chest.py
vendored
Normal file
67
dist/ba_data/python/bacommon/classic/_chest.py
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""BombSquad specific bits."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import assert_never, Annotated, override
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs
|
||||||
|
import bacommon.displayitem as ditm
|
||||||
|
|
||||||
|
|
||||||
|
class ClassicChestAppearance(Enum):
|
||||||
|
"""Appearances bombsquad classic chests can have."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
DEFAULT = 'd'
|
||||||
|
L1 = 'l1'
|
||||||
|
L2 = 'l2'
|
||||||
|
L3 = 'l3'
|
||||||
|
L4 = 'l4'
|
||||||
|
L5 = 'l5'
|
||||||
|
L6 = 'l6'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pretty_name(self) -> str:
|
||||||
|
"""Pretty name for the chest in English."""
|
||||||
|
# pylint: disable=too-many-return-statements
|
||||||
|
cls = ClassicChestAppearance
|
||||||
|
|
||||||
|
if self is cls.UNKNOWN:
|
||||||
|
return 'Unknown Chest'
|
||||||
|
if self is cls.DEFAULT:
|
||||||
|
return 'Chest'
|
||||||
|
if self is cls.L1:
|
||||||
|
return 'L1 Chest'
|
||||||
|
if self is cls.L2:
|
||||||
|
return 'L2 Chest'
|
||||||
|
if self is cls.L3:
|
||||||
|
return 'L3 Chest'
|
||||||
|
if self is cls.L4:
|
||||||
|
return 'L4 Chest'
|
||||||
|
if self is cls.L5:
|
||||||
|
return 'L5 Chest'
|
||||||
|
if self is cls.L6:
|
||||||
|
return 'L6 Chest'
|
||||||
|
|
||||||
|
assert_never(self)
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ClassicChestDisplayItem(ditm.Item):
|
||||||
|
"""Display a chest."""
|
||||||
|
|
||||||
|
appearance: Annotated[ClassicChestAppearance, IOAttrs('a')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ditm.ItemTypeID:
|
||||||
|
return ditm.ItemTypeID.CHEST
|
||||||
|
|
||||||
|
@override
|
||||||
|
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
return self.appearance.pretty_name, []
|
||||||
9
dist/ba_data/python/bacommon/classic/_classic.py
vendored
Normal file
9
dist/ba_data/python/bacommon/classic/_classic.py
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""BombSquad specific bits."""
|
||||||
|
|
||||||
|
# Token counts for our various packs.
|
||||||
|
TOKENS1_COUNT = 50
|
||||||
|
TOKENS2_COUNT = 500
|
||||||
|
TOKENS3_COUNT = 1200
|
||||||
|
TOKENS4_COUNT = 2600
|
||||||
28
dist/ba_data/python/bacommon/classic/_displayitem.py
vendored
Normal file
28
dist/ba_data/python/bacommon/classic/_displayitem.py
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Display-item bits of classic."""
|
||||||
|
|
||||||
|
# from __future__ import annotations
|
||||||
|
|
||||||
|
# from enum import Enum
|
||||||
|
# from typing import assert_never, Annotated, override
|
||||||
|
# from dataclasses import dataclass
|
||||||
|
|
||||||
|
# from efro.dataclassio import ioprepped, IOAttrs
|
||||||
|
# import bacommon.displayitem as ditm
|
||||||
|
|
||||||
|
# @ioprepped
|
||||||
|
# @dataclass
|
||||||
|
# class ClassicCharacterDisplayItem(ditm.Item):
|
||||||
|
# """Display a character."""
|
||||||
|
|
||||||
|
# : Annotated[ClassicChestAppearance, IOAttrs('a')]
|
||||||
|
|
||||||
|
# @override
|
||||||
|
# @classmethod
|
||||||
|
# def get_type_id(cls) -> ditm.ItemTypeID:
|
||||||
|
# return ditm.ItemTypeID.CHEST
|
||||||
|
|
||||||
|
# @override
|
||||||
|
# def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
# return self.appearance.pretty_name, []
|
||||||
249
dist/ba_data/python/bacommon/classic/_msg.py
vendored
Normal file
249
dist/ba_data/python/bacommon/classic/_msg.py
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""BombSquad specific bits."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Annotated, override
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs
|
||||||
|
from efro.message import Message, Response
|
||||||
|
|
||||||
|
import bacommon.displayitem as ditm
|
||||||
|
import bacommon.clouddialog as cdlg
|
||||||
|
import bacommon.clienteffect as clfx
|
||||||
|
from bacommon.classic._chest import ClassicChestAppearance
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class GetClassicLeaguePresidentButtonInfoMessage(Message):
|
||||||
|
"""Curious who is president of my league?.."""
|
||||||
|
|
||||||
|
season: Annotated[str | None, IOAttrs('s')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [GetClassicLeaguePresidentButtonInfoResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class GetClassicLeaguePresidentButtonInfoResponse(Response):
|
||||||
|
"""Here's that info about the president you asked for boss."""
|
||||||
|
|
||||||
|
# Lstr for the name shown on the button.
|
||||||
|
name: Annotated[str | None, IOAttrs('n')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class GetClassicPurchasesMessage(Message):
|
||||||
|
"""Asking for current account's classic purchases."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [GetClassicPurchasesResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class GetClassicPurchasesResponse(Response):
|
||||||
|
"""Here's those classic purchases ya asked for boss."""
|
||||||
|
|
||||||
|
purchases: Annotated[set[str], IOAttrs('p')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class GlobalProfileCheckMessage(Message):
|
||||||
|
"""Is this global profile name available?"""
|
||||||
|
|
||||||
|
name: Annotated[str, IOAttrs('n')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [GlobalProfileCheckResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class GlobalProfileCheckResponse(Response):
|
||||||
|
"""Here's that profile check ya asked for boss."""
|
||||||
|
|
||||||
|
available: Annotated[bool, IOAttrs('a')]
|
||||||
|
ticket_cost: Annotated[int, IOAttrs('tc')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class InboxRequestMessage(Message):
|
||||||
|
"""Message requesting our inbox."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [InboxRequestResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class InboxRequestResponse(Response):
|
||||||
|
"""Here's that inbox contents you asked for, boss."""
|
||||||
|
|
||||||
|
wrappers: Annotated[list[cdlg.Wrapper], IOAttrs('w')]
|
||||||
|
|
||||||
|
# Printable error if something goes wrong.
|
||||||
|
error: Annotated[str | None, IOAttrs('e')] = None
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class LegacyRequest(Message):
|
||||||
|
"""A generic request for the legacy master server."""
|
||||||
|
|
||||||
|
request: Annotated[str, IOAttrs('r')]
|
||||||
|
request_type: Annotated[str, IOAttrs('t')]
|
||||||
|
user_agent_string: Annotated[str, IOAttrs('u')]
|
||||||
|
data: Annotated[str, IOAttrs('d')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [LegacyResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class LegacyResponse(Response):
|
||||||
|
"""Response for generic legacy request."""
|
||||||
|
|
||||||
|
data: Annotated[str | None, IOAttrs('d')]
|
||||||
|
zipped: Annotated[bool, IOAttrs('z')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ChestInfoMessage(Message):
|
||||||
|
"""Request info about a chest."""
|
||||||
|
|
||||||
|
chest_id: Annotated[str, IOAttrs('i')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [ChestInfoResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ChestInfoResponse(Response):
|
||||||
|
"""Here's that chest info you asked for, boss."""
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Chest:
|
||||||
|
"""A lovely chest."""
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PrizeSet:
|
||||||
|
"""A possible set of prizes for this chest."""
|
||||||
|
|
||||||
|
weight: Annotated[float, IOAttrs('w')]
|
||||||
|
contents: Annotated[list[ditm.Wrapper], IOAttrs('c')]
|
||||||
|
|
||||||
|
appearance: Annotated[
|
||||||
|
ClassicChestAppearance,
|
||||||
|
IOAttrs('a', enum_fallback=ClassicChestAppearance.UNKNOWN),
|
||||||
|
]
|
||||||
|
|
||||||
|
# How much it costs to unlock *now*.
|
||||||
|
unlock_tokens: Annotated[int, IOAttrs('tk')]
|
||||||
|
|
||||||
|
# When it unlocks on its own.
|
||||||
|
unlock_time: Annotated[datetime.datetime, IOAttrs('t')]
|
||||||
|
|
||||||
|
# Possible prizes we contain.
|
||||||
|
prizesets: Annotated[list[PrizeSet], IOAttrs('p')]
|
||||||
|
|
||||||
|
# Are ads allowed now?
|
||||||
|
ad_allow: Annotated[bool, IOAttrs('aa')]
|
||||||
|
|
||||||
|
chest: Annotated[Chest | None, IOAttrs('c')]
|
||||||
|
user_tokens: Annotated[int | None, IOAttrs('t')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class PrivatePartyMessage(Message):
|
||||||
|
"""Message asking about info we need for private-party UI."""
|
||||||
|
|
||||||
|
need_datacode: Annotated[bool, IOAttrs('d')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [PrivatePartyResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class PrivatePartyResponse(Response):
|
||||||
|
"""Here's that private party UI info you asked for, boss."""
|
||||||
|
|
||||||
|
success: Annotated[bool, IOAttrs('s')]
|
||||||
|
tokens: Annotated[int, IOAttrs('t')]
|
||||||
|
gold_pass: Annotated[bool, IOAttrs('g')]
|
||||||
|
datacode: Annotated[str | None, IOAttrs('d')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ScoreSubmitMessage(Message):
|
||||||
|
"""Let the server know we got some score in something."""
|
||||||
|
|
||||||
|
score_token: Annotated[str, IOAttrs('t')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [ScoreSubmitResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ScoreSubmitResponse(Response):
|
||||||
|
"""Did something to that inbox entry, boss."""
|
||||||
|
|
||||||
|
# Things we should show on our end.
|
||||||
|
effects: Annotated[list[clfx.Effect], IOAttrs('fx')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class SendInfoMessage(Message):
|
||||||
|
"""User is using the send-info function."""
|
||||||
|
|
||||||
|
description: Annotated[str, IOAttrs('c')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [SendInfoResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class SendInfoResponse(Response):
|
||||||
|
"""Response to sending info to the server."""
|
||||||
|
|
||||||
|
handled: Annotated[bool, IOAttrs('v')]
|
||||||
|
message: Annotated[str | None, IOAttrs('m', store_default=False)] = None
|
||||||
|
effects: Annotated[list[clfx.Effect], IOAttrs('e', store_default=False)] = (
|
||||||
|
field(default_factory=list)
|
||||||
|
)
|
||||||
|
legacy_code: Annotated[str | None, IOAttrs('l', store_default=False)] = None
|
||||||
224
dist/ba_data/python/bacommon/clienteffect.py
vendored
Normal file
224
dist/ba_data/python/bacommon/clienteffect.py
vendored
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""ClientEffect related functionality.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Annotated, override, assert_never
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
||||||
|
|
||||||
|
|
||||||
|
class EffectTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
LEGACY_SCREEN_MESSAGE = 'm'
|
||||||
|
SCREEN_MESSAGE = 'sm'
|
||||||
|
SOUND = 's'
|
||||||
|
DELAY = 'd'
|
||||||
|
CHEST_WAIT_TIME_ANIMATION = 't'
|
||||||
|
TICKETS_ANIMATION = 'ta'
|
||||||
|
TOKENS_ANIMATION = 'toa'
|
||||||
|
|
||||||
|
|
||||||
|
class Effect(IOMultiType[EffectTypeID]):
|
||||||
|
"""Something that can happen on the client.
|
||||||
|
|
||||||
|
This can include screen messages, sounds, visual effects, etc.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: EffectTypeID) -> type[Effect]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
# pylint: disable=too-many-return-statements
|
||||||
|
|
||||||
|
t = EffectTypeID
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return Unknown
|
||||||
|
if type_id is t.LEGACY_SCREEN_MESSAGE:
|
||||||
|
return LegacyScreenMessage
|
||||||
|
if type_id is t.SCREEN_MESSAGE:
|
||||||
|
return ScreenMessage
|
||||||
|
if type_id is t.SOUND:
|
||||||
|
return PlaySound
|
||||||
|
if type_id is t.DELAY:
|
||||||
|
return Delay
|
||||||
|
if type_id is t.CHEST_WAIT_TIME_ANIMATION:
|
||||||
|
return ChestWaitTimeAnimation
|
||||||
|
if type_id is t.TICKETS_ANIMATION:
|
||||||
|
return TicketsAnimation
|
||||||
|
if type_id is t.TOKENS_ANIMATION:
|
||||||
|
return TokensAnimation
|
||||||
|
|
||||||
|
# Important to make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> Effect:
|
||||||
|
# If we encounter some future message type we don't know
|
||||||
|
# anything about, drop in a placeholder.
|
||||||
|
return Unknown()
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Unknown(Effect):
|
||||||
|
"""Fallback substitute for types we don't recognize."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class LegacyScreenMessage(Effect):
|
||||||
|
"""Display a screen-message (Legacy version).
|
||||||
|
|
||||||
|
This will be processed as an Lstr with translation category
|
||||||
|
'serverResponses'.
|
||||||
|
|
||||||
|
When possible, migrate to using :class:`ScreenMessage`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
message: Annotated[str, IOAttrs('m')]
|
||||||
|
subs: Annotated[list[str], IOAttrs('s', store_default=False)] = field(
|
||||||
|
default_factory=list
|
||||||
|
)
|
||||||
|
color: Annotated[
|
||||||
|
tuple[float, float, float], IOAttrs('c', store_default=False)
|
||||||
|
] = (1.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.LEGACY_SCREEN_MESSAGE
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ScreenMessage(Effect):
|
||||||
|
"""Display a screen-message.
|
||||||
|
|
||||||
|
Supported on engine build 22606 or newer.
|
||||||
|
|
||||||
|
This version does no translation by default (expecting translation
|
||||||
|
to happen server-side). Pass a Lstr json string and set is_lstr=True
|
||||||
|
for client-side translation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
message: Annotated[str, IOAttrs('m')]
|
||||||
|
color: Annotated[
|
||||||
|
tuple[float, float, float], IOAttrs('c', store_default=False)
|
||||||
|
] = (1.0, 1.0, 1.0)
|
||||||
|
is_lstr: Annotated[bool, IOAttrs('l', store_default=False)] = False
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.SCREEN_MESSAGE
|
||||||
|
|
||||||
|
|
||||||
|
class Sound(Enum):
|
||||||
|
"""Sounds that can be played."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
CASH_REGISTER = 'c'
|
||||||
|
ERROR = 'e'
|
||||||
|
POWER_DOWN = 'p'
|
||||||
|
GUN_COCKING = 'g'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class PlaySound(Effect):
|
||||||
|
"""Play a sound."""
|
||||||
|
|
||||||
|
sound: Annotated[Sound, IOAttrs('s', enum_fallback=Sound.UNKNOWN)]
|
||||||
|
volume: Annotated[float, IOAttrs('v', store_default=False)] = 1.0
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.SOUND
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ChestWaitTimeAnimation(Effect):
|
||||||
|
"""Animate chest wait time changing."""
|
||||||
|
|
||||||
|
chestid: Annotated[str, IOAttrs('c')]
|
||||||
|
duration: Annotated[float, IOAttrs('u')]
|
||||||
|
startvalue: Annotated[datetime.datetime, IOAttrs('o')]
|
||||||
|
endvalue: Annotated[datetime.datetime, IOAttrs('n')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.CHEST_WAIT_TIME_ANIMATION
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class TicketsAnimation(Effect):
|
||||||
|
"""Animate tickets count."""
|
||||||
|
|
||||||
|
duration: Annotated[float, IOAttrs('u')]
|
||||||
|
startvalue: Annotated[int, IOAttrs('s')]
|
||||||
|
endvalue: Annotated[int, IOAttrs('e')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.TICKETS_ANIMATION
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class TokensAnimation(Effect):
|
||||||
|
"""Animate tokens count."""
|
||||||
|
|
||||||
|
duration: Annotated[float, IOAttrs('u')]
|
||||||
|
startvalue: Annotated[int, IOAttrs('s')]
|
||||||
|
endvalue: Annotated[int, IOAttrs('e')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.TOKENS_ANIMATION
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Delay(Effect):
|
||||||
|
"""Delay effect processing."""
|
||||||
|
|
||||||
|
seconds: Annotated[float, IOAttrs('s')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> EffectTypeID:
|
||||||
|
return EffectTypeID.DELAY
|
||||||
107
dist/ba_data/python/bacommon/cloud.py
vendored
107
dist/ba_data/python/bacommon/cloud.py
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to cloud functionality."""
|
"""Cloud related functionality.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -10,9 +16,13 @@ from typing import TYPE_CHECKING, Annotated, override
|
||||||
|
|
||||||
from efro.message import Message, Response
|
from efro.message import Message, Response
|
||||||
from efro.dataclassio import ioprepped, IOAttrs
|
from efro.dataclassio import ioprepped, IOAttrs
|
||||||
|
from bacommon.analytics import AnalyticsEvent
|
||||||
from bacommon.securedata import SecureDataChecker
|
from bacommon.securedata import SecureDataChecker
|
||||||
from bacommon.transfer import DirectoryManifest
|
from bacommon.transfer import DirectoryManifest
|
||||||
from bacommon.login import LoginType
|
from bacommon.login import LoginType
|
||||||
|
from bacommon.docui import DocUIRequest, DocUIResponse
|
||||||
|
import bacommon.displayitem as ditm
|
||||||
|
import bacommon.clienteffect as clfx
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
pass
|
pass
|
||||||
|
|
@ -357,3 +367,98 @@ class CloudValsResponse(Response):
|
||||||
"""Here's them cloud vals ya asked for, boss."""
|
"""Here's them cloud vals ya asked for, boss."""
|
||||||
|
|
||||||
vals: Annotated[CloudVals, IOAttrs('v')]
|
vals: Annotated[CloudVals, IOAttrs('v')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ChestActionMessage(Message):
|
||||||
|
"""Request action about a chest."""
|
||||||
|
|
||||||
|
class Action(Enum):
|
||||||
|
"""Types of actions we can request."""
|
||||||
|
|
||||||
|
# Unlocking (for free or with tokens).
|
||||||
|
UNLOCK = 'u'
|
||||||
|
|
||||||
|
# Watched an ad to reduce wait.
|
||||||
|
AD = 'ad'
|
||||||
|
|
||||||
|
action: Annotated[Action, IOAttrs('a')]
|
||||||
|
|
||||||
|
# Tokens we are paying (only applies to unlock).
|
||||||
|
token_payment: Annotated[int, IOAttrs('t')]
|
||||||
|
|
||||||
|
chest_id: Annotated[str, IOAttrs('i')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [ChestActionResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ChestActionResponse(Response):
|
||||||
|
"""Here's the results of that action you asked for, boss."""
|
||||||
|
|
||||||
|
# Tokens that were actually charged.
|
||||||
|
tokens_charged: Annotated[int, IOAttrs('t')] = 0
|
||||||
|
|
||||||
|
# If present, signifies the chest has been opened and we should show
|
||||||
|
# the user this stuff that was in it.
|
||||||
|
contents: Annotated[list[ditm.Wrapper] | None, IOAttrs('c')] = None
|
||||||
|
|
||||||
|
# If contents are present, which of the chest's prize-sets they
|
||||||
|
# represent.
|
||||||
|
prizeindex: Annotated[int, IOAttrs('i')] = 0
|
||||||
|
|
||||||
|
# Printable error if something goes wrong.
|
||||||
|
error: Annotated[str | None, IOAttrs('e')] = None
|
||||||
|
|
||||||
|
# Printable warning. Shown in orange with an error sound. Does not
|
||||||
|
# mean the action failed; only that there's something to tell the
|
||||||
|
# users such as 'It looks like you are faking ad views; stop it or
|
||||||
|
# you won't have ad options anymore.'
|
||||||
|
warning: Annotated[str | None, IOAttrs('w', store_default=False)] = None
|
||||||
|
|
||||||
|
# Printable success message. Shown in green with a cash-register
|
||||||
|
# sound. Can be used for things like successful wait reductions via
|
||||||
|
# ad views. Used in builds earlier than 22311; can remove once
|
||||||
|
# 22311+ is ubiquitous.
|
||||||
|
success_msg: Annotated[str | None, IOAttrs('s', store_default=False)] = None
|
||||||
|
|
||||||
|
# Effects to show on the client. Replaces warning and success_msg in
|
||||||
|
# build 22311 or newer.
|
||||||
|
effects: Annotated[
|
||||||
|
list[clfx.Effect], IOAttrs('fx', store_default=False)
|
||||||
|
] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class FulfillDocUIRequest(Message):
|
||||||
|
"""Can a fella get a doc-ui round here?"""
|
||||||
|
|
||||||
|
request: Annotated[DocUIRequest, IOAttrs('r')]
|
||||||
|
domain: Annotated[str, IOAttrs('d')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [FulfillDocUIResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class FulfillDocUIResponse(Response):
|
||||||
|
"""Here's that doc-ui you asked for, boss."""
|
||||||
|
|
||||||
|
response: Annotated[DocUIResponse, IOAttrs('r')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class AnalyticsEventMessage(Message):
|
||||||
|
"""Have a nice analytics event!"""
|
||||||
|
|
||||||
|
event: Annotated[AnalyticsEvent, IOAttrs('e')]
|
||||||
|
|
|
||||||
29
dist/ba_data/python/bacommon/clouddialog/__init__.py
vendored
Normal file
29
dist/ba_data/python/bacommon/clouddialog/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Functionality related to cloud-dialogs.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from bacommon.clouddialog._clouddialog import (
|
||||||
|
CloudDialogTypeID,
|
||||||
|
CloudDialog,
|
||||||
|
Unknown,
|
||||||
|
Wrapper,
|
||||||
|
Action,
|
||||||
|
ActionMessage,
|
||||||
|
ActionResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'CloudDialogTypeID',
|
||||||
|
'CloudDialog',
|
||||||
|
'Unknown',
|
||||||
|
'Wrapper',
|
||||||
|
'Action',
|
||||||
|
'ActionMessage',
|
||||||
|
'ActionResponse',
|
||||||
|
]
|
||||||
142
dist/ba_data/python/bacommon/clouddialog/_clouddialog.py
vendored
Normal file
142
dist/ba_data/python/bacommon/clouddialog/_clouddialog.py
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Simple cloud-defined UIs for things like notifications.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Annotated, override, assert_never
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
||||||
|
from efro.message import Message, Response
|
||||||
|
|
||||||
|
import bacommon.clienteffect as clfx
|
||||||
|
|
||||||
|
|
||||||
|
class CloudDialogTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
BASIC = 'b'
|
||||||
|
|
||||||
|
|
||||||
|
class CloudDialog(IOMultiType[CloudDialogTypeID]):
|
||||||
|
"""Small self-contained ui bit provided by the cloud.
|
||||||
|
|
||||||
|
These take care of updating and/or dismissing themselves based on
|
||||||
|
user input. Useful for things such as inbox messages. For more
|
||||||
|
complex UI construction, look at :mod:`bacommon.docui`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> CloudDialogTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: CloudDialogTypeID) -> type[CloudDialog]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = CloudDialogTypeID
|
||||||
|
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return Unknown
|
||||||
|
|
||||||
|
if type_id is t.BASIC:
|
||||||
|
from bacommon.clouddialog.basic import BasicCloudDialog
|
||||||
|
|
||||||
|
return BasicCloudDialog
|
||||||
|
|
||||||
|
# Make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> CloudDialog:
|
||||||
|
# If we encounter some future message type we don't know
|
||||||
|
# anything about, drop in a placeholder.
|
||||||
|
return Unknown()
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Unknown(CloudDialog):
|
||||||
|
"""Fallback type for unrecognized entries."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> CloudDialogTypeID:
|
||||||
|
return CloudDialogTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Wrapper:
|
||||||
|
"""Wrapper for a CloudDialog and its common data."""
|
||||||
|
|
||||||
|
id: Annotated[str, IOAttrs('i')]
|
||||||
|
createtime: Annotated[datetime.datetime, IOAttrs('c')]
|
||||||
|
ui: Annotated[CloudDialog, IOAttrs('e')]
|
||||||
|
|
||||||
|
|
||||||
|
class Action(Enum):
|
||||||
|
"""Types of actions we can run."""
|
||||||
|
|
||||||
|
BUTTON_PRESS_POSITIVE = 'p'
|
||||||
|
BUTTON_PRESS_NEGATIVE = 'n'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ActionMessage(Message):
|
||||||
|
"""Do something to a client ui."""
|
||||||
|
|
||||||
|
id: Annotated[str, IOAttrs('i')]
|
||||||
|
action: Annotated[Action, IOAttrs('a')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_response_types(cls) -> list[type[Response] | None]:
|
||||||
|
return [ActionResponse]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ActionResponse(Response):
|
||||||
|
"""Did something to that inbox entry, boss."""
|
||||||
|
|
||||||
|
class ErrorType(Enum):
|
||||||
|
"""Types of errors that may have occurred."""
|
||||||
|
|
||||||
|
# Probably a future error type we don't recognize.
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
|
||||||
|
# Something went wrong on the server, but specifics are not
|
||||||
|
# relevant.
|
||||||
|
INTERNAL = 'i'
|
||||||
|
|
||||||
|
# The entry expired on the server. In various cases such as 'ok'
|
||||||
|
# buttons this can generally be ignored.
|
||||||
|
EXPIRED = 'e'
|
||||||
|
|
||||||
|
error_type: Annotated[
|
||||||
|
ErrorType | None, IOAttrs('et', enum_fallback=ErrorType.UNKNOWN)
|
||||||
|
]
|
||||||
|
|
||||||
|
# User facing error message in the case of errors.
|
||||||
|
error_message: Annotated[str | None, IOAttrs('em')]
|
||||||
|
|
||||||
|
effects: Annotated[list[clfx.Effect], IOAttrs('fx')]
|
||||||
233
dist/ba_data/python/bacommon/clouddialog/basic.py
vendored
Normal file
233
dist/ba_data/python/bacommon/clouddialog/basic.py
vendored
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Basic cloud-dialog."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Annotated, override, assert_never
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
||||||
|
|
||||||
|
import bacommon.displayitem as ditm
|
||||||
|
from bacommon.clouddialog._clouddialog import CloudDialog, CloudDialogTypeID
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
TEXT = 't'
|
||||||
|
LINK = 'l'
|
||||||
|
BS_CLASSIC_TOURNEY_RESULT = 'ct'
|
||||||
|
DISPLAY_ITEMS = 'di'
|
||||||
|
EXPIRE_TIME = 'd'
|
||||||
|
|
||||||
|
|
||||||
|
class Component(IOMultiType[ComponentTypeID]):
|
||||||
|
"""Top level class for our multitype."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ComponentTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: ComponentTypeID) -> type[Component]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = ComponentTypeID
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return Unknown
|
||||||
|
if type_id is t.TEXT:
|
||||||
|
return Text
|
||||||
|
if type_id is t.LINK:
|
||||||
|
return Link
|
||||||
|
if type_id is t.BS_CLASSIC_TOURNEY_RESULT:
|
||||||
|
return ClassicTourneyResult
|
||||||
|
if type_id is t.DISPLAY_ITEMS:
|
||||||
|
return DisplayItems
|
||||||
|
if type_id is t.EXPIRE_TIME:
|
||||||
|
return ExpireTime
|
||||||
|
|
||||||
|
# Important to make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> Component:
|
||||||
|
# If we encounter some future message type we don't know
|
||||||
|
# anything about, drop in a placeholder.
|
||||||
|
return Unknown()
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Unknown(Component):
|
||||||
|
"""An unknown basic client component type.
|
||||||
|
|
||||||
|
In practice these should never show up since the master-server
|
||||||
|
generates these on the fly for the client and so should not send
|
||||||
|
clients one they can't digest.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ComponentTypeID:
|
||||||
|
return ComponentTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Text(Component):
|
||||||
|
"""Show some text in the inbox message."""
|
||||||
|
|
||||||
|
text: Annotated[str, IOAttrs('t')]
|
||||||
|
subs: Annotated[list[str], IOAttrs('s', store_default=False)] = field(
|
||||||
|
default_factory=list
|
||||||
|
)
|
||||||
|
scale: Annotated[float, IOAttrs('sc', store_default=False)] = 1.0
|
||||||
|
color: Annotated[
|
||||||
|
tuple[float, float, float, float], IOAttrs('c', store_default=False)
|
||||||
|
] = (1.0, 1.0, 1.0, 1.0)
|
||||||
|
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
|
||||||
|
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ComponentTypeID:
|
||||||
|
return ComponentTypeID.TEXT
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Link(Component):
|
||||||
|
"""Show a link in the inbox message."""
|
||||||
|
|
||||||
|
url: Annotated[str, IOAttrs('u')]
|
||||||
|
label: Annotated[str, IOAttrs('l')]
|
||||||
|
subs: Annotated[list[str], IOAttrs('s', store_default=False)] = field(
|
||||||
|
default_factory=list
|
||||||
|
)
|
||||||
|
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
|
||||||
|
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ComponentTypeID:
|
||||||
|
return ComponentTypeID.LINK
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ClassicTourneyResult(Component):
|
||||||
|
"""Show info about a classic tourney."""
|
||||||
|
|
||||||
|
tournament_id: Annotated[str, IOAttrs('t')]
|
||||||
|
game: Annotated[str, IOAttrs('g')]
|
||||||
|
players: Annotated[int, IOAttrs('p')]
|
||||||
|
rank: Annotated[int, IOAttrs('r')]
|
||||||
|
trophy: Annotated[str | None, IOAttrs('tr')]
|
||||||
|
prizes: Annotated[list[ditm.Wrapper], IOAttrs('pr')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ComponentTypeID:
|
||||||
|
return ComponentTypeID.BS_CLASSIC_TOURNEY_RESULT
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class DisplayItems(Component):
|
||||||
|
"""Show some display-items."""
|
||||||
|
|
||||||
|
items: Annotated[list[ditm.Wrapper], IOAttrs('d')]
|
||||||
|
width: Annotated[float, IOAttrs('w')] = 100.0
|
||||||
|
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
|
||||||
|
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ComponentTypeID:
|
||||||
|
return ComponentTypeID.DISPLAY_ITEMS
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ExpireTime(Component):
|
||||||
|
"""Show expire-time."""
|
||||||
|
|
||||||
|
time: Annotated[datetime.datetime, IOAttrs('d')]
|
||||||
|
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
|
||||||
|
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ComponentTypeID:
|
||||||
|
return ComponentTypeID.EXPIRE_TIME
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonLabel(Enum):
|
||||||
|
"""Distinct button labels we support."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
OK = 'o'
|
||||||
|
APPLY = 'a'
|
||||||
|
CANCEL = 'c'
|
||||||
|
ACCEPT = 'ac'
|
||||||
|
DECLINE = 'dn'
|
||||||
|
IGNORE = 'ig'
|
||||||
|
CLAIM = 'cl'
|
||||||
|
DISCARD = 'd'
|
||||||
|
|
||||||
|
|
||||||
|
class InteractionStyle(Enum):
|
||||||
|
"""Overall interaction styles we support."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
BUTTON_POSITIVE = 'p'
|
||||||
|
BUTTON_POSITIVE_NEGATIVE = 'pn'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class BasicCloudDialog(CloudDialog):
|
||||||
|
"""A basic UI for the client."""
|
||||||
|
|
||||||
|
components: Annotated[list[Component], IOAttrs('s')]
|
||||||
|
|
||||||
|
interaction_style: Annotated[
|
||||||
|
InteractionStyle, IOAttrs('i', enum_fallback=InteractionStyle.UNKNOWN)
|
||||||
|
] = InteractionStyle.BUTTON_POSITIVE
|
||||||
|
|
||||||
|
button_label_positive: Annotated[
|
||||||
|
ButtonLabel, IOAttrs('p', enum_fallback=ButtonLabel.UNKNOWN)
|
||||||
|
] = ButtonLabel.OK
|
||||||
|
|
||||||
|
button_label_negative: Annotated[
|
||||||
|
ButtonLabel, IOAttrs('n', enum_fallback=ButtonLabel.UNKNOWN)
|
||||||
|
] = ButtonLabel.CANCEL
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> CloudDialogTypeID:
|
||||||
|
return CloudDialogTypeID.BASIC
|
||||||
|
|
||||||
|
def contains_unknown_elements(self) -> bool:
|
||||||
|
"""Whether something within us is an unknown type or enum."""
|
||||||
|
return (
|
||||||
|
self.interaction_style is InteractionStyle.UNKNOWN
|
||||||
|
or self.button_label_positive is ButtonLabel.UNKNOWN
|
||||||
|
or self.button_label_negative is ButtonLabel.UNKNOWN
|
||||||
|
or any(
|
||||||
|
c.get_type_id() is ComponentTypeID.UNKNOWN
|
||||||
|
for c in self.components
|
||||||
|
)
|
||||||
|
)
|
||||||
198
dist/ba_data/python/bacommon/displayitem.py
vendored
Normal file
198
dist/ba_data/python/bacommon/displayitem.py
vendored
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Functionality for displaying currencies, prizes, owned items, etc.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Annotated, override, assert_never
|
||||||
|
|
||||||
|
from efro.util import pairs_to_flat
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
||||||
|
|
||||||
|
|
||||||
|
class ItemTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
TICKETS = 't'
|
||||||
|
TICKETS_PURPLE = 'tp'
|
||||||
|
TOKENS = 'k'
|
||||||
|
TEST = 's'
|
||||||
|
CHEST = 'c'
|
||||||
|
|
||||||
|
|
||||||
|
class Item(IOMultiType[ItemTypeID]):
|
||||||
|
"""Some amount of something that can be shown or described.
|
||||||
|
|
||||||
|
Used to depict chest contents, inventory, rewards, etc.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ItemTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: ItemTypeID) -> type[Item]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = ItemTypeID
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return Unknown
|
||||||
|
if type_id is t.TICKETS:
|
||||||
|
return Tickets
|
||||||
|
if type_id is t.TICKETS_PURPLE:
|
||||||
|
return PurpleTickets
|
||||||
|
if type_id is t.TOKENS:
|
||||||
|
return Tokens
|
||||||
|
if type_id is t.TEST:
|
||||||
|
return Test
|
||||||
|
if type_id is t.CHEST:
|
||||||
|
from bacommon.classic._chest import ClassicChestDisplayItem
|
||||||
|
|
||||||
|
return ClassicChestDisplayItem
|
||||||
|
|
||||||
|
# Important to make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
"""Return a string description and subs for the item.
|
||||||
|
|
||||||
|
Will be translated on the client using the 'displayItemNames'
|
||||||
|
Lstr category.
|
||||||
|
|
||||||
|
These decriptions are baked into the display-item wrapper and
|
||||||
|
should be accessed from there when available. This allows
|
||||||
|
clients to give descriptions even for newer display item types
|
||||||
|
they don't recognize.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
# Implement fallbacks so client can digest item lists even if they
|
||||||
|
# contain unrecognized stuff. The wrapper contains basic
|
||||||
|
# baked down info that they can still use in such cases.
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> Item:
|
||||||
|
return Unknown()
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Unknown(Item):
|
||||||
|
"""Something we don't know how to display."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ItemTypeID:
|
||||||
|
return ItemTypeID.UNKNOWN
|
||||||
|
|
||||||
|
@override
|
||||||
|
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Make noise but don't break.
|
||||||
|
logging.exception(
|
||||||
|
'Unknown.get_description() should never be called.'
|
||||||
|
' Always access descriptions on the display-item wrapper.'
|
||||||
|
)
|
||||||
|
return 'Unknown', []
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Tickets(Item):
|
||||||
|
"""Some amount of tickets."""
|
||||||
|
|
||||||
|
count: Annotated[int, IOAttrs('c')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ItemTypeID:
|
||||||
|
return ItemTypeID.TICKETS
|
||||||
|
|
||||||
|
@override
|
||||||
|
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
return '${C} Tickets', [('${C}', str(self.count))]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class PurpleTickets(Item):
|
||||||
|
"""Some amount of purple tickets."""
|
||||||
|
|
||||||
|
count: Annotated[int, IOAttrs('c')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ItemTypeID:
|
||||||
|
return ItemTypeID.TICKETS_PURPLE
|
||||||
|
|
||||||
|
@override
|
||||||
|
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
return '${C} Purple Tickets', [('${C}', str(self.count))]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Tokens(Item):
|
||||||
|
"""Some amount of tokens."""
|
||||||
|
|
||||||
|
count: Annotated[int, IOAttrs('c')]
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ItemTypeID:
|
||||||
|
return ItemTypeID.TOKENS
|
||||||
|
|
||||||
|
@override
|
||||||
|
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
return '${C} Tokens', [('${C}', str(self.count))]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Test(Item):
|
||||||
|
"""Fills usable space for a display-item - good for calibration."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ItemTypeID:
|
||||||
|
return ItemTypeID.TEST
|
||||||
|
|
||||||
|
@override
|
||||||
|
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
|
||||||
|
return 'Test', []
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Wrapper:
|
||||||
|
"""Wraps a display-item and some baked out info.
|
||||||
|
|
||||||
|
This allows clients to at least give descriptions of new
|
||||||
|
display-item types they may not have locally.
|
||||||
|
"""
|
||||||
|
|
||||||
|
item: Annotated[Item, IOAttrs('i')]
|
||||||
|
description: Annotated[str, IOAttrs('d')]
|
||||||
|
description_subs: Annotated[list[str] | None, IOAttrs('s')]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_item(cls, item: Item) -> Wrapper:
|
||||||
|
"""Convenience method to wrap a display-item."""
|
||||||
|
desc, subs = item.get_description()
|
||||||
|
return Wrapper(item, desc, pairs_to_flat(subs))
|
||||||
30
dist/ba_data/python/bacommon/docui/__init__.py
vendored
Normal file
30
dist/ba_data/python/bacommon/docui/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Declarative UI system.
|
||||||
|
|
||||||
|
A high level way to build UIs that lives as a layer on top of engine
|
||||||
|
apis such as :mod:`bauiv1`. UIs can easily be serialized to json data
|
||||||
|
and be provided by webservers or other local or remote sources.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from bacommon.docui._docui import (
|
||||||
|
DocUIRequest,
|
||||||
|
DocUIRequestTypeID,
|
||||||
|
UnknownDocUIRequest,
|
||||||
|
DocUIResponse,
|
||||||
|
DocUIResponseTypeID,
|
||||||
|
UnknownDocUIResponse,
|
||||||
|
DocUIWebRequest,
|
||||||
|
DocUIWebResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'DocUIRequest',
|
||||||
|
'DocUIRequestTypeID',
|
||||||
|
'UnknownDocUIRequest',
|
||||||
|
'DocUIResponse',
|
||||||
|
'DocUIResponseTypeID',
|
||||||
|
'UnknownDocUIResponse',
|
||||||
|
'DocUIWebRequest',
|
||||||
|
'DocUIWebResponse',
|
||||||
|
]
|
||||||
172
dist/ba_data/python/bacommon/docui/_docui.py
vendored
Normal file
172
dist/ba_data/python/bacommon/docui/_docui.py
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Version 1 of our doc-ui system."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import override, assert_never, TYPE_CHECKING, Annotated
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
||||||
|
from bacommon.locale import Locale
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DocUIRequestTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
V1 = 'v1'
|
||||||
|
|
||||||
|
|
||||||
|
class DocUIRequest(IOMultiType[DocUIRequestTypeID]):
|
||||||
|
"""A request for some UI."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DocUIRequestTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: DocUIRequestTypeID) -> type[DocUIRequest]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = DocUIRequestTypeID
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return UnknownDocUIRequest
|
||||||
|
if type_id is t.V1:
|
||||||
|
from bacommon.docui.v1 import Request
|
||||||
|
|
||||||
|
return Request
|
||||||
|
|
||||||
|
# Make sure we cover all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> DocUIRequest:
|
||||||
|
# If we encounter some future type we don't know anything about,
|
||||||
|
# drop in a placeholder.
|
||||||
|
return UnknownDocUIRequest()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id_storage_name(cls) -> str:
|
||||||
|
return '_t'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class UnknownDocUIRequest(DocUIRequest):
|
||||||
|
"""Fallback type for unrecognized UI types.
|
||||||
|
|
||||||
|
Will show the client a 'cannot display this UI' placeholder request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DocUIRequestTypeID:
|
||||||
|
return DocUIRequestTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
class DocUIResponseTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
V1 = 'v1'
|
||||||
|
|
||||||
|
|
||||||
|
class DocUIResponse(IOMultiType[DocUIResponseTypeID]):
|
||||||
|
"""A UI provied in response to a :class:`DocUIRequest`."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DocUIResponseTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: DocUIResponseTypeID) -> type[DocUIResponse]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = DocUIResponseTypeID
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return UnknownDocUIResponse
|
||||||
|
if type_id is t.V1:
|
||||||
|
from bacommon.docui.v1 import Response
|
||||||
|
|
||||||
|
return Response
|
||||||
|
|
||||||
|
# Make sure we cover all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> DocUIResponse:
|
||||||
|
# If we encounter some future type we don't know anything about,
|
||||||
|
# drop in a placeholder.
|
||||||
|
return UnknownDocUIResponse()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id_storage_name(cls) -> str:
|
||||||
|
return '_t'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class UnknownDocUIResponse(DocUIResponse):
|
||||||
|
"""Fallback type for unrecognized UI types.
|
||||||
|
|
||||||
|
Will show the client a 'cannot display this UI' placeholder response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DocUIResponseTypeID:
|
||||||
|
return DocUIResponseTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class DocUIWebRequest:
|
||||||
|
"""Complete data sent for doc-ui http requests."""
|
||||||
|
|
||||||
|
#: The wrapped doc-ui request.
|
||||||
|
doc_ui_request: Annotated[DocUIRequest, IOAttrs('r')]
|
||||||
|
|
||||||
|
#: The current locale of the client. doc-ui generally deals in raw
|
||||||
|
#: strings and expects localization to happen on the server.
|
||||||
|
locale: Annotated[Locale, IOAttrs('l')]
|
||||||
|
|
||||||
|
#: Engine build number. In some cases it may make sense to adjust
|
||||||
|
#: responses depending on available engine features.
|
||||||
|
engine_build_number: Annotated[int, IOAttrs('b')]
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class DocUIWebResponse:
|
||||||
|
"""Complete data returned for doc-ui http requests."""
|
||||||
|
|
||||||
|
#: Human readable error string (if an error occurs). Either this or
|
||||||
|
#: doc_ui_response should be set; not both.
|
||||||
|
error: Annotated[str | None, IOAttrs('e', store_default=False)] = None
|
||||||
|
|
||||||
|
#: doc-ui response. Either this or error should be set; not both.
|
||||||
|
doc_ui_response: Annotated[
|
||||||
|
DocUIResponse | None,
|
||||||
|
IOAttrs('r', store_default=False),
|
||||||
|
] = None
|
||||||
783
dist/ba_data/python/bacommon/docui/v1.py
vendored
Normal file
783
dist/ba_data/python/bacommon/docui/v1.py
vendored
Normal file
|
|
@ -0,0 +1,783 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Version 1 doc-ui types."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Annotated, override, assert_never
|
||||||
|
|
||||||
|
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
||||||
|
|
||||||
|
import bacommon.displayitem as ditm
|
||||||
|
import bacommon.clienteffect as clfx
|
||||||
|
from bacommon.docui._docui import (
|
||||||
|
DocUIRequest,
|
||||||
|
DocUIRequestTypeID,
|
||||||
|
DocUIResponse,
|
||||||
|
DocUIResponseTypeID,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RequestMethod(Enum):
|
||||||
|
"""Typeof of requests that can be made to doc-ui servers."""
|
||||||
|
|
||||||
|
#: An unknown request method. This can appear if a newer client is
|
||||||
|
#: requesting some method from an older server that is not known to
|
||||||
|
#: the server.
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
|
||||||
|
#: Fetch some resource. This can be retried and its results can
|
||||||
|
#: optionally be cached for some amount of time.
|
||||||
|
GET = 'g'
|
||||||
|
|
||||||
|
#: Change some resource. This cannot be implicitly retried (at least
|
||||||
|
#: without deduplication), nor can it be cached.
|
||||||
|
POST = 'p'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Request(DocUIRequest):
|
||||||
|
"""Full request to doc-ui."""
|
||||||
|
|
||||||
|
path: Annotated[str, IOAttrs('p')]
|
||||||
|
method: Annotated[
|
||||||
|
RequestMethod,
|
||||||
|
IOAttrs('m', store_default=False, enum_fallback=RequestMethod.UNKNOWN),
|
||||||
|
] = RequestMethod.GET
|
||||||
|
args: Annotated[dict, IOAttrs('r', store_default=False)] = field(
|
||||||
|
default_factory=dict
|
||||||
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DocUIRequestTypeID:
|
||||||
|
return DocUIRequestTypeID.V1
|
||||||
|
|
||||||
|
|
||||||
|
class ActionTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
BROWSE = 'b'
|
||||||
|
REPLACE = 'r'
|
||||||
|
LOCAL = 'l'
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
|
||||||
|
|
||||||
|
class Action(IOMultiType[ActionTypeID]):
|
||||||
|
"""Top level class for our multitype."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ActionTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: ActionTypeID) -> type[Action]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = ActionTypeID
|
||||||
|
if type_id is t.BROWSE:
|
||||||
|
return Browse
|
||||||
|
if type_id is t.REPLACE:
|
||||||
|
return Replace
|
||||||
|
if type_id is t.LOCAL:
|
||||||
|
return Local
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return UnknownAction
|
||||||
|
|
||||||
|
# Important to make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id_storage_name(cls) -> str:
|
||||||
|
return '_t'
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> Action:
|
||||||
|
# If we encounter some future type we don't know anything about,
|
||||||
|
# drop in a placeholder.
|
||||||
|
return UnknownAction()
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class UnknownAction(Action):
|
||||||
|
"""Action type we don't recognize."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ActionTypeID:
|
||||||
|
return ActionTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Browse(Action):
|
||||||
|
"""Browse to a new page in a new window."""
|
||||||
|
|
||||||
|
request: Annotated[Request, IOAttrs('r')]
|
||||||
|
|
||||||
|
#: Plays a swish.
|
||||||
|
default_sound: Annotated[bool, IOAttrs('ds', store_default=False)] = True
|
||||||
|
|
||||||
|
#: Client-effects to run immediately when the button is pressed.
|
||||||
|
#:
|
||||||
|
#: :meta private:
|
||||||
|
immediate_client_effects: Annotated[
|
||||||
|
list[clfx.Effect], IOAttrs('fx', store_default=False)
|
||||||
|
] = field(default_factory=list)
|
||||||
|
|
||||||
|
#: Local action to run immediately when the button is pressed. Will
|
||||||
|
#: be handled by
|
||||||
|
#: :meth:`bauiv1lib.docui.DocUIController.local_action()`.
|
||||||
|
immediate_local_action: Annotated[
|
||||||
|
str | None, IOAttrs('a', store_default=False)
|
||||||
|
] = None
|
||||||
|
immediate_local_action_args: Annotated[
|
||||||
|
dict | None, IOAttrs('aa', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ActionTypeID:
|
||||||
|
return ActionTypeID.BROWSE
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Replace(Action):
|
||||||
|
"""Replace current page with a new one.
|
||||||
|
|
||||||
|
Should be used to effectively 'modify' existing UIs by replacing
|
||||||
|
them with something slightly different. Things like scroll position
|
||||||
|
and selection will be carried across to the new layout when possible
|
||||||
|
to make for a seamless transition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
request: Annotated[Request, IOAttrs('r')]
|
||||||
|
|
||||||
|
#: Plays a click if triggered by a button press.
|
||||||
|
default_sound: Annotated[bool, IOAttrs('ds', store_default=False)] = True
|
||||||
|
|
||||||
|
#: Client-effects to run immediately when the button is pressed.
|
||||||
|
#:
|
||||||
|
#: :meta private:
|
||||||
|
immediate_client_effects: Annotated[
|
||||||
|
list[clfx.Effect], IOAttrs('fx', store_default=False)
|
||||||
|
] = field(default_factory=list)
|
||||||
|
|
||||||
|
#: Local action to run immediately when the button is pressed. Will
|
||||||
|
#: be handled by
|
||||||
|
#: :meth:`bauiv1lib.docui.DocUIController.local_action()`.
|
||||||
|
immediate_local_action: Annotated[
|
||||||
|
str | None, IOAttrs('a', store_default=False)
|
||||||
|
] = None
|
||||||
|
immediate_local_action_args: Annotated[
|
||||||
|
dict | None, IOAttrs('aa', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ActionTypeID:
|
||||||
|
return ActionTypeID.REPLACE
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Local(Action):
|
||||||
|
"""Perform only local actions; no new requests or page changes."""
|
||||||
|
|
||||||
|
close_window: Annotated[bool, IOAttrs('c', store_default=False)] = False
|
||||||
|
|
||||||
|
#: Plays a swish if closing the window or a click if triggered by a
|
||||||
|
#: button press.
|
||||||
|
default_sound: Annotated[bool, IOAttrs('ds', store_default=False)] = True
|
||||||
|
|
||||||
|
#: Client-effects to run immediately when the button is pressed.
|
||||||
|
#:
|
||||||
|
#: :meta private:
|
||||||
|
immediate_client_effects: Annotated[
|
||||||
|
list[clfx.Effect], IOAttrs('fx', store_default=False)
|
||||||
|
] = field(default_factory=list)
|
||||||
|
|
||||||
|
#: Local action to run immediately when the button is pressed. Will
|
||||||
|
#: be handled by
|
||||||
|
#: :meth:`bauiv1lib.docui.DocUIController.local_action()`.
|
||||||
|
immediate_local_action: Annotated[
|
||||||
|
str | None, IOAttrs('a', store_default=False)
|
||||||
|
] = None
|
||||||
|
immediate_local_action_args: Annotated[
|
||||||
|
dict | None, IOAttrs('aa', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> ActionTypeID:
|
||||||
|
return ActionTypeID.LOCAL
|
||||||
|
|
||||||
|
|
||||||
|
class HAlign(Enum):
|
||||||
|
"""Horizontal alignment."""
|
||||||
|
|
||||||
|
LEFT = 'l'
|
||||||
|
CENTER = 'c'
|
||||||
|
RIGHT = 'r'
|
||||||
|
|
||||||
|
|
||||||
|
class VAlign(Enum):
|
||||||
|
"""Vertical alignment."""
|
||||||
|
|
||||||
|
TOP = 't'
|
||||||
|
CENTER = 'c'
|
||||||
|
BOTTOM = 'b'
|
||||||
|
|
||||||
|
|
||||||
|
class DecorationTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
TEXT = 't'
|
||||||
|
IMAGE = 'i'
|
||||||
|
DISPLAY_ITEM = 'd'
|
||||||
|
|
||||||
|
|
||||||
|
class Decoration(IOMultiType[DecorationTypeID]):
|
||||||
|
"""Top level class for our multitype."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DecorationTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: DecorationTypeID) -> type[Decoration]:
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = DecorationTypeID
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return UnknownDecoration
|
||||||
|
if type_id is t.TEXT:
|
||||||
|
return Text
|
||||||
|
if type_id is t.IMAGE:
|
||||||
|
return Image
|
||||||
|
if type_id is t.DISPLAY_ITEM:
|
||||||
|
return DisplayItem
|
||||||
|
|
||||||
|
# Important to make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> Decoration:
|
||||||
|
# If we encounter some future type we don't know anything about,
|
||||||
|
# drop in a placeholder.
|
||||||
|
return UnknownDecoration()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id_storage_name(cls) -> str:
|
||||||
|
return '_t'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class UnknownDecoration(Decoration):
|
||||||
|
"""An unknown decoration.
|
||||||
|
|
||||||
|
In practice these should never show up since the master-server
|
||||||
|
generates these on the fly for the client and so should not send
|
||||||
|
clients one they can't digest.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DecorationTypeID:
|
||||||
|
return DecorationTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Text(Decoration):
|
||||||
|
"""Text decoration."""
|
||||||
|
|
||||||
|
#: Note that doc-ui accepts only raw :class:`str` values for text;
|
||||||
|
#: use :meth:`babase.Lstr.evaluate()` or whatnot for multi-language
|
||||||
|
#: support.
|
||||||
|
text: Annotated[str, IOAttrs('t')]
|
||||||
|
position: Annotated[tuple[float, float], IOAttrs('p')]
|
||||||
|
|
||||||
|
#: Note that this effectively is max-width and max-height.
|
||||||
|
size: Annotated[tuple[float, float], IOAttrs('i')]
|
||||||
|
scale: Annotated[float, IOAttrs('s', store_default=False)] = 1.0
|
||||||
|
h_align: Annotated[HAlign, IOAttrs('ha', store_default=False)] = (
|
||||||
|
HAlign.CENTER
|
||||||
|
)
|
||||||
|
v_align: Annotated[VAlign, IOAttrs('va', store_default=False)] = (
|
||||||
|
VAlign.CENTER
|
||||||
|
)
|
||||||
|
color: Annotated[
|
||||||
|
tuple[float, float, float, float] | None,
|
||||||
|
IOAttrs('c', store_default=False),
|
||||||
|
] = None
|
||||||
|
flatness: Annotated[float | None, IOAttrs('f', store_default=False)] = None
|
||||||
|
shadow: Annotated[float | None, IOAttrs('sh', store_default=False)] = None
|
||||||
|
|
||||||
|
is_lstr: Annotated[bool, IOAttrs('l', store_default=False)] = False
|
||||||
|
|
||||||
|
highlight: Annotated[bool, IOAttrs('h', store_default=False)] = True
|
||||||
|
depth_range: Annotated[tuple[float, float] | None, IOAttrs('z')] = None
|
||||||
|
|
||||||
|
#: Show max-width/height bounds; useful during development.
|
||||||
|
debug: Annotated[bool, IOAttrs('d', store_default=False)] = False
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DecorationTypeID:
|
||||||
|
return DecorationTypeID.TEXT
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Image(Decoration):
|
||||||
|
"""Image decoration."""
|
||||||
|
|
||||||
|
texture: Annotated[str, IOAttrs('t')]
|
||||||
|
position: Annotated[tuple[float, float], IOAttrs('p')]
|
||||||
|
size: Annotated[tuple[float, float], IOAttrs('s')]
|
||||||
|
color: Annotated[
|
||||||
|
tuple[float, float, float, float] | None,
|
||||||
|
IOAttrs('c', store_default=False),
|
||||||
|
] = None
|
||||||
|
h_align: Annotated[HAlign, IOAttrs('ha', store_default=False)] = (
|
||||||
|
HAlign.CENTER
|
||||||
|
)
|
||||||
|
v_align: Annotated[VAlign, IOAttrs('va', store_default=False)] = (
|
||||||
|
VAlign.CENTER
|
||||||
|
)
|
||||||
|
tint_texture: Annotated[str | None, IOAttrs('tt', store_default=False)] = (
|
||||||
|
None
|
||||||
|
)
|
||||||
|
tint_color: Annotated[
|
||||||
|
tuple[float, float, float] | None, IOAttrs('tc1', store_default=False)
|
||||||
|
] = None
|
||||||
|
tint2_color: Annotated[
|
||||||
|
tuple[float, float, float] | None, IOAttrs('tc2', store_default=False)
|
||||||
|
] = None
|
||||||
|
mask_texture: Annotated[str | None, IOAttrs('mt', store_default=False)] = (
|
||||||
|
None
|
||||||
|
)
|
||||||
|
mesh_opaque: Annotated[str | None, IOAttrs('mo', store_default=False)] = (
|
||||||
|
None
|
||||||
|
)
|
||||||
|
mesh_transparent: Annotated[
|
||||||
|
str | None, IOAttrs('mn', store_default=False)
|
||||||
|
] = None
|
||||||
|
highlight: Annotated[bool, IOAttrs('h', store_default=False)] = True
|
||||||
|
depth_range: Annotated[tuple[float, float] | None, IOAttrs('z')] = None
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DecorationTypeID:
|
||||||
|
return DecorationTypeID.IMAGE
|
||||||
|
|
||||||
|
|
||||||
|
class DisplayItemStyle(Enum):
|
||||||
|
"""Styles a display-item can be drawn in."""
|
||||||
|
|
||||||
|
#: Shows graphics and/or text fully conveying what the item is. Fits
|
||||||
|
#: in to a 4:3 box and works best with large-ish displays.
|
||||||
|
FULL = 'f'
|
||||||
|
|
||||||
|
#: Graphics and/or text fully conveying what the item is, but
|
||||||
|
#: condensed to fit in a 2:1 box displayed at small sizes.
|
||||||
|
COMPACT = 'c'
|
||||||
|
|
||||||
|
#: A graphics-only representation of the item (though text may be
|
||||||
|
#: used in fallback cases). Does not fully convey what the item is,
|
||||||
|
#: but instead is intended to be used alongside the item's textual
|
||||||
|
#: description. For example, some number of coins may simply display
|
||||||
|
#: a coin graphic here without the number. Draws in a 1:1 box and
|
||||||
|
#: works for large or small display.
|
||||||
|
ICON = 'i'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class DisplayItem(Decoration):
|
||||||
|
"""DisplayItem decoration."""
|
||||||
|
|
||||||
|
wrapper: Annotated[ditm.Wrapper, IOAttrs('w')]
|
||||||
|
position: Annotated[tuple[float, float], IOAttrs('p')]
|
||||||
|
size: Annotated[tuple[float, float], IOAttrs('s')]
|
||||||
|
style: Annotated[DisplayItemStyle, IOAttrs('t', store_default=False)] = (
|
||||||
|
DisplayItemStyle.FULL
|
||||||
|
)
|
||||||
|
text_color: Annotated[
|
||||||
|
tuple[float, float, float] | None, IOAttrs('c', store_default=False)
|
||||||
|
] = None
|
||||||
|
highlight: Annotated[bool, IOAttrs('h', store_default=False)] = True
|
||||||
|
depth_range: Annotated[tuple[float, float] | None, IOAttrs('z')] = None
|
||||||
|
debug: Annotated[bool, IOAttrs('d', store_default=False)] = False
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DecorationTypeID:
|
||||||
|
return DecorationTypeID.DISPLAY_ITEM
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonStyle(Enum):
|
||||||
|
"""Styles a button can be."""
|
||||||
|
|
||||||
|
SQUARE = 'q'
|
||||||
|
TAB = 't'
|
||||||
|
SMALL = 's'
|
||||||
|
MEDIUM = 'm'
|
||||||
|
LARGE = 'l'
|
||||||
|
LARGER = 'xl'
|
||||||
|
BACK = 'b'
|
||||||
|
BACK_SMALL = 'bs'
|
||||||
|
SQUARE_WIDE = 'w'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Button:
|
||||||
|
"""A button in our doc-ui.
|
||||||
|
|
||||||
|
Note that size, padding, and all decorations are scaled consistently
|
||||||
|
with 'scale'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#: Note that doc-ui accepts only raw :class:`str` values for text;
|
||||||
|
#: use :meth:`babase.Lstr.evaluate()` or whatnot for multi-language
|
||||||
|
#: support.
|
||||||
|
label: Annotated[str | None, IOAttrs('l', store_default=False)] = None
|
||||||
|
|
||||||
|
action: Annotated[Action | None, IOAttrs('a', store_default=False)] = None
|
||||||
|
|
||||||
|
size: Annotated[
|
||||||
|
tuple[float, float] | None, IOAttrs('sz', store_default=False)
|
||||||
|
] = None
|
||||||
|
color: Annotated[
|
||||||
|
tuple[float, float, float, float] | None,
|
||||||
|
IOAttrs('cl', store_default=False),
|
||||||
|
] = None
|
||||||
|
label_color: Annotated[
|
||||||
|
tuple[float, float, float, float] | None,
|
||||||
|
IOAttrs('lc', store_default=False),
|
||||||
|
] = None
|
||||||
|
label_flatness: Annotated[
|
||||||
|
float | None, IOAttrs('lf', store_default=False)
|
||||||
|
] = None
|
||||||
|
label_scale: Annotated[float | None, IOAttrs('ls', store_default=False)] = (
|
||||||
|
None
|
||||||
|
)
|
||||||
|
label_is_lstr: Annotated[bool, IOAttrs('ll', store_default=False)] = False
|
||||||
|
texture: Annotated[str | None, IOAttrs('tex', store_default=False)] = None
|
||||||
|
scale: Annotated[float, IOAttrs('sc', store_default=False)] = 1.0
|
||||||
|
padding_left: Annotated[float, IOAttrs('pl', store_default=False)] = 0.0
|
||||||
|
padding_top: Annotated[float, IOAttrs('pt', store_default=False)] = 0.0
|
||||||
|
padding_right: Annotated[float, IOAttrs('pr', store_default=False)] = 0.0
|
||||||
|
padding_bottom: Annotated[float, IOAttrs('pb', store_default=False)] = 0.0
|
||||||
|
decorations: Annotated[
|
||||||
|
list[Decoration] | None, IOAttrs('c', store_default=False)
|
||||||
|
] = None
|
||||||
|
style: Annotated[ButtonStyle, IOAttrs('y', store_default=False)] = (
|
||||||
|
ButtonStyle.SQUARE
|
||||||
|
)
|
||||||
|
default: Annotated[bool, IOAttrs('df', store_default=False)] = False
|
||||||
|
selected: Annotated[bool, IOAttrs('sel', store_default=False)] = False
|
||||||
|
|
||||||
|
icon: Annotated[str | None, IOAttrs('icn', store_default=False)] = None
|
||||||
|
icon_scale: Annotated[float | None, IOAttrs('is', store_default=False)] = (
|
||||||
|
None
|
||||||
|
)
|
||||||
|
icon_color: Annotated[
|
||||||
|
tuple[float, float, float, float] | None,
|
||||||
|
IOAttrs('ic', store_default=False),
|
||||||
|
] = None
|
||||||
|
depth_range: Annotated[
|
||||||
|
tuple[float, float] | None, IOAttrs('z', store_default=None)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
#: Custom widget id. Will be prefixed with window id, but must be
|
||||||
|
#: unique within the window.
|
||||||
|
widget_id: Annotated[str | None, IOAttrs('i', store_default=False)] = None
|
||||||
|
|
||||||
|
#: Draw bounds of the button.
|
||||||
|
debug: Annotated[bool, IOAttrs('d', store_default=False)] = False
|
||||||
|
|
||||||
|
|
||||||
|
class RowTypeID(Enum):
|
||||||
|
"""Type ID for each of our subclasses."""
|
||||||
|
|
||||||
|
BUTTON_ROW = 'b'
|
||||||
|
UNKNOWN = 'u'
|
||||||
|
|
||||||
|
|
||||||
|
class Row(IOMultiType[RowTypeID]):
|
||||||
|
"""Top level class for our multitype."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> RowTypeID:
|
||||||
|
# Require child classes to supply this themselves. If we did a
|
||||||
|
# full type registry/lookup here it would require us to import
|
||||||
|
# everything and would prevent lazy loading.
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type(cls, type_id: RowTypeID) -> type[Row]:
|
||||||
|
"""Return the subclass for each of our type-ids."""
|
||||||
|
# pylint: disable=cyclic-import
|
||||||
|
|
||||||
|
t = RowTypeID
|
||||||
|
if type_id is t.UNKNOWN:
|
||||||
|
return UnknownRow
|
||||||
|
if type_id is t.BUTTON_ROW:
|
||||||
|
return ButtonRow
|
||||||
|
|
||||||
|
# Important to make sure we provide all types.
|
||||||
|
assert_never(type_id)
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_unknown_type_fallback(cls) -> Row:
|
||||||
|
# If we encounter some future type we don't know anything about,
|
||||||
|
# drop in a placeholder.
|
||||||
|
return UnknownRow()
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id_storage_name(cls) -> str:
|
||||||
|
return '_t'
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class UnknownRow(Row):
|
||||||
|
"""A row type we don't have."""
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> RowTypeID:
|
||||||
|
return RowTypeID.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class ButtonRow(Row):
|
||||||
|
"""A row consisting of buttons."""
|
||||||
|
|
||||||
|
buttons: Annotated[list[Button], IOAttrs('b')]
|
||||||
|
|
||||||
|
header_height: Annotated[float, IOAttrs('h', store_default=False)] = 0.0
|
||||||
|
header_scale: Annotated[float, IOAttrs('hs', store_default=False)] = 1.0
|
||||||
|
header_decorations_left: Annotated[
|
||||||
|
list[Decoration] | None, IOAttrs('hdl', store_default=False)
|
||||||
|
] = None
|
||||||
|
header_decorations_center: Annotated[
|
||||||
|
list[Decoration] | None, IOAttrs('hdc', store_default=False)
|
||||||
|
] = None
|
||||||
|
header_decorations_right: Annotated[
|
||||||
|
list[Decoration] | None, IOAttrs('hdr', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
#: Note that doc-ui accepts only raw :class:`str` values for text;
|
||||||
|
#: use :meth:`babase.Lstr.evaluate()` or whatnot for multi-language
|
||||||
|
#: support.
|
||||||
|
title: Annotated[str | None, IOAttrs('t', store_default=False)] = None
|
||||||
|
title_color: Annotated[
|
||||||
|
tuple[float, float, float, float] | None,
|
||||||
|
IOAttrs('tc', store_default=False),
|
||||||
|
] = None
|
||||||
|
title_flatness: Annotated[
|
||||||
|
float | None, IOAttrs('tf', store_default=False)
|
||||||
|
] = None
|
||||||
|
title_shadow: Annotated[
|
||||||
|
float | None, IOAttrs('ts', store_default=False)
|
||||||
|
] = None
|
||||||
|
title_is_lstr: Annotated[bool, IOAttrs('tl', store_default=False)] = False
|
||||||
|
subtitle: Annotated[str | None, IOAttrs('s', store_default=False)] = None
|
||||||
|
subtitle_color: Annotated[
|
||||||
|
tuple[float, float, float, float] | None,
|
||||||
|
IOAttrs('sc', store_default=False),
|
||||||
|
] = None
|
||||||
|
subtitle_flatness: Annotated[
|
||||||
|
float | None, IOAttrs('sf', store_default=False)
|
||||||
|
] = None
|
||||||
|
subtitle_shadow: Annotated[
|
||||||
|
float | None, IOAttrs('ss', store_default=False)
|
||||||
|
] = None
|
||||||
|
subtitle_is_lstr: Annotated[bool, IOAttrs('sl', store_default=False)] = (
|
||||||
|
False
|
||||||
|
)
|
||||||
|
|
||||||
|
#: Spacing between all buttons in the row.
|
||||||
|
button_spacing: Annotated[float, IOAttrs('bs', store_default=False)] = 15.0
|
||||||
|
|
||||||
|
#: Padding on the left of the row's horizonally-scrollable area.
|
||||||
|
padding_left: Annotated[float, IOAttrs('pl', store_default=False)] = 10.0
|
||||||
|
#: Padding on the right of the row's horizonally-scrollable area.
|
||||||
|
padding_right: Annotated[float, IOAttrs('pr', store_default=False)] = 10.0
|
||||||
|
#: Padding on the top of the row's horizonally-scrollable area.
|
||||||
|
padding_top: Annotated[float, IOAttrs('pt', store_default=False)] = 10.0
|
||||||
|
#: Padding on the bottom of the row's horizonally-scrollable area.
|
||||||
|
padding_bottom: Annotated[float, IOAttrs('pb', store_default=False)] = 10.0
|
||||||
|
|
||||||
|
#: Extra space above the row's horizontally-scrollable area.
|
||||||
|
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
|
||||||
|
|
||||||
|
#: Extra space below the row's horizontally-scrollable area.
|
||||||
|
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
|
||||||
|
|
||||||
|
center_content: Annotated[bool, IOAttrs('c', store_default=False)] = False
|
||||||
|
center_title: Annotated[bool, IOAttrs('ct', store_default=False)] = False
|
||||||
|
|
||||||
|
#: If things disappear when scrolling left/right, turn this up.
|
||||||
|
simple_culling_h: Annotated[float, IOAttrs('sch', store_default=False)] = (
|
||||||
|
100.0
|
||||||
|
)
|
||||||
|
|
||||||
|
#: Draw bounds of the overall row and individual button columns
|
||||||
|
#: (including padding). The UI will scroll to keep these areas
|
||||||
|
#: visible in their entirety when changing selection via directional
|
||||||
|
#: controls, so try to make sure all decorations for a button are
|
||||||
|
#: within these bounds.
|
||||||
|
debug: Annotated[bool, IOAttrs('d', store_default=False)] = False
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> RowTypeID:
|
||||||
|
return RowTypeID.BUTTON_ROW
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Page:
|
||||||
|
"""Doc-UI page version 1."""
|
||||||
|
|
||||||
|
#: Note that doc-ui accepts only raw :class:`str` values for text;
|
||||||
|
#: use :meth:`babase.Lstr.evaluate()` or whatnot for multi-language
|
||||||
|
#: support.
|
||||||
|
title: Annotated[str, IOAttrs('t')]
|
||||||
|
rows: Annotated[list[Row], IOAttrs('r')]
|
||||||
|
|
||||||
|
#: If True, content smaller than the available height will be
|
||||||
|
#: centered vertically. This can look natural for certain types of
|
||||||
|
#: content such as confirmation dialogs.
|
||||||
|
center_vertically: Annotated[bool, IOAttrs('cv', store_default=False)] = (
|
||||||
|
False
|
||||||
|
)
|
||||||
|
|
||||||
|
row_spacing: Annotated[float, IOAttrs('s', store_default=False)] = 10.0
|
||||||
|
|
||||||
|
#: If things disappear when scrolling up and down, turn this up.
|
||||||
|
simple_culling_v: Annotated[float, IOAttrs('scv', store_default=False)] = (
|
||||||
|
100.0
|
||||||
|
)
|
||||||
|
|
||||||
|
#: Whether the title is a json dict representing an Lstr. Generally
|
||||||
|
#: doc-ui translation should be handled server-side, but this can
|
||||||
|
#: allow client-side translation.
|
||||||
|
title_is_lstr: Annotated[bool, IOAttrs('tl', store_default=False)] = False
|
||||||
|
|
||||||
|
padding_bottom: Annotated[float, IOAttrs('pb', store_default=False)] = 0.0
|
||||||
|
padding_left: Annotated[float, IOAttrs('pl', store_default=False)] = 0.0
|
||||||
|
padding_top: Annotated[float, IOAttrs('pt', store_default=False)] = 0.0
|
||||||
|
padding_right: Annotated[float, IOAttrs('pr', store_default=False)] = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseStatus(Enum):
|
||||||
|
"""The overall result of a request."""
|
||||||
|
|
||||||
|
SUCCESS = 0
|
||||||
|
|
||||||
|
#: Something went wrong. That's all we know.
|
||||||
|
UNKNOWN_ERROR = 1
|
||||||
|
|
||||||
|
#: Something went wrong talking to the server. A 'Retry' button may
|
||||||
|
#: be appropriate to show here (for GET requests at least).
|
||||||
|
COMMUNICATION_ERROR = 2
|
||||||
|
|
||||||
|
#: This requires the user to be signed in, and they aint.
|
||||||
|
NOT_SIGNED_IN_ERROR = 3
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class Response(DocUIResponse):
|
||||||
|
"""Full docui response."""
|
||||||
|
|
||||||
|
page: Annotated[Page, IOAttrs('p')]
|
||||||
|
status: Annotated[ResponseStatus, IOAttrs('s', store_default=False)] = (
|
||||||
|
ResponseStatus.SUCCESS
|
||||||
|
)
|
||||||
|
|
||||||
|
#: Effects to run on the client when this response is initially
|
||||||
|
#: received. Note that these effects will not re-run if the page is
|
||||||
|
#: automatically refreshed later (due to window resizing, back
|
||||||
|
#: navigation, etc).
|
||||||
|
#:
|
||||||
|
#: :meta private:
|
||||||
|
client_effects: Annotated[
|
||||||
|
list[clfx.Effect], IOAttrs('fx', store_default=False)
|
||||||
|
] = field(default_factory=list)
|
||||||
|
|
||||||
|
#: Local action to run after this response is initially received.
|
||||||
|
#: Will be handled by
|
||||||
|
#: :meth:`bauiv1lib.docui.DocUIController.local_action()`. Note that
|
||||||
|
#: these actions will not re-run if the page is automatically
|
||||||
|
#: refreshed later (due to window resizing, back navigation, etc).
|
||||||
|
local_action: Annotated[str | None, IOAttrs('a', store_default=False)] = (
|
||||||
|
None
|
||||||
|
)
|
||||||
|
local_action_args: Annotated[
|
||||||
|
dict | None, IOAttrs('aa', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
#: New overall action to have the client schedule after this
|
||||||
|
#: response is received. Useful for redirecting to other pages or
|
||||||
|
#: closing the doc-ui window.
|
||||||
|
timed_action: Annotated[
|
||||||
|
Action | None, IOAttrs('ta', store_default=False)
|
||||||
|
] = None
|
||||||
|
timed_action_delay: Annotated[
|
||||||
|
float, IOAttrs('tad', store_default=False)
|
||||||
|
] = 0.0
|
||||||
|
|
||||||
|
#: If provided, error on builds older than this (can be used to gate
|
||||||
|
#: functionality without bumping entire docui version).
|
||||||
|
minimum_engine_build: Annotated[
|
||||||
|
int | None, IOAttrs('b', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
#: The client maintains some persistent state (such as widget
|
||||||
|
#: selection) for all pages viewed. The default index for these
|
||||||
|
#: states is the path of the request. If a server returns a
|
||||||
|
#: significant variety of responses for a single path, however,
|
||||||
|
#: (based on args, etc) then it may make sense for the server to
|
||||||
|
#: provide explicit state ids for those different variations.
|
||||||
|
shared_state_id: Annotated[
|
||||||
|
str | None, IOAttrs('t', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> DocUIResponseTypeID:
|
||||||
|
return DocUIResponseTypeID.V1
|
||||||
30
dist/ba_data/python/bacommon/locale.py
vendored
30
dist/ba_data/python/bacommon/locale.py
vendored
|
|
@ -71,6 +71,7 @@ class Locale(Enum):
|
||||||
VENETIAN = 'venetn'
|
VENETIAN = 'venetn'
|
||||||
VIETNAMESE = 'viet'
|
VIETNAMESE = 'viet'
|
||||||
KAZAKH = 'kazk'
|
KAZAKH = 'kazk'
|
||||||
|
JAPANESE = 'jpn'
|
||||||
|
|
||||||
# Note: We use if-statement chains here so we can use assert_never()
|
# Note: We use if-statement chains here so we can use assert_never()
|
||||||
# to ensure we cover all existing values. But we cache lookups so
|
# to ensure we cover all existing values. But we cache lookups so
|
||||||
|
|
@ -87,7 +88,7 @@ class Locale(Enum):
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
# pylint: disable=too-many-return-statements
|
# pylint: disable=too-many-return-statements
|
||||||
|
|
||||||
cls = type(self)
|
cls = Locale
|
||||||
|
|
||||||
if self is cls.ENGLISH:
|
if self is cls.ENGLISH:
|
||||||
return 'English'
|
return 'English'
|
||||||
|
|
@ -175,6 +176,8 @@ class Locale(Enum):
|
||||||
return 'Vietnamese'
|
return 'Vietnamese'
|
||||||
if self is cls.KAZAKH:
|
if self is cls.KAZAKH:
|
||||||
return 'Kazakh'
|
return 'Kazakh'
|
||||||
|
if self is cls.JAPANESE:
|
||||||
|
return 'Japanese'
|
||||||
|
|
||||||
# Make sure we've covered all cases.
|
# Make sure we've covered all cases.
|
||||||
assert_never(self)
|
assert_never(self)
|
||||||
|
|
@ -206,7 +209,7 @@ class Locale(Enum):
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
# pylint: disable=too-many-return-statements
|
# pylint: disable=too-many-return-statements
|
||||||
|
|
||||||
cls = type(self)
|
cls = Locale
|
||||||
|
|
||||||
if self is cls.ENGLISH:
|
if self is cls.ENGLISH:
|
||||||
return 'English'
|
return 'English'
|
||||||
|
|
@ -296,6 +299,8 @@ class Locale(Enum):
|
||||||
return 'Vietnamese'
|
return 'Vietnamese'
|
||||||
if self is cls.KAZAKH:
|
if self is cls.KAZAKH:
|
||||||
return 'Kazakh'
|
return 'Kazakh'
|
||||||
|
if self is cls.JAPANESE:
|
||||||
|
return 'Japanese'
|
||||||
|
|
||||||
# Make sure we've covered all cases.
|
# Make sure we've covered all cases.
|
||||||
assert_never(self)
|
assert_never(self)
|
||||||
|
|
@ -306,7 +311,7 @@ class Locale(Enum):
|
||||||
# pylint: disable=too-many-return-statements
|
# pylint: disable=too-many-return-statements
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
|
|
||||||
cls = type(self)
|
cls = Locale
|
||||||
R = LocaleResolved
|
R = LocaleResolved
|
||||||
|
|
||||||
if self is cls.ENGLISH:
|
if self is cls.ENGLISH:
|
||||||
|
|
@ -389,6 +394,8 @@ class Locale(Enum):
|
||||||
return R.VIETNAMESE
|
return R.VIETNAMESE
|
||||||
if self is cls.KAZAKH:
|
if self is cls.KAZAKH:
|
||||||
return R.KAZAKH
|
return R.KAZAKH
|
||||||
|
if self is cls.JAPANESE:
|
||||||
|
return R.JAPANESE
|
||||||
|
|
||||||
# Make sure we're covering all cases.
|
# Make sure we're covering all cases.
|
||||||
assert_never(self)
|
assert_never(self)
|
||||||
|
|
@ -444,6 +451,7 @@ class LocaleResolved(Enum):
|
||||||
VENETIAN = 'venetn'
|
VENETIAN = 'venetn'
|
||||||
VIETNAMESE = 'viet'
|
VIETNAMESE = 'viet'
|
||||||
KAZAKH = 'kazk'
|
KAZAKH = 'kazk'
|
||||||
|
JAPANESE = 'jpn'
|
||||||
|
|
||||||
# Note: We use if-statement chains here so we can use assert_never()
|
# Note: We use if-statement chains here so we can use assert_never()
|
||||||
# to ensure we cover all existing values. But we cache lookups so
|
# to ensure we cover all existing values. But we cache lookups so
|
||||||
|
|
@ -464,7 +472,7 @@ class LocaleResolved(Enum):
|
||||||
# pylint: disable=too-many-return-statements
|
# pylint: disable=too-many-return-statements
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
|
|
||||||
cls = type(self)
|
cls = LocaleResolved
|
||||||
|
|
||||||
if self is cls.ENGLISH:
|
if self is cls.ENGLISH:
|
||||||
return Locale.ENGLISH
|
return Locale.ENGLISH
|
||||||
|
|
@ -546,6 +554,8 @@ class LocaleResolved(Enum):
|
||||||
return Locale.VIETNAMESE
|
return Locale.VIETNAMESE
|
||||||
if self is cls.KAZAKH:
|
if self is cls.KAZAKH:
|
||||||
return Locale.KAZAKH
|
return Locale.KAZAKH
|
||||||
|
if self is cls.JAPANESE:
|
||||||
|
return Locale.JAPANESE
|
||||||
|
|
||||||
# Make sure we're covering all cases.
|
# Make sure we're covering all cases.
|
||||||
assert_never(self)
|
assert_never(self)
|
||||||
|
|
@ -561,7 +571,7 @@ class LocaleResolved(Enum):
|
||||||
"""
|
"""
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
# pylint: disable=too-many-statements
|
# pylint: disable=too-many-statements
|
||||||
cls = type(self)
|
cls = LocaleResolved
|
||||||
|
|
||||||
val: str | None = None
|
val: str | None = None
|
||||||
|
|
||||||
|
|
@ -647,6 +657,8 @@ class LocaleResolved(Enum):
|
||||||
val = 'vi'
|
val = 'vi'
|
||||||
elif self is cls.KAZAKH:
|
elif self is cls.KAZAKH:
|
||||||
val = 'kk'
|
val = 'kk'
|
||||||
|
elif self is cls.JAPANESE:
|
||||||
|
val = 'ja'
|
||||||
else:
|
else:
|
||||||
# Make sure we cover all cases.
|
# Make sure we cover all cases.
|
||||||
assert_never(self)
|
assert_never(self)
|
||||||
|
|
@ -668,9 +680,9 @@ class LocaleResolved(Enum):
|
||||||
|
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
@lru_cache(maxsize=128)
|
@lru_cache(maxsize=128)
|
||||||
def from_tag(cls, tag: str) -> LocaleResolved:
|
def from_tag(tag: str) -> LocaleResolved:
|
||||||
"""Return a locale for a given string tag.
|
"""Return a locale for a given string tag.
|
||||||
|
|
||||||
Tags can be provided in BCP 47 form ('en-US') or POSIX locale
|
Tags can be provided in BCP 47 form ('en-US') or POSIX locale
|
||||||
|
|
@ -680,6 +692,8 @@ class LocaleResolved(Enum):
|
||||||
# pylint: disable=too-many-statements
|
# pylint: disable=too-many-statements
|
||||||
# pylint: disable=too-many-return-statements
|
# pylint: disable=too-many-return-statements
|
||||||
|
|
||||||
|
cls = LocaleResolved
|
||||||
|
|
||||||
# POSIX locale strings can contain a dot followed by an
|
# POSIX locale strings can contain a dot followed by an
|
||||||
# encoding. Strip that off.
|
# encoding. Strip that off.
|
||||||
tag2 = tag.split('.')[0]
|
tag2 = tag.split('.')[0]
|
||||||
|
|
@ -838,6 +852,8 @@ class LocaleResolved(Enum):
|
||||||
return cls.VIETNAMESE
|
return cls.VIETNAMESE
|
||||||
if lang == 'kk':
|
if lang == 'kk':
|
||||||
return cls.KAZAKH
|
return cls.KAZAKH
|
||||||
|
if lang == 'ja':
|
||||||
|
return cls.JAPANESE
|
||||||
|
|
||||||
# Make noise if we come across something unexpected so we can
|
# Make noise if we come across something unexpected so we can
|
||||||
# add it.
|
# add it.
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,11 @@ class LoggerControlConfig:
|
||||||
for logname in existinglognames:
|
for logname in existinglognames:
|
||||||
logger = logging.getLogger(logname)
|
logger = logging.getLogger(logname)
|
||||||
if logger.getEffectiveLevel() != self.get_effective_level(logname):
|
if logger.getEffectiveLevel() != self.get_effective_level(logname):
|
||||||
|
|
||||||
|
# Exceptions for ones that I don't care to look into.
|
||||||
|
if logname in {'pyasn1'}:
|
||||||
|
continue
|
||||||
|
|
||||||
logging.error(
|
logging.error(
|
||||||
'loggercontrol effective-level sanity check failed;'
|
'loggercontrol effective-level sanity check failed;'
|
||||||
' expected logger %s to have effective level %s'
|
' expected logger %s to have effective level %s'
|
||||||
|
|
|
||||||
2
dist/ba_data/python/bacommon/logging.py
vendored
2
dist/ba_data/python/bacommon/logging.py
vendored
|
|
@ -48,7 +48,7 @@ class ClientLoggerName(Enum):
|
||||||
"""Return a short description for the logger."""
|
"""Return a short description for the logger."""
|
||||||
# pylint: disable=too-many-return-statements
|
# pylint: disable=too-many-return-statements
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
cls = type(self)
|
cls = ClientLoggerName
|
||||||
if self is cls.BA:
|
if self is cls.BA:
|
||||||
return 'top level Ballistica logger - use to adjust everything'
|
return 'top level Ballistica logger - use to adjust everything'
|
||||||
if self is cls.ENV:
|
if self is cls.ENV:
|
||||||
|
|
|
||||||
4
dist/ba_data/python/bacommon/login.py
vendored
4
dist/ba_data/python/bacommon/login.py
vendored
|
|
@ -32,7 +32,7 @@ class LoginType(Enum):
|
||||||
@property
|
@property
|
||||||
def displayname(self) -> str:
|
def displayname(self) -> str:
|
||||||
"""A human readable name for this value."""
|
"""A human readable name for this value."""
|
||||||
cls = type(self)
|
cls = LoginType
|
||||||
match self:
|
match self:
|
||||||
case cls.EMAIL:
|
case cls.EMAIL:
|
||||||
return 'Email/Password'
|
return 'Email/Password'
|
||||||
|
|
@ -44,7 +44,7 @@ class LoginType(Enum):
|
||||||
@property
|
@property
|
||||||
def displaynameshort(self) -> str:
|
def displaynameshort(self) -> str:
|
||||||
"""A short human readable name for this value."""
|
"""A short human readable name for this value."""
|
||||||
cls = type(self)
|
cls = LoginType
|
||||||
match self:
|
match self:
|
||||||
case cls.EMAIL:
|
case cls.EMAIL:
|
||||||
return 'Email'
|
return 'Email'
|
||||||
|
|
|
||||||
8
dist/ba_data/python/bacommon/net.py
vendored
8
dist/ba_data/python/bacommon/net.py
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Network related data and functionality."""
|
"""Network related data and functionality.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
8
dist/ba_data/python/bacommon/securedata.py
vendored
8
dist/ba_data/python/bacommon/securedata.py
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to verifying ballistica server generated data."""
|
"""Functionality related to verifying server generated data.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to the server manager script."""
|
"""Functionality related to the server manager script."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
|
||||||
121
dist/ba_data/python/bacommon/text.py
vendored
Normal file
121
dist/ba_data/python/bacommon/text.py
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Text related bits."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SpecialChar(Enum):
|
||||||
|
"""Custom unicode characters the engine can display.
|
||||||
|
|
||||||
|
Keep this in sync with babase._mgen.enums.SpecialChar.
|
||||||
|
"""
|
||||||
|
|
||||||
|
LEFT_ARROW = '\ue001'
|
||||||
|
RIGHT_ARROW = '\ue002'
|
||||||
|
UP_ARROW = '\ue003'
|
||||||
|
DOWN_ARROW = '\ue004'
|
||||||
|
LEFT_BUTTON = '\ue005'
|
||||||
|
TOP_BUTTON = '\ue006'
|
||||||
|
RIGHT_BUTTON = '\ue007'
|
||||||
|
BOTTOM_BUTTON = '\ue008'
|
||||||
|
DELETE = '\ue009'
|
||||||
|
SHIFT = '\ue00a'
|
||||||
|
BACK = '\ue00b'
|
||||||
|
LOGO_FLAT = '\ue00c'
|
||||||
|
REWIND_BUTTON = '\ue00d'
|
||||||
|
PLAY_PAUSE_BUTTON = '\ue00e'
|
||||||
|
FAST_FORWARD_BUTTON = '\ue00f'
|
||||||
|
DPAD_CENTER_BUTTON = '\ue010'
|
||||||
|
PLAY_STATION_CROSS_BUTTON = '\ue011'
|
||||||
|
PLAY_STATION_CIRCLE_BUTTON = '\ue012'
|
||||||
|
PLAY_STATION_TRIANGLE_BUTTON = '\ue013'
|
||||||
|
PLAY_STATION_SQUARE_BUTTON = '\ue014'
|
||||||
|
PLAY_BUTTON = '\ue015'
|
||||||
|
PAUSE_BUTTON = '\ue016'
|
||||||
|
CLOSE = '\ue017'
|
||||||
|
OUYA_BUTTON_O = '\ue019'
|
||||||
|
OUYA_BUTTON_U = '\ue01a'
|
||||||
|
OUYA_BUTTON_Y = '\ue01b'
|
||||||
|
OUYA_BUTTON_A = '\ue01c'
|
||||||
|
TOKEN = '\ue01d'
|
||||||
|
LOGO = '\ue01e'
|
||||||
|
TICKET = '\ue01f'
|
||||||
|
GOOGLE_PLAY_GAMES_LOGO = '\ue020'
|
||||||
|
GAME_CENTER_LOGO = '\ue021'
|
||||||
|
DICE_BUTTON1 = '\ue022'
|
||||||
|
DICE_BUTTON2 = '\ue023'
|
||||||
|
DICE_BUTTON3 = '\ue024'
|
||||||
|
DICE_BUTTON4 = '\ue025'
|
||||||
|
GAME_CIRCLE_LOGO = '\ue026'
|
||||||
|
PARTY_ICON = '\ue027'
|
||||||
|
TEST_ACCOUNT = '\ue028'
|
||||||
|
TICKET_BACKING = '\ue029'
|
||||||
|
TROPHY1 = '\ue02a'
|
||||||
|
TROPHY2 = '\ue02b'
|
||||||
|
TROPHY3 = '\ue02c'
|
||||||
|
TROPHY0A = '\ue02d'
|
||||||
|
TROPHY0B = '\ue02e'
|
||||||
|
TROPHY4 = '\ue02f'
|
||||||
|
LOCAL_ACCOUNT = '\ue030'
|
||||||
|
EXPLODINARY_LOGO = '\ue031'
|
||||||
|
FLAG_UNITED_STATES = '\ue032'
|
||||||
|
FLAG_MEXICO = '\ue033'
|
||||||
|
FLAG_GERMANY = '\ue034'
|
||||||
|
FLAG_BRAZIL = '\ue035'
|
||||||
|
FLAG_RUSSIA = '\ue036'
|
||||||
|
FLAG_CHINA = '\ue037'
|
||||||
|
FLAG_UNITED_KINGDOM = '\ue038'
|
||||||
|
FLAG_CANADA = '\ue039'
|
||||||
|
FLAG_INDIA = '\ue03a'
|
||||||
|
FLAG_JAPAN = '\ue03b'
|
||||||
|
FLAG_FRANCE = '\ue03c'
|
||||||
|
FLAG_INDONESIA = '\ue03d'
|
||||||
|
FLAG_ITALY = '\ue03e'
|
||||||
|
FLAG_SOUTH_KOREA = '\ue03f'
|
||||||
|
FLAG_NETHERLANDS = '\ue040'
|
||||||
|
FEDORA = '\ue041'
|
||||||
|
HAL = '\ue042'
|
||||||
|
CROWN = '\ue043'
|
||||||
|
YIN_YANG = '\ue044'
|
||||||
|
EYE_BALL = '\ue045'
|
||||||
|
SKULL = '\ue046'
|
||||||
|
HEART = '\ue047'
|
||||||
|
DRAGON = '\ue048'
|
||||||
|
HELMET = '\ue049'
|
||||||
|
MUSHROOM = '\ue04a'
|
||||||
|
NINJA_STAR = '\ue04b'
|
||||||
|
VIKING_HELMET = '\ue04c'
|
||||||
|
MOON = '\ue04d'
|
||||||
|
SPIDER = '\ue04e'
|
||||||
|
FIREBALL = '\ue04f'
|
||||||
|
FLAG_UNITED_ARAB_EMIRATES = '\ue050'
|
||||||
|
FLAG_QATAR = '\ue051'
|
||||||
|
FLAG_EGYPT = '\ue052'
|
||||||
|
FLAG_KUWAIT = '\ue053'
|
||||||
|
FLAG_ALGERIA = '\ue054'
|
||||||
|
FLAG_SAUDI_ARABIA = '\ue055'
|
||||||
|
FLAG_MALAYSIA = '\ue056'
|
||||||
|
FLAG_CZECH_REPUBLIC = '\ue057'
|
||||||
|
FLAG_AUSTRALIA = '\ue058'
|
||||||
|
FLAG_SINGAPORE = '\ue059'
|
||||||
|
OCULUS_LOGO = '\ue05a'
|
||||||
|
STEAM_LOGO = '\ue05b'
|
||||||
|
NVIDIA_LOGO = '\ue05c'
|
||||||
|
FLAG_IRAN = '\ue05d'
|
||||||
|
FLAG_POLAND = '\ue05e'
|
||||||
|
FLAG_ARGENTINA = '\ue05f'
|
||||||
|
FLAG_PHILIPPINES = '\ue060'
|
||||||
|
FLAG_CHILE = '\ue061'
|
||||||
|
MIKIROG = '\ue062'
|
||||||
|
V2_LOGO = '\ue063'
|
||||||
|
SANTA_HAT = '\ue064'
|
||||||
|
POTATO = '\ue065'
|
||||||
|
PALM_TREE = '\ue066'
|
||||||
|
BOXING_GLOVE = '\ue067'
|
||||||
8
dist/ba_data/python/bacommon/transfer.py
vendored
8
dist/ba_data/python/bacommon/transfer.py
vendored
|
|
@ -1,6 +1,12 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to transferring files/data."""
|
"""Functionality related to transferring files/data.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an internal api and subject to change at any time. Do not use
|
||||||
|
it in mod code.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
#
|
#
|
||||||
"""Public types for assets-v1 workspaces.
|
"""Public types for assets-v1 workspaces.
|
||||||
|
|
||||||
These types may only be used server-side, but they are exposed here
|
While this module is currently only used server-side, its source code
|
||||||
for reference when setting workspace config data by hand or for use
|
can be useful as reference when setting workspace config data by hand or
|
||||||
in client-side workspace modification tools. There may be advanced
|
for use in client-side workspace modification tools. There may be
|
||||||
settings that are not accessible through the UI/etc.
|
advanced settings that are not accessible through the UI/etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -18,7 +18,6 @@ from typing import TYPE_CHECKING, Annotated, override, assert_never
|
||||||
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
|
||||||
from bacommon.locale import Locale
|
from bacommon.locale import Locale
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -85,8 +84,8 @@ class AssetsV1StringFileV1(AssetsV1StringFile):
|
||||||
|
|
||||||
NONE = 'none'
|
NONE = 'none'
|
||||||
TITLE = 'title'
|
TITLE = 'title'
|
||||||
INTENSE = 'intense'
|
LOUD = 'loud'
|
||||||
SUBTLE = 'subtle'
|
SOFT = 'soft'
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -121,7 +120,7 @@ class AssetsV1PathValsTypeID(Enum):
|
||||||
"""Types of vals we can store for paths."""
|
"""Types of vals we can store for paths."""
|
||||||
|
|
||||||
TEX_V1 = 'tex_v1'
|
TEX_V1 = 'tex_v1'
|
||||||
# STR_V1 = 'str_v1'
|
STR_V1 = 'str_v1'
|
||||||
|
|
||||||
|
|
||||||
class AssetsV1PathVals(IOMultiType[AssetsV1PathValsTypeID]):
|
class AssetsV1PathVals(IOMultiType[AssetsV1PathValsTypeID]):
|
||||||
|
|
@ -151,6 +150,9 @@ class AssetsV1PathVals(IOMultiType[AssetsV1PathValsTypeID]):
|
||||||
if type_id is t.TEX_V1:
|
if type_id is t.TEX_V1:
|
||||||
return AssetsV1PathValsTexV1
|
return AssetsV1PathValsTexV1
|
||||||
|
|
||||||
|
if type_id is t.STR_V1:
|
||||||
|
return AssetsV1PathValsStrV1
|
||||||
|
|
||||||
# Important to make sure we provide all types.
|
# Important to make sure we provide all types.
|
||||||
assert_never(type_id)
|
assert_never(type_id)
|
||||||
|
|
||||||
|
|
@ -176,3 +178,20 @@ class AssetsV1PathValsTexV1(AssetsV1PathVals):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_type_id(cls) -> AssetsV1PathValsTypeID:
|
def get_type_id(cls) -> AssetsV1PathValsTypeID:
|
||||||
return AssetsV1PathValsTypeID.TEX_V1
|
return AssetsV1PathValsTypeID.TEX_V1
|
||||||
|
|
||||||
|
|
||||||
|
@ioprepped
|
||||||
|
@dataclass
|
||||||
|
class AssetsV1PathValsStrV1(AssetsV1PathVals):
|
||||||
|
"""Path-specific values for an assets_v1 workspace path."""
|
||||||
|
|
||||||
|
#: Hash generated when all translations for this entry are complete.
|
||||||
|
#: Used as a fast-out for checking whether updates are needed.
|
||||||
|
up_to_date_state: Annotated[
|
||||||
|
str | None, IOAttrs('up_to_date_state', store_default=False)
|
||||||
|
] = None
|
||||||
|
|
||||||
|
@override
|
||||||
|
@classmethod
|
||||||
|
def get_type_id(cls) -> AssetsV1PathValsTypeID:
|
||||||
|
return AssetsV1PathValsTypeID.STR_V1
|
||||||
|
|
|
||||||
7
dist/ba_data/python/baenv.py
vendored
7
dist/ba_data/python/baenv.py
vendored
|
|
@ -14,6 +14,7 @@ Ballistica can be used without explicitly configuring the environment in
|
||||||
order to integrate it in arbitrary Python environments, but this may
|
order to integrate it in arbitrary Python environments, but this may
|
||||||
cause some features to be disabled or behave differently than expected.
|
cause some features to be disabled or behave differently than expected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
@ -56,8 +57,8 @@ 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 = 22584
|
TARGET_BALLISTICA_BUILD = 22712
|
||||||
TARGET_BALLISTICA_VERSION = '1.7.53'
|
TARGET_BALLISTICA_VERSION = '1.7.61'
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -95,7 +96,7 @@ class EnvConfig:
|
||||||
#: stderr into the engine so they show up on in-app consoles, etc.
|
#: stderr into the engine so they show up on in-app consoles, etc.
|
||||||
log_handler: LogHandler | None
|
log_handler: LogHandler | None
|
||||||
|
|
||||||
# Initial data from the ``config.json`` file in the config dir.
|
#: Initial data from the ``config.json`` file in the config dir.
|
||||||
initial_app_config: Any
|
initial_app_config: Any
|
||||||
|
|
||||||
#: Timestamp when we first started doing stuff.
|
#: Timestamp when we first started doing stuff.
|
||||||
|
|
|
||||||
1
dist/ba_data/python/baplus/_ads.py
vendored
1
dist/ba_data/python/baplus/_ads.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to ads."""
|
"""Functionality related to ads."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
|
||||||
11
dist/ba_data/python/baplus/_appsubsystem.py
vendored
11
dist/ba_data/python/baplus/_appsubsystem.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides plus app subsystem."""
|
"""Provides plus app subsystem."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
@ -13,7 +14,7 @@ from baplus._ads import AdsSubsystem
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Callable, Any
|
from typing import Callable, Any
|
||||||
|
|
||||||
import bacommon.bs
|
import bacommon.classic
|
||||||
from babase import AccountV2Subsystem
|
from babase import AccountV2Subsystem
|
||||||
|
|
||||||
from baplus._cloud import CloudSubsystem
|
from baplus._cloud import CloudSubsystem
|
||||||
|
|
@ -142,14 +143,6 @@ class PlusAppSubsystem(AppSubsystem):
|
||||||
""":meta private:"""
|
""":meta private:"""
|
||||||
return _baplus.get_v1_account_state_num()
|
return _baplus.get_v1_account_state_num()
|
||||||
|
|
||||||
# @staticmethod
|
|
||||||
# def get_v1_account_ticket_count() -> int:
|
|
||||||
# """Return the number of tickets for the current account.
|
|
||||||
|
|
||||||
# :meta private:
|
|
||||||
# """
|
|
||||||
# return _baplus.get_v1_account_ticket_count()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_v1_account_type() -> str:
|
def get_v1_account_type() -> str:
|
||||||
""":meta private:"""
|
""":meta private:"""
|
||||||
|
|
|
||||||
94
dist/ba_data/python/baplus/_cloud.py
vendored
94
dist/ba_data/python/baplus/_cloud.py
vendored
|
|
@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, overload
|
||||||
from efro.error import CommunicationError
|
from efro.error import CommunicationError
|
||||||
from efro.call import CallbackSet
|
from efro.call import CallbackSet
|
||||||
from efro.dataclassio import dataclass_from_dict, dataclass_to_dict
|
from efro.dataclassio import dataclass_from_dict, dataclass_to_dict
|
||||||
import bacommon.bs
|
import bacommon.classic
|
||||||
import bacommon.cloud
|
import bacommon.cloud
|
||||||
import babase
|
import babase
|
||||||
|
|
||||||
|
|
@ -19,7 +19,8 @@ if TYPE_CHECKING:
|
||||||
from typing import Callable, Any
|
from typing import Callable, Any
|
||||||
|
|
||||||
from efro.message import Message, Response, BoolResponse
|
from efro.message import Message, Response, BoolResponse
|
||||||
import bacommon.bs
|
import bacommon.classic
|
||||||
|
import bacommon.clouddialog as cdlg
|
||||||
|
|
||||||
|
|
||||||
# TODO: Should make it possible to define a protocol in bacommon.cloud and
|
# TODO: Should make it possible to define a protocol in bacommon.cloud and
|
||||||
|
|
@ -36,6 +37,8 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#: General engine config values provided by the cloud.
|
#: General engine config values provided by the cloud.
|
||||||
|
#:
|
||||||
|
#: :meta private:
|
||||||
vals: bacommon.cloud.CloudVals
|
vals: bacommon.cloud.CloudVals
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
@ -214,9 +217,9 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||||
@overload
|
@overload
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: bacommon.bs.GetClassicPurchasesMessage,
|
msg: bacommon.classic.GetClassicPurchasesMessage,
|
||||||
on_response: Callable[
|
on_response: Callable[
|
||||||
[bacommon.bs.GetClassicPurchasesResponse | Exception], None
|
[bacommon.classic.GetClassicPurchasesResponse | Exception], None
|
||||||
],
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
|
|
@ -232,61 +235,59 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||||
@overload
|
@overload
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: bacommon.bs.PrivatePartyMessage,
|
msg: bacommon.classic.PrivatePartyMessage,
|
||||||
on_response: Callable[
|
on_response: Callable[
|
||||||
[bacommon.bs.PrivatePartyResponse | Exception], None
|
[bacommon.classic.PrivatePartyResponse | Exception], None
|
||||||
],
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: bacommon.bs.InboxRequestMessage,
|
msg: bacommon.classic.InboxRequestMessage,
|
||||||
on_response: Callable[
|
on_response: Callable[
|
||||||
[bacommon.bs.InboxRequestResponse | Exception], None
|
[bacommon.classic.InboxRequestResponse | Exception], None
|
||||||
],
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: bacommon.bs.CloudDialogActionMessage,
|
msg: cdlg.ActionMessage,
|
||||||
|
on_response: Callable[[cdlg.ActionResponse | Exception], None],
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def send_message_cb(
|
||||||
|
self,
|
||||||
|
msg: bacommon.classic.ChestInfoMessage,
|
||||||
on_response: Callable[
|
on_response: Callable[
|
||||||
[bacommon.bs.CloudDialogActionResponse | Exception], None
|
[bacommon.classic.ChestInfoResponse | Exception], None
|
||||||
],
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: bacommon.bs.ChestInfoMessage,
|
msg: bacommon.cloud.ChestActionMessage,
|
||||||
on_response: Callable[
|
on_response: Callable[
|
||||||
[bacommon.bs.ChestInfoResponse | Exception], None
|
[bacommon.cloud.ChestActionResponse | Exception], None
|
||||||
],
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: bacommon.bs.ChestActionMessage,
|
msg: bacommon.classic.GlobalProfileCheckMessage,
|
||||||
on_response: Callable[
|
|
||||||
[bacommon.bs.ChestActionResponse | Exception], None
|
|
||||||
],
|
|
||||||
) -> None: ...
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def send_message_cb(
|
|
||||||
self,
|
|
||||||
msg: bacommon.bs.GlobalProfileCheckMessage,
|
|
||||||
on_response: Callable[[BoolResponse | Exception], None],
|
on_response: Callable[[BoolResponse | Exception], None],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: bacommon.bs.ScoreSubmitMessage,
|
msg: bacommon.classic.ScoreSubmitMessage,
|
||||||
on_response: Callable[
|
on_response: Callable[
|
||||||
[bacommon.bs.ScoreSubmitResponse | Exception], None
|
[bacommon.classic.ScoreSubmitResponse | Exception], None
|
||||||
],
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
|
|
@ -308,6 +309,29 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||||
],
|
],
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def send_message_cb(
|
||||||
|
self,
|
||||||
|
msg: bacommon.classic.GetClassicLeaguePresidentButtonInfoMessage,
|
||||||
|
on_response: Callable[
|
||||||
|
[
|
||||||
|
bacommon.classic.GetClassicLeaguePresidentButtonInfoResponse
|
||||||
|
| Exception
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
],
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def send_message_cb(
|
||||||
|
self,
|
||||||
|
msg: bacommon.cloud.AnalyticsEventMessage,
|
||||||
|
on_response: Callable[
|
||||||
|
[None | Exception],
|
||||||
|
None,
|
||||||
|
],
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
def send_message_cb(
|
def send_message_cb(
|
||||||
self,
|
self,
|
||||||
msg: Message,
|
msg: Message,
|
||||||
|
|
@ -339,8 +363,13 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def send_message(
|
def send_message(
|
||||||
self, msg: bacommon.bs.LegacyRequest
|
self, msg: bacommon.classic.LegacyRequest
|
||||||
) -> bacommon.bs.LegacyResponse: ...
|
) -> bacommon.classic.LegacyResponse: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def send_message(
|
||||||
|
self, msg: bacommon.cloud.FulfillDocUIRequest
|
||||||
|
) -> bacommon.cloud.FulfillDocUIResponse: ...
|
||||||
|
|
||||||
def send_message(self, msg: Message) -> Response | None:
|
def send_message(self, msg: Message) -> Response | None:
|
||||||
"""Synchronously send a message to the cloud.
|
"""Synchronously send a message to the cloud.
|
||||||
|
|
@ -353,8 +382,8 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
async def send_message_async(
|
async def send_message_async(
|
||||||
self, msg: bacommon.bs.SendInfoMessage
|
self, msg: bacommon.classic.SendInfoMessage
|
||||||
) -> bacommon.bs.SendInfoResponse: ...
|
) -> bacommon.classic.SendInfoResponse: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
async def send_message_async(
|
async def send_message_async(
|
||||||
|
|
@ -383,9 +412,14 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||||
|
|
||||||
def subscribe_classic_account_data(
|
def subscribe_classic_account_data(
|
||||||
self,
|
self,
|
||||||
updatecall: Callable[[bacommon.bs.ClassicAccountLiveData], None],
|
updatecall: Callable[
|
||||||
|
[bacommon.classic.ClassicLiveAccountClientData], None
|
||||||
|
],
|
||||||
) -> babase.CloudSubscription:
|
) -> babase.CloudSubscription:
|
||||||
"""Subscribe to classic account data."""
|
"""Subscribe to classic account data.
|
||||||
|
|
||||||
|
:meta private:
|
||||||
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'Cloud functionality is not present in this build.'
|
'Cloud functionality is not present in this build.'
|
||||||
)
|
)
|
||||||
|
|
|
||||||
1
dist/ba_data/python/baplus/_hooks.py
vendored
1
dist/ba_data/python/baplus/_hooks.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Snippets of code for use by the c++ layer."""
|
"""Snippets of code for use by the c++ layer."""
|
||||||
|
|
||||||
# (most of these are self-explanatory)
|
# (most of these are self-explanatory)
|
||||||
# pylint: disable=missing-function-docstring
|
# pylint: disable=missing-function-docstring
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
|
||||||
15
dist/ba_data/python/bascenev1/__init__.py
vendored
15
dist/ba_data/python/bascenev1/__init__.py
vendored
|
|
@ -16,7 +16,6 @@ import logging
|
||||||
# other modules; the goal is to let most simple mods rely solely on this
|
# other modules; the goal is to let most simple mods rely solely on this
|
||||||
# module to keep things simple.
|
# module to keep things simple.
|
||||||
|
|
||||||
# from efro.util import set_canonical_module_names
|
|
||||||
from babase import (
|
from babase import (
|
||||||
ActivityNotFoundError,
|
ActivityNotFoundError,
|
||||||
add_clean_frame_callback,
|
add_clean_frame_callback,
|
||||||
|
|
@ -32,6 +31,8 @@ from babase import (
|
||||||
apptimer,
|
apptimer,
|
||||||
AppTimer,
|
AppTimer,
|
||||||
Call,
|
Call,
|
||||||
|
CallPartial,
|
||||||
|
CallStrict,
|
||||||
ContextError,
|
ContextError,
|
||||||
ContextRef,
|
ContextRef,
|
||||||
displaytime,
|
displaytime,
|
||||||
|
|
@ -63,6 +64,8 @@ from babase import (
|
||||||
unlock_all_input,
|
unlock_all_input,
|
||||||
Vec3,
|
Vec3,
|
||||||
WeakCall,
|
WeakCall,
|
||||||
|
WeakCallPartial,
|
||||||
|
WeakCallStrict,
|
||||||
)
|
)
|
||||||
|
|
||||||
from _bascenev1 import (
|
from _bascenev1 import (
|
||||||
|
|
@ -275,6 +278,8 @@ __all__ = [
|
||||||
'BaseTimer',
|
'BaseTimer',
|
||||||
'BoolSetting',
|
'BoolSetting',
|
||||||
'Call',
|
'Call',
|
||||||
|
'CallPartial',
|
||||||
|
'CallStrict',
|
||||||
'cameraflash',
|
'cameraflash',
|
||||||
'camerashake',
|
'camerashake',
|
||||||
'Campaign',
|
'Campaign',
|
||||||
|
|
@ -475,15 +480,11 @@ __all__ = [
|
||||||
'unlock_all_input',
|
'unlock_all_input',
|
||||||
'Vec3',
|
'Vec3',
|
||||||
'WeakCall',
|
'WeakCall',
|
||||||
|
'WeakCallPartial',
|
||||||
|
'WeakCallStrict',
|
||||||
'WinnerGroup',
|
'WinnerGroup',
|
||||||
]
|
]
|
||||||
|
|
||||||
# We want stuff here to show up as bascenev1.Foo instead of
|
|
||||||
# bascenev1._submodule.Foo.
|
|
||||||
# UPDATE: Trying without this for now. Seems like this might cause more
|
|
||||||
# harm than good. Can flip it back on if it is missed.
|
|
||||||
# set_canonical_module_names(globals())
|
|
||||||
|
|
||||||
# Sanity check: we want to keep ballistica's dependencies and
|
# Sanity check: we want to keep ballistica's dependencies and
|
||||||
# bootstrapping order clearly defined; let's check a few particular
|
# bootstrapping order clearly defined; let's check a few particular
|
||||||
# modules to make sure they never directly or indirectly import us
|
# modules to make sure they never directly or indirectly import us
|
||||||
|
|
|
||||||
10
dist/ba_data/python/bascenev1/_activity.py
vendored
10
dist/ba_data/python/bascenev1/_activity.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Defines Activity class."""
|
"""Defines Activity class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import weakref
|
import weakref
|
||||||
|
|
@ -12,9 +13,8 @@ import _bascenev1
|
||||||
from bascenev1._dependency import DependencyComponent
|
from bascenev1._dependency import DependencyComponent
|
||||||
from bascenev1._messages import UNHANDLED
|
from bascenev1._messages import UNHANDLED
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
import bascenev1
|
import bascenev1
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -192,7 +192,7 @@ class Activity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
session = self._session()
|
session = self._session()
|
||||||
if session is not None:
|
if session is not None:
|
||||||
babase.pushcall(
|
babase.pushcall(
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
session.transitioning_out_activity_was_freed,
|
session.transitioning_out_activity_was_freed,
|
||||||
self.can_show_ad_on_death,
|
self.can_show_ad_on_death,
|
||||||
)
|
)
|
||||||
|
|
@ -286,7 +286,7 @@ class Activity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
ref = weakref.ref(self)
|
ref = weakref.ref(self)
|
||||||
self._activity_death_check_timer = babase.AppTimer(
|
self._activity_death_check_timer = babase.AppTimer(
|
||||||
5.0,
|
5.0,
|
||||||
babase.Call(self._check_activity_death, ref, [0]),
|
babase.CallStrict(self._check_activity_death, ref, [0]),
|
||||||
repeat=True,
|
repeat=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -722,7 +722,7 @@ class Activity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_activity_death(
|
def _check_activity_death(
|
||||||
cls, activity_ref: weakref.ref[Activity], counter: list[int]
|
cls, activity_ref: weakref.ref[Self], counter: list[int]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Sanity check to make sure an Activity was destroyed properly.
|
"""Sanity check to make sure an Activity was destroyed properly.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Some handy base class and special purpose Activity types."""
|
"""Some handy base class and special purpose Activity types."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
@ -14,7 +15,6 @@ from bascenev1._player import EmptyPlayer
|
||||||
from bascenev1._team import EmptyTeam
|
from bascenev1._team import EmptyTeam
|
||||||
from bascenev1._music import MusicType, setmusic
|
from bascenev1._music import MusicType, setmusic
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import bascenev1
|
import bascenev1
|
||||||
from bascenev1._lobby import JoinInfo
|
from bascenev1._lobby import JoinInfo
|
||||||
|
|
@ -54,7 +54,7 @@ class EndSessionActivity(Activity[EmptyPlayer, EmptyTeam]):
|
||||||
babase.unlock_all_input()
|
babase.unlock_all_input()
|
||||||
assert babase.app.plus is not None
|
assert babase.app.plus is not None
|
||||||
|
|
||||||
call = babase.Call(_bascenev1.new_host_session, main_menu_session)
|
call = babase.CallStrict(_bascenev1.new_host_session, main_menu_session)
|
||||||
if classic.can_show_interstitial():
|
if classic.can_show_interstitial():
|
||||||
plus.ads.call_after_ad(call)
|
plus.ads.call_after_ad(call)
|
||||||
else:
|
else:
|
||||||
|
|
@ -172,7 +172,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
|
||||||
# If we're still kicking at the end of our assign-delay, assign this
|
# If we're still kicking at the end of our assign-delay, assign this
|
||||||
# guy's input to trigger us.
|
# guy's input to trigger us.
|
||||||
_bascenev1.timer(
|
_bascenev1.timer(
|
||||||
time_till_assign, babase.WeakCall(self._safe_assign, player)
|
time_till_assign, babase.WeakCallStrict(self._safe_assign, player)
|
||||||
)
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
1
dist/ba_data/python/bascenev1/_campaign.py
vendored
1
dist/ba_data/python/bascenev1/_campaign.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to co-op campaigns."""
|
"""Functionality related to co-op campaigns."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
8
dist/ba_data/python/bascenev1/_collision.py
vendored
8
dist/ba_data/python/bascenev1/_collision.py
vendored
|
|
@ -19,7 +19,9 @@ class Collision:
|
||||||
@property
|
@property
|
||||||
def position(self) -> bascenev1.Vec3:
|
def position(self) -> bascenev1.Vec3:
|
||||||
"""The position of the current collision."""
|
"""The position of the current collision."""
|
||||||
return babase.Vec3(_bascenev1.get_collision_info('position'))
|
out = babase.Vec3(_bascenev1.get_collision_info('position'))
|
||||||
|
assert isinstance(out, babase.Vec3)
|
||||||
|
return out
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sourcenode(self) -> bascenev1.Node:
|
def sourcenode(self) -> bascenev1.Node:
|
||||||
|
|
@ -30,7 +32,7 @@ class Collision:
|
||||||
start of the collision callback).
|
start of the collision callback).
|
||||||
"""
|
"""
|
||||||
node = _bascenev1.get_collision_info('sourcenode')
|
node = _bascenev1.get_collision_info('sourcenode')
|
||||||
assert isinstance(node, (_bascenev1.Node, type(None)))
|
assert isinstance(node, _bascenev1.Node | None)
|
||||||
if not node:
|
if not node:
|
||||||
raise babase.NodeNotFoundError()
|
raise babase.NodeNotFoundError()
|
||||||
return node
|
return node
|
||||||
|
|
@ -45,7 +47,7 @@ class Collision:
|
||||||
currently-colliding node.
|
currently-colliding node.
|
||||||
"""
|
"""
|
||||||
node = _bascenev1.get_collision_info('opposingnode')
|
node = _bascenev1.get_collision_info('opposingnode')
|
||||||
assert isinstance(node, (_bascenev1.Node, type(None)))
|
assert isinstance(node, _bascenev1.Node | None)
|
||||||
if not node:
|
if not node:
|
||||||
raise babase.NodeNotFoundError()
|
raise babase.NodeNotFoundError()
|
||||||
return node
|
return node
|
||||||
|
|
|
||||||
7
dist/ba_data/python/bascenev1/_coopgame.py
vendored
7
dist/ba_data/python/bascenev1/_coopgame.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to co-op games."""
|
"""Functionality related to co-op games."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -64,11 +65,11 @@ class CoopGameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
|
|
||||||
if not arcade_or_demo:
|
if not arcade_or_demo:
|
||||||
_bascenev1.timer(
|
_bascenev1.timer(
|
||||||
3.8, babase.WeakCall(self._show_remaining_achievements)
|
3.8, babase.WeakCallStrict(self._show_remaining_achievements)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Preload achievement images in case we get some.
|
# Preload achievement images in case we get some.
|
||||||
_bascenev1.timer(2.0, babase.WeakCall(self._preload_achievements))
|
_bascenev1.timer(2.0, babase.WeakCallStrict(self._preload_achievements))
|
||||||
|
|
||||||
# FIXME: this is now redundant with activityutils.getscoreconfig();
|
# FIXME: this is now redundant with activityutils.getscoreconfig();
|
||||||
# need to kill this.
|
# need to kill this.
|
||||||
|
|
@ -232,7 +233,7 @@ class CoopGameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
"""Set up a beeping noise to play when any players are near death."""
|
"""Set up a beeping noise to play when any players are near death."""
|
||||||
self._life_warning_beep = None
|
self._life_warning_beep = None
|
||||||
self._life_warning_beep_timer = _bascenev1.Timer(
|
self._life_warning_beep_timer = _bascenev1.Timer(
|
||||||
1.0, babase.WeakCall(self._update_life_warning), repeat=True
|
1.0, babase.WeakCallStrict(self._update_life_warning), repeat=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_life_warning(self) -> None:
|
def _update_life_warning(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to coop-mode sessions."""
|
"""Functionality related to coop-mode sessions."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
@ -185,7 +186,9 @@ class CoopSession(Session):
|
||||||
def on_player_leave(self, sessionplayer: bascenev1.SessionPlayer) -> None:
|
def on_player_leave(self, sessionplayer: bascenev1.SessionPlayer) -> None:
|
||||||
super().on_player_leave(sessionplayer)
|
super().on_player_leave(sessionplayer)
|
||||||
|
|
||||||
_bascenev1.timer(2.0, babase.WeakCall(self._handle_empty_activity))
|
_bascenev1.timer(
|
||||||
|
2.0, babase.WeakCallStrict(self._handle_empty_activity)
|
||||||
|
)
|
||||||
|
|
||||||
def _handle_empty_activity(self) -> None:
|
def _handle_empty_activity(self) -> None:
|
||||||
"""Handle cases where all players have left the current activity."""
|
"""Handle cases where all players have left the current activity."""
|
||||||
|
|
@ -358,7 +361,7 @@ class CoopSession(Session):
|
||||||
{
|
{
|
||||||
'label': babase.Lstr(resource='restartText'),
|
'label': babase.Lstr(resource='restartText'),
|
||||||
'resume_on_call': False,
|
'resume_on_call': False,
|
||||||
'call': babase.WeakCall(
|
'call': babase.WeakCallPartial(
|
||||||
self._on_tournament_restart_menu_press
|
self._on_tournament_restart_menu_press
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
@ -367,7 +370,7 @@ class CoopSession(Session):
|
||||||
self._custom_menu_ui = [
|
self._custom_menu_ui = [
|
||||||
{
|
{
|
||||||
'label': babase.Lstr(resource='restartText'),
|
'label': babase.Lstr(resource='restartText'),
|
||||||
'call': babase.WeakCall(self.restart),
|
'call': babase.WeakCallStrict(self.restart),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
1
dist/ba_data/python/bascenev1/_debug.py
vendored
1
dist/ba_data/python/bascenev1/_debug.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Debugging functionality."""
|
"""Debugging functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to teams sessions."""
|
"""Functionality related to teams sessions."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from typing import TYPE_CHECKING, override
|
||||||
|
|
|
||||||
19
dist/ba_data/python/bascenev1/_gameactivity.py
vendored
19
dist/ba_data/python/bascenev1/_gameactivity.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Provides GameActivity class."""
|
"""Provides GameActivity class."""
|
||||||
|
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -405,7 +406,9 @@ class GameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
'tournamentIDs': [tournament_id],
|
'tournamentIDs': [tournament_id],
|
||||||
'source': 'in-game time remaining query',
|
'source': 'in-game time remaining query',
|
||||||
},
|
},
|
||||||
callback=babase.WeakCall(self._on_tournament_query_response),
|
callback=babase.WeakCallPartial(
|
||||||
|
self._on_tournament_query_response
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _on_tournament_query_response(
|
def _on_tournament_query_response(
|
||||||
|
|
@ -805,7 +808,7 @@ class GameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
|
|
||||||
player.customdata['respawn_timer'] = _bascenev1.Timer(
|
player.customdata['respawn_timer'] = _bascenev1.Timer(
|
||||||
respawn_time,
|
respawn_time,
|
||||||
babase.WeakCall(self.spawn_player_if_exists, player),
|
babase.WeakCallStrict(self.spawn_player_if_exists, player),
|
||||||
)
|
)
|
||||||
player.customdata['respawn_icon'] = RespawnIcon(
|
player.customdata['respawn_icon'] = RespawnIcon(
|
||||||
player, respawn_time
|
player, respawn_time
|
||||||
|
|
@ -902,7 +905,7 @@ class GameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
|
|
||||||
self._powerup_drop_timer = _bascenev1.Timer(
|
self._powerup_drop_timer = _bascenev1.Timer(
|
||||||
DEFAULT_POWERUP_INTERVAL,
|
DEFAULT_POWERUP_INTERVAL,
|
||||||
babase.WeakCall(self._standard_drop_powerups),
|
babase.WeakCallStrict(self._standard_drop_powerups),
|
||||||
repeat=True,
|
repeat=True,
|
||||||
)
|
)
|
||||||
self._standard_drop_powerups()
|
self._standard_drop_powerups()
|
||||||
|
|
@ -927,7 +930,7 @@ class GameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
points = self.map.powerup_spawn_points
|
points = self.map.powerup_spawn_points
|
||||||
for i in range(len(points)):
|
for i in range(len(points)):
|
||||||
_bascenev1.timer(
|
_bascenev1.timer(
|
||||||
i * 0.4, babase.WeakCall(self._standard_drop_powerup, i)
|
i * 0.4, babase.WeakCallStrict(self._standard_drop_powerup, i)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _setup_standard_tnt_drops(self) -> None:
|
def _setup_standard_tnt_drops(self) -> None:
|
||||||
|
|
@ -953,7 +956,9 @@ class GameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
return
|
return
|
||||||
self._standard_time_limit_time = int(duration)
|
self._standard_time_limit_time = int(duration)
|
||||||
self._standard_time_limit_timer = _bascenev1.Timer(
|
self._standard_time_limit_timer = _bascenev1.Timer(
|
||||||
1.0, babase.WeakCall(self._standard_time_limit_tick), repeat=True
|
1.0,
|
||||||
|
babase.WeakCallStrict(self._standard_time_limit_tick),
|
||||||
|
repeat=True,
|
||||||
)
|
)
|
||||||
self._standard_time_limit_text = NodeActor(
|
self._standard_time_limit_text = NodeActor(
|
||||||
_bascenev1.newnode(
|
_bascenev1.newnode(
|
||||||
|
|
@ -1043,7 +1048,9 @@ class GameActivity[PlayerT: bascenev1.Player, TeamT: bascenev1.Team](
|
||||||
# then we have to mess with contexts and whatnot since its currently
|
# then we have to mess with contexts and whatnot since its currently
|
||||||
# not available in activity contexts. :-/
|
# not available in activity contexts. :-/
|
||||||
self._tournament_time_limit_timer = _bascenev1.BaseTimer(
|
self._tournament_time_limit_timer = _bascenev1.BaseTimer(
|
||||||
1.0, babase.WeakCall(self._tournament_time_limit_tick), repeat=True
|
1.0,
|
||||||
|
babase.WeakCallStrict(self._tournament_time_limit_tick),
|
||||||
|
repeat=True,
|
||||||
)
|
)
|
||||||
self._tournament_time_limit_title_text = NodeActor(
|
self._tournament_time_limit_title_text = NodeActor(
|
||||||
_bascenev1.newnode(
|
_bascenev1.newnode(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to game results."""
|
"""Functionality related to game results."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
|
||||||
1
dist/ba_data/python/bascenev1/_hooks.py
vendored
1
dist/ba_data/python/bascenev1/_hooks.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Snippets of code for use by the c++ layer."""
|
"""Snippets of code for use by the c++ layer."""
|
||||||
|
|
||||||
# (most of these are self-explanatory)
|
# (most of these are self-explanatory)
|
||||||
# pylint: disable=missing-function-docstring
|
# pylint: disable=missing-function-docstring
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
|
||||||
10
dist/ba_data/python/bascenev1/_level.py
vendored
10
dist/ba_data/python/bascenev1/_level.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to individual levels in a campaign."""
|
"""Functionality related to individual levels in a campaign."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
@ -118,10 +119,11 @@ class Level:
|
||||||
def get_high_scores(self) -> dict:
|
def get_high_scores(self) -> dict:
|
||||||
"""Return the current high scores for this level."""
|
"""Return the current high scores for this level."""
|
||||||
config = self._get_config_dict()
|
config = self._get_config_dict()
|
||||||
high_scores_key = 'High Scores' + self.get_score_version_string()
|
high_scores_key = f'High Scores{self.get_score_version_string()}'
|
||||||
if high_scores_key not in config:
|
val = config.get(high_scores_key)
|
||||||
return {}
|
if isinstance(val, dict):
|
||||||
return copy.deepcopy(config[high_scores_key])
|
return copy.deepcopy(val)
|
||||||
|
return {}
|
||||||
|
|
||||||
def set_high_scores(self, high_scores: dict) -> None:
|
def set_high_scores(self, high_scores: dict) -> None:
|
||||||
"""Set high scores for this level."""
|
"""Set high scores for this level."""
|
||||||
|
|
|
||||||
25
dist/ba_data/python/bascenev1/_lobby.py
vendored
25
dist/ba_data/python/bascenev1/_lobby.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Implements lobby system for gathering before games, char select, etc."""
|
"""Implements lobby system for gathering before games, char select, etc."""
|
||||||
|
|
||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -118,7 +119,7 @@ class JoinInfo:
|
||||||
)
|
)
|
||||||
|
|
||||||
self._timer = _bascenev1.Timer(
|
self._timer = _bascenev1.Timer(
|
||||||
4.0, babase.WeakCall(self._update), repeat=True
|
4.0, babase.WeakCallStrict(self._update), repeat=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_for_keyboard(self, keyboard: bascenev1.InputDevice) -> None:
|
def _update_for_keyboard(self, keyboard: bascenev1.InputDevice) -> None:
|
||||||
|
|
@ -609,25 +610,29 @@ class Chooser:
|
||||||
if not ready:
|
if not ready:
|
||||||
self._sessionplayer.assigninput(
|
self._sessionplayer.assigninput(
|
||||||
babase.InputType.LEFT_PRESS,
|
babase.InputType.LEFT_PRESS,
|
||||||
babase.Call(self.handlemessage, ChangeMessage('team', -1)),
|
babase.CallStrict(
|
||||||
|
self.handlemessage, ChangeMessage('team', -1)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self._sessionplayer.assigninput(
|
self._sessionplayer.assigninput(
|
||||||
babase.InputType.RIGHT_PRESS,
|
babase.InputType.RIGHT_PRESS,
|
||||||
babase.Call(self.handlemessage, ChangeMessage('team', 1)),
|
babase.CallStrict(self.handlemessage, ChangeMessage('team', 1)),
|
||||||
)
|
)
|
||||||
self._sessionplayer.assigninput(
|
self._sessionplayer.assigninput(
|
||||||
babase.InputType.BOMB_PRESS,
|
babase.InputType.BOMB_PRESS,
|
||||||
babase.Call(self.handlemessage, ChangeMessage('character', 1)),
|
babase.CallStrict(
|
||||||
|
self.handlemessage, ChangeMessage('character', 1)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self._sessionplayer.assigninput(
|
self._sessionplayer.assigninput(
|
||||||
babase.InputType.UP_PRESS,
|
babase.InputType.UP_PRESS,
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
self.handlemessage, ChangeMessage('profileindex', -1)
|
self.handlemessage, ChangeMessage('profileindex', -1)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self._sessionplayer.assigninput(
|
self._sessionplayer.assigninput(
|
||||||
babase.InputType.DOWN_PRESS,
|
babase.InputType.DOWN_PRESS,
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
self.handlemessage, ChangeMessage('profileindex', 1)
|
self.handlemessage, ChangeMessage('profileindex', 1)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -637,7 +642,9 @@ class Chooser:
|
||||||
babase.InputType.PICK_UP_PRESS,
|
babase.InputType.PICK_UP_PRESS,
|
||||||
babase.InputType.PUNCH_PRESS,
|
babase.InputType.PUNCH_PRESS,
|
||||||
),
|
),
|
||||||
babase.Call(self.handlemessage, ChangeMessage('ready', 1)),
|
babase.CallStrict(
|
||||||
|
self.handlemessage, ChangeMessage('ready', 1)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self._ready = False
|
self._ready = False
|
||||||
self._update_text()
|
self._update_text()
|
||||||
|
|
@ -662,7 +669,9 @@ class Chooser:
|
||||||
babase.InputType.PICK_UP_PRESS,
|
babase.InputType.PICK_UP_PRESS,
|
||||||
babase.InputType.PUNCH_PRESS,
|
babase.InputType.PUNCH_PRESS,
|
||||||
),
|
),
|
||||||
babase.Call(self.handlemessage, ChangeMessage('ready', 0)),
|
babase.CallStrict(
|
||||||
|
self.handlemessage, ChangeMessage('ready', 0)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Store the last profile picked by this input for reuse.
|
# Store the last profile picked by this input for reuse.
|
||||||
|
|
|
||||||
1
dist/ba_data/python/bascenev1/_map.py
vendored
1
dist/ba_data/python/bascenev1/_map.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Map related functionality."""
|
"""Map related functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
|
||||||
2
dist/ba_data/python/bascenev1/_messages.py
vendored
2
dist/ba_data/python/bascenev1/_messages.py
vendored
|
|
@ -99,7 +99,7 @@ class PlayerDiedMessage:
|
||||||
|
|
||||||
Pass the Player type being used by the current game.
|
Pass the Player type being used by the current game.
|
||||||
"""
|
"""
|
||||||
assert isinstance(self._killerplayer, (playertype, type(None)))
|
assert isinstance(self._killerplayer, playertype | None)
|
||||||
return self._killerplayer
|
return self._killerplayer
|
||||||
|
|
||||||
def getplayer[PlayerT: bascenev1.Player](
|
def getplayer[PlayerT: bascenev1.Player](
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to teams sessions."""
|
"""Functionality related to teams sessions."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
|
||||||
1
dist/ba_data/python/bascenev1/_net.py
vendored
1
dist/ba_data/python/bascenev1/_net.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to net play."""
|
"""Functionality related to net play."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
|
||||||
5
dist/ba_data/python/bascenev1/_player.py
vendored
5
dist/ba_data/python/bascenev1/_player.py
vendored
|
|
@ -35,7 +35,8 @@ class StandLocation:
|
||||||
angle: float | None = None
|
angle: float | None = None
|
||||||
|
|
||||||
|
|
||||||
class Player[TeamT: bascenev1.Team]:
|
# class Player[TeamT: bascenev1.Team]:
|
||||||
|
class Player[TeamT]:
|
||||||
"""A player in a specific bascenev1.Activity.
|
"""A player in a specific bascenev1.Activity.
|
||||||
|
|
||||||
These correspond to bascenev1.SessionPlayer objects, but are associated
|
These correspond to bascenev1.SessionPlayer objects, but are associated
|
||||||
|
|
@ -315,5 +316,5 @@ def playercast_o[PlayerT: bascenev1.Player](
|
||||||
totype: type[PlayerT], player: bascenev1.Player | None
|
totype: type[PlayerT], player: bascenev1.Player | None
|
||||||
) -> PlayerT | None:
|
) -> PlayerT | None:
|
||||||
"""A variant of bascenev1.playercast() for optional Player values."""
|
"""A variant of bascenev1.playercast() for optional Player values."""
|
||||||
assert isinstance(player, (totype, type(None)))
|
assert isinstance(player, totype | None)
|
||||||
return player
|
return player
|
||||||
|
|
|
||||||
1
dist/ba_data/python/bascenev1/_profile.py
vendored
1
dist/ba_data/python/bascenev1/_profile.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to player profiles."""
|
"""Functionality related to player profiles."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
|
||||||
11
dist/ba_data/python/bascenev1/_session.py
vendored
11
dist/ba_data/python/bascenev1/_session.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Defines base session class."""
|
"""Defines base session class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
@ -338,7 +339,9 @@ class Session:
|
||||||
with babase.ContextRef.empty():
|
with babase.ContextRef.empty():
|
||||||
self._waitlist_timers[identifier] = babase.AppTimer(
|
self._waitlist_timers[identifier] = babase.AppTimer(
|
||||||
_g_player_rejoin_cooldown,
|
_g_player_rejoin_cooldown,
|
||||||
babase.Call(self._remove_player_from_waitlist, identifier),
|
babase.CallStrict(
|
||||||
|
self._remove_player_from_waitlist, identifier
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if not sessionplayer.in_game:
|
if not sessionplayer.in_game:
|
||||||
|
|
@ -372,7 +375,7 @@ class Session:
|
||||||
|
|
||||||
# Grab their activity-specific player instance.
|
# Grab their activity-specific player instance.
|
||||||
player = sessionplayer.activityplayer
|
player = sessionplayer.activityplayer
|
||||||
assert isinstance(player, (Player, type(None)))
|
assert isinstance(player, Player | None)
|
||||||
|
|
||||||
# Remove them from any current Activity.
|
# Remove them from any current Activity.
|
||||||
if player is not None and activity is not None:
|
if player is not None and activity is not None:
|
||||||
|
|
@ -498,7 +501,9 @@ class Session:
|
||||||
# Set a timer to set in motion this activity's demise.
|
# Set a timer to set in motion this activity's demise.
|
||||||
self._activity_end_timer = _bascenev1.BaseTimer(
|
self._activity_end_timer = _bascenev1.BaseTimer(
|
||||||
delay,
|
delay,
|
||||||
babase.Call(self._complete_end_activity, activity, results),
|
babase.CallStrict(
|
||||||
|
self._complete_end_activity, activity, results
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
def handlemessage(self, msg: Any) -> Any:
|
||||||
|
|
|
||||||
4
dist/ba_data/python/bascenev1/_stats.py
vendored
4
dist/ba_data/python/bascenev1/_stats.py
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
#
|
#
|
||||||
"""Functionality related to scores and statistics."""
|
"""Functionality related to scores and statistics."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
@ -13,7 +14,6 @@ import babase
|
||||||
|
|
||||||
import _bascenev1
|
import _bascenev1
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Any, Sequence
|
from typing import Any, Sequence
|
||||||
|
|
||||||
|
|
@ -237,7 +237,7 @@ class PlayerRecord:
|
||||||
if name is not None:
|
if name is not None:
|
||||||
_bascenev1.timer(
|
_bascenev1.timer(
|
||||||
0.3 + delay,
|
0.3 + delay,
|
||||||
babase.Call(
|
babase.CallStrict(
|
||||||
_apply, name, score, showpoints, color, scale, sound
|
_apply, name, score, showpoints, color, scale, sound
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
3
dist/ba_data/python/bascenev1/_team.py
vendored
3
dist/ba_data/python/bascenev1/_team.py
vendored
|
|
@ -65,7 +65,8 @@ class SessionTeam:
|
||||||
self.customdata = {}
|
self.customdata = {}
|
||||||
|
|
||||||
|
|
||||||
class Team[PlayerT: bascenev1.Player]:
|
# class Team[PlayerT: bascenev1.Player]:
|
||||||
|
class Team[PlayerT]:
|
||||||
"""A team in a specific :class:`~bascenev1.Activity`.
|
"""A team in a specific :class:`~bascenev1.Activity`.
|
||||||
|
|
||||||
These correspond to :class:`~bascenev1.SessionTeam` objects, but are
|
These correspond to :class:`~bascenev1.SessionTeam` objects, but are
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue