Source code for bacommon.bs._msg
# 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, field
from typing import Annotated, override
from efro.dataclassio import ioprepped, IOAttrs
from efro.message import Message, Response
from bacommon.bs._displayitem import DisplayItemWrapper
from bacommon.bs._clienteffect import ClientEffect
from bacommon.bs._clouddialog import CloudDialogAction, CloudDialogWrapper
from bacommon.bs._chest import ClassicChestAppearance
[docs]
@ioprepped
@dataclass
class ChestActionMessage(Message):
"""Request action about a chest."""
[docs]
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: Action
# Tokens we are paying (only applies to unlock).
token_payment: int
chest_id: str
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [ChestActionResponse]
[docs]
@ioprepped
@dataclass
class ChestActionResponse(Response):
"""Here's the results of that action you asked for, boss."""
# Tokens that were actually charged.
tokens_charged: int = 0
# If present, signifies the chest has been opened and we should show
# the user this stuff that was in it.
contents: list[DisplayItemWrapper] | None = None
# If contents are present, which of the chest's prize-sets they
# represent.
prizeindex: int = 0
# Printable error if something goes wrong.
error: str | None = 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: str | None = 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: str | None = None
# Effects to show on the client. Replaces warning and success_msg in
# build 22311 or newer.
effects: list[ClientEffect] = field(default_factory=list)
[docs]
@ioprepped
@dataclass
class CloudDialogActionMessage(Message):
"""Do something to a client ui."""
id: str
action: CloudDialogAction
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [CloudDialogActionResponse]
[docs]
@ioprepped
@dataclass
class CloudDialogActionResponse(Response):
"""Did something to that inbox entry, boss."""
[docs]
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: ErrorType | None
# User facing error message in the case of errors.
error_message: str | None
effects: list[ClientEffect]
[docs]
@ioprepped
@dataclass
class GetClassicPurchasesMessage(Message):
"""Asking for current account's classic purchases."""
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [GetClassicPurchasesResponse]
[docs]
@ioprepped
@dataclass
class GetClassicPurchasesResponse(Response):
"""Here's those classic purchases ya asked for boss."""
purchases: set[str]
[docs]
@ioprepped
@dataclass
class GlobalProfileCheckMessage(Message):
"""Is this global profile name available?"""
name: str
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [GlobalProfileCheckResponse]
[docs]
@ioprepped
@dataclass
class GlobalProfileCheckResponse(Response):
"""Here's that profile check ya asked for boss."""
available: bool
ticket_cost: int
[docs]
@ioprepped
@dataclass
class InboxRequestMessage(Message):
"""Message requesting our inbox."""
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [InboxRequestResponse]
[docs]
@ioprepped
@dataclass
class InboxRequestResponse(Response):
"""Here's that inbox contents you asked for, boss."""
wrappers: list[CloudDialogWrapper]
# Printable error if something goes wrong.
error: str | None = None
[docs]
@ioprepped
@dataclass
class LegacyRequest(Message):
"""A generic request for the legacy master server."""
request: str
request_type: str
user_agent_string: str
data: str
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [LegacyResponse]
[docs]
@ioprepped
@dataclass
class LegacyResponse(Response):
"""Response for generic legacy request."""
data: str | None
zipped: bool
[docs]
@ioprepped
@dataclass
class ChestInfoMessage(Message):
"""Request info about a chest."""
chest_id: str
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [ChestInfoResponse]
[docs]
@ioprepped
@dataclass
class ChestInfoResponse(Response):
"""Here's that chest info you asked for, boss."""
[docs]
@dataclass
class Chest:
"""A lovely chest."""
[docs]
@dataclass
class PrizeSet:
"""A possible set of prizes for this chest."""
weight: float
contents: list[DisplayItemWrapper]
appearance: ClassicChestAppearance
# How much it costs to unlock *now*.
unlock_tokens: int
# When it unlocks on its own.
unlock_time: datetime.datetime
# Possible prizes we contain.
prizesets: list[PrizeSet]
# Are ads allowed now?
ad_allow: bool
chest: Chest | None
user_tokens: int | None
[docs]
@ioprepped
@dataclass
class PrivatePartyMessage(Message):
"""Message asking about info we need for private-party UI."""
need_datacode: bool
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [PrivatePartyResponse]
[docs]
@ioprepped
@dataclass
class PrivatePartyResponse(Response):
"""Here's that private party UI info you asked for, boss."""
success: bool
tokens: int
gold_pass: bool
datacode: str | None
[docs]
@ioprepped
@dataclass
class ScoreSubmitMessage(Message):
"""Let the server know we got some score in something."""
score_token: str
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [ScoreSubmitResponse]
[docs]
@ioprepped
@dataclass
class ScoreSubmitResponse(Response):
"""Did something to that inbox entry, boss."""
# Things we should show on our end.
effects: list[ClientEffect]
[docs]
@ioprepped
@dataclass
class SendInfoMessage(Message):
"""User is using the send-info function."""
description: str
[docs]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [SendInfoResponse]
[docs]
@ioprepped
@dataclass
class SendInfoResponse(Response):
"""Response to sending info to the server."""
handled: bool
message: str | None = None
effects: list[ClientEffect] = field(default_factory=list)
legacy_code: str | None = None
# Docs-generation hack; import some stuff that we likely only forward-declared
# in our actual source code so that docs tools can find it.
from typing import (Coroutine, Any, Literal, Callable,
Generator, Awaitable, Sequence, Self)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum