Bombsquad-Ballistica-Modded.../dist/ba_data/python/bacommon/cloud.py

487 lines
12 KiB
Python
Raw Normal View History

2022-06-09 01:26:46 +05:30
# Released under the MIT License. See LICENSE for details.
#
2026-02-15 14:00:00 +05:30
"""Cloud related functionality.
.. warning::
This is an internal api and subject to change at any time. Do not use
it in mod code.
"""
2022-06-09 01:26:46 +05:30
from __future__ import annotations
2025-02-09 00:17:58 +05:30
from enum import Enum
2022-06-30 00:31:52 +05:30
from dataclasses import dataclass, field
2024-05-19 18:25:43 +05:30
from typing import TYPE_CHECKING, Annotated, override
2022-06-09 01:26:46 +05:30
from efro.message import Message, Response
from efro.dataclassio import ioprepped, IOAttrs
2026-02-15 14:00:00 +05:30
from bacommon.analytics import AnalyticsEvent
2025-04-06 17:17:13 +05:30
from bacommon.securedata import SecureDataChecker
2022-06-30 00:31:52 +05:30
from bacommon.transfer import DirectoryManifest
2022-12-25 00:39:49 +05:30
from bacommon.login import LoginType
2026-02-15 14:00:00 +05:30
from bacommon.docui import DocUIRequest, DocUIResponse
import bacommon.displayitem as ditm
import bacommon.clienteffect as clfx
2022-06-09 01:26:46 +05:30
if TYPE_CHECKING:
pass
2024-11-28 00:23:35 +05:30
class WebLocation(Enum):
"""Set of places we can be directed on ballistica.net."""
ACCOUNT_EDITOR = 'e'
ACCOUNT_DELETE_SECTION = 'd'
2025-09-07 18:34:55 +05:30
@ioprepped
@dataclass
class CloudVals:
"""Engine config values provided by the master server.
Used to convey things such as debug logging.
"""
#: Fully qualified type names we should emit extra debug logs for
#: when garbage-collected (for debugging ref loops).
gc_debug_types: Annotated[
list[str], IOAttrs('gct', store_default=False)
] = field(default_factory=list)
#: Max number of objects of a given type to emit debug logs for.
gc_debug_type_limit: Annotated[int, IOAttrs('gdl', store_default=False)] = 2
2022-06-09 01:26:46 +05:30
@ioprepped
@dataclass
class LoginProxyRequestMessage(Message):
"""Request send to the cloud to ask for a login-proxy."""
2024-01-27 21:25:16 +05:30
@override
2022-06-09 01:26:46 +05:30
@classmethod
2022-10-01 14:51:35 +05:30
def get_response_types(cls) -> list[type[Response] | None]:
2022-06-09 01:26:46 +05:30
return [LoginProxyRequestResponse]
@ioprepped
@dataclass
class LoginProxyRequestResponse(Response):
"""Response to a request for a login proxy."""
2024-05-19 18:25:43 +05:30
# URL to direct the user to for sign in.
2022-06-09 01:26:46 +05:30
url: Annotated[str, IOAttrs('u')]
2024-05-19 18:25:43 +05:30
# URL to use for overlay-web-browser sign ins.
url_overlay: Annotated[str, IOAttrs('uo')]
2022-06-09 01:26:46 +05:30
# Proxy-Login id for querying results.
proxyid: Annotated[str, IOAttrs('p')]
# Proxy-Login key for querying results.
proxykey: Annotated[str, IOAttrs('k')]
@ioprepped
@dataclass
class LoginProxyStateQueryMessage(Message):
"""Soo.. how is that login proxy going?"""
2022-06-09 01:26:46 +05:30
proxyid: Annotated[str, IOAttrs('p')]
proxykey: Annotated[str, IOAttrs('k')]
2024-01-27 21:25:16 +05:30
@override
2022-06-09 01:26:46 +05:30
@classmethod
2022-10-01 14:51:35 +05:30
def get_response_types(cls) -> list[type[Response] | None]:
2022-06-09 01:26:46 +05:30
return [LoginProxyStateQueryResponse]
@ioprepped
@dataclass
class LoginProxyStateQueryResponse(Response):
"""Here's the info on that login-proxy you asked about, boss."""
class State(Enum):
"""States a login-proxy can be in."""
2022-06-09 01:26:46 +05:30
WAITING = 'waiting'
SUCCESS = 'success'
FAIL = 'fail'
state: Annotated[State, IOAttrs('s')]
# On success, these will be filled out.
2022-06-30 00:31:52 +05:30
credentials: Annotated[str | None, IOAttrs('tk')]
2022-06-09 01:26:46 +05:30
@ioprepped
@dataclass
class LoginProxyCompleteMessage(Message):
"""Just so you know, we're done with this proxy."""
2022-06-09 01:26:46 +05:30
proxyid: Annotated[str, IOAttrs('p')]
2022-07-16 17:59:14 +05:30
@ioprepped
@dataclass
class PingMessage(Message):
"""Standard ping."""
2024-01-27 21:25:16 +05:30
@override
2022-07-16 17:59:14 +05:30
@classmethod
2022-10-01 14:51:35 +05:30
def get_response_types(cls) -> list[type[Response] | None]:
2022-07-16 17:59:14 +05:30
return [PingResponse]
@ioprepped
@dataclass
class PingResponse(Response):
"""pong."""
2022-06-09 01:26:46 +05:30
@ioprepped
@dataclass
2022-06-30 00:31:52 +05:30
class TestMessage(Message):
"""Can I get some of that workspace action?"""
2022-06-30 00:31:52 +05:30
testfoo: Annotated[int, IOAttrs('f')]
2024-01-27 21:25:16 +05:30
@override
2022-06-30 00:31:52 +05:30
@classmethod
2022-10-01 14:51:35 +05:30
def get_response_types(cls) -> list[type[Response] | None]:
2022-06-30 00:31:52 +05:30
return [TestResponse]
2022-06-09 01:26:46 +05:30
@ioprepped
@dataclass
2022-06-30 00:31:52 +05:30
class TestResponse(Response):
"""Here's that workspace you asked for, boss."""
2022-06-09 01:26:46 +05:30
2022-06-30 00:31:52 +05:30
testfoo: Annotated[int, IOAttrs('f')]
@ioprepped
@dataclass
class WorkspaceFetchState:
"""Common state data for a workspace fetch."""
2022-06-30 00:31:52 +05:30
manifest: Annotated[DirectoryManifest, IOAttrs('m')]
iteration: Annotated[int, IOAttrs('i')] = 0
total_deletes: Annotated[int, IOAttrs('tdels')] = 0
total_downloads: Annotated[int, IOAttrs('tdlds')] = 0
total_up_to_date: Annotated[int | None, IOAttrs('tunmd')] = None
2022-06-09 01:26:46 +05:30
@ioprepped
@dataclass
2022-06-30 00:31:52 +05:30
class WorkspaceFetchMessage(Message):
"""Can I get some of that workspace action?"""
2022-06-30 00:31:52 +05:30
workspaceid: Annotated[str, IOAttrs('w')]
state: Annotated[WorkspaceFetchState, IOAttrs('s')]
2022-06-09 01:26:46 +05:30
2024-01-27 21:25:16 +05:30
@override
2022-06-30 00:31:52 +05:30
@classmethod
2022-10-01 14:51:35 +05:30
def get_response_types(cls) -> list[type[Response] | None]:
2022-06-30 00:31:52 +05:30
return [WorkspaceFetchResponse]
2022-06-09 01:26:46 +05:30
2022-06-30 00:31:52 +05:30
@ioprepped
@dataclass
class WorkspaceFetchResponse(Response):
"""Here's that workspace you asked for, boss."""
state: Annotated[WorkspaceFetchState, IOAttrs('s')]
deletes: Annotated[list[str], IOAttrs('dlt', store_default=False)] = field(
default_factory=list
)
downloads_inline: Annotated[
dict[str, bytes], IOAttrs('dinl', store_default=False)
] = field(default_factory=dict)
2022-06-30 00:31:52 +05:30
done: Annotated[bool, IOAttrs('d')] = False
2022-12-25 00:39:49 +05:30
@ioprepped
@dataclass
class MerchAvailabilityMessage(Message):
"""Can we show merch link?"""
2024-01-27 21:25:16 +05:30
@override
2022-12-25 00:39:49 +05:30
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [MerchAvailabilityResponse]
@ioprepped
@dataclass
class MerchAvailabilityResponse(Response):
"""About that merch..."""
url: Annotated[str | None, IOAttrs('u')]
@ioprepped
@dataclass
class SignInMessage(Message):
"""Can I sign in please?"""
login_type: Annotated[LoginType, IOAttrs('l')]
sign_in_token: Annotated[str, IOAttrs('t')]
2023-01-30 23:35:08 +05:30
# For debugging. Can remove soft_default once build 20988+ is ubiquitous.
description: Annotated[str, IOAttrs('d', soft_default='-')]
apptime: Annotated[float, IOAttrs('at', soft_default=-1.0)]
2024-01-27 21:25:16 +05:30
@override
2022-12-25 00:39:49 +05:30
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [SignInResponse]
@ioprepped
@dataclass
class SignInResponse(Response):
"""Here's that sign-in result you asked for, boss."""
credentials: Annotated[str | None, IOAttrs('c')]
@ioprepped
@dataclass
class ManageAccountMessage(Message):
"""Message asking for a manage-account url."""
2024-11-28 00:23:35 +05:30
weblocation: Annotated[WebLocation, IOAttrs('l')] = (
WebLocation.ACCOUNT_EDITOR
)
2024-01-27 21:25:16 +05:30
@override
2022-12-25 00:39:49 +05:30
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [ManageAccountResponse]
@ioprepped
@dataclass
class ManageAccountResponse(Response):
"""Here's that sign-in result you asked for, boss."""
url: Annotated[str | None, IOAttrs('u')]
2024-11-28 00:23:35 +05:30
@ioprepped
@dataclass
class StoreQueryMessage(Message):
"""Message asking about purchasable stuff and store related state."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [StoreQueryResponse]
@ioprepped
@dataclass
class StoreQueryResponse(Response):
"""Here's that store info you asked for, boss."""
class Result(Enum):
"""Our overall result."""
SUCCESS = 's'
ERROR = 'e'
@dataclass
class Purchase:
"""Info about a purchasable thing."""
purchaseid: Annotated[str, IOAttrs('id')]
# Overall result; all data is undefined if not SUCCESS.
result: Annotated[Result, IOAttrs('r')]
tokens: Annotated[int, IOAttrs('t')]
gold_pass: Annotated[bool, IOAttrs('g')]
available_purchases: Annotated[list[Purchase], IOAttrs('p')]
token_info_url: Annotated[str, IOAttrs('tiu')]
2025-04-06 17:17:13 +05:30
@ioprepped
@dataclass
class SecureDataCheckMessage(Message):
"""Was this data signed by the master-server?."""
data: Annotated[bytes, IOAttrs('d')]
signature: Annotated[bytes, IOAttrs('s')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [SecureDataCheckResponse]
@ioprepped
@dataclass
class SecureDataCheckResponse(Response):
"""Here's the result of that data check, boss."""
# Whether the data signature was valid.
result: Annotated[bool, IOAttrs('v')]
@ioprepped
@dataclass
class SecureDataCheckerRequest(Message):
"""Can I get a checker over here?."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [SecureDataCheckerResponse]
@ioprepped
@dataclass
class SecureDataCheckerResponse(Response):
"""Here's that checker ya asked for, boss."""
checker: Annotated[SecureDataChecker, IOAttrs('c')]
2025-09-07 18:34:55 +05:30
@ioprepped
@dataclass
class CloudValsRequest(Message):
"""Can a fella get some cloud vals around here?."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [CloudValsResponse]
@ioprepped
@dataclass
class CloudValsResponse(Response):
"""Here's them cloud vals ya asked for, boss."""
vals: Annotated[CloudVals, IOAttrs('v')]
2026-02-15 14:00:00 +05:30
@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')]
2026-02-23 19:28:45 +05:30
@ioprepped
@dataclass
class AuthRequestMessage(Message):
"""Request access to a server for a current account."""
global_app_instance_uuid: Annotated[str, IOAttrs('a')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [AuthRequestResponse]
@ioprepped
@dataclass
class AuthRequestResponse(Response):
"""Here's that access ya asked for boss."""
error: Annotated[str | None, IOAttrs('e')]
token: Annotated[str | None, IOAttrs('t')]