mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2025-11-07 17:36:15 +00:00
API update to 1.6.3 stable
This commit is contained in:
parent
463bae3913
commit
fcd8a94e81
146 changed files with 2254 additions and 510 deletions
BIN
dist/ba_data/python/ba/__pycache__/__init__.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/__init__.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_account.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_account.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_achievement.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_achievement.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_activity.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_activity.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_activitytypes.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_activitytypes.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_actor.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_actor.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_ads.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_ads.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_analytics.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_analytics.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_app.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_app.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_appconfig.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_appconfig.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_appdelegate.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_appdelegate.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_appmode.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_appmode.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_apputils.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_apputils.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_assetmanager.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_assetmanager.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_benchmark.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_benchmark.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_campaign.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_campaign.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_collision.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_collision.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_coopgame.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_coopgame.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_coopsession.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_coopsession.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_dependency.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_dependency.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_dualteamsession.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_dualteamsession.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_enums.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_enums.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_error.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_error.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_freeforallsession.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_freeforallsession.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_gameactivity.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_gameactivity.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_gameresults.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_gameresults.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_gameutils.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_gameutils.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_general.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_general.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_hooks.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_input.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_keyboard.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_keyboard.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_language.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_language.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_level.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_lobby.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_map.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_map.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_math.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_math.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_messages.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_messages.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_meta.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_meta.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_multiteamsession.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_multiteamsession.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_music.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_music.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_net.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_net.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_nodeactor.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_nodeactor.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_player.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_player.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_playlist.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_playlist.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_plugin.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_plugin.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_powerup.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_powerup.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_profile.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_profile.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_score.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_score.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_servermode.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_servermode.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_session.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_session.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_settings.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_settings.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_stats.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_stats.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_store.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_store.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_team.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_team.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_teamgame.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_teamgame.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_tips.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_tips.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_tournament.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_tournament.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/_ui.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/_ui.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/deprecated.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/deprecated.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/internal.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/internal.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/macmusicapp.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/macmusicapp.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/modutils.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/modutils.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/ba/__pycache__/osmusic.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/ba/__pycache__/osmusic.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
6
dist/ba_data/python/ba/_account.py
vendored
6
dist/ba_data/python/ba/_account.py
vendored
|
|
@ -181,7 +181,7 @@ class AccountSubsystem:
|
|||
"""Return whether pro is currently unlocked."""
|
||||
|
||||
# Check our tickets-based pro upgrade and our two real-IAP based
|
||||
# upgrades. Also unlock this stuff in ballistica-core builds.
|
||||
# upgrades. Also always unlock this stuff in ballistica-core builds.
|
||||
return bool(
|
||||
_ba.get_purchased('upgrades.pro')
|
||||
or _ba.get_purchased('static.pro')
|
||||
|
|
@ -191,8 +191,8 @@ class AccountSubsystem:
|
|||
def have_pro_options(self) -> bool:
|
||||
"""Return whether pro-options are present.
|
||||
|
||||
This is True for owners of Pro or old installs
|
||||
before Pro was a requirement for these.
|
||||
This is True for owners of Pro or for old installs
|
||||
before Pro was a requirement for these options.
|
||||
"""
|
||||
|
||||
# We expose pro options if the server tells us to
|
||||
|
|
|
|||
7
dist/ba_data/python/ba/_actor.py
vendored
7
dist/ba_data/python/ba/_actor.py
vendored
|
|
@ -161,9 +161,10 @@ class Actor:
|
|||
"""Returns whether the Actor is 'alive'.
|
||||
|
||||
What this means is up to the Actor.
|
||||
It is not a requirement for Actors to be
|
||||
able to die; just that they report whether
|
||||
they are Alive or not.
|
||||
It is not a requirement for Actors to be able to die;
|
||||
just that they report whether they consider themselves
|
||||
to be alive or not. In cases where dead/alive is
|
||||
irrelevant, True should be returned.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
|
|
|||
55
dist/ba_data/python/ba/_app.py
vendored
55
dist/ba_data/python/ba/_app.py
vendored
|
|
@ -3,6 +3,7 @@
|
|||
"""Functionality related to the high level state of the app."""
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ from ba._plugin import PluginSubsystem
|
|||
from ba._account import AccountSubsystem
|
||||
from ba._meta import MetadataSubsystem
|
||||
from ba._ads import AdsSubsystem
|
||||
from ba._net import NetworkSubsystem
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import ba
|
||||
|
|
@ -32,8 +34,16 @@ class App:
|
|||
Note that properties not documented here should be considered internal
|
||||
and subject to change without warning.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
class State(Enum):
|
||||
"""High level state the app can be in."""
|
||||
LAUNCHING = 0
|
||||
RUNNING = 1
|
||||
PAUSED = 2
|
||||
SHUTTING_DOWN = 3
|
||||
|
||||
@property
|
||||
def build_number(self) -> int:
|
||||
"""Integer build number.
|
||||
|
|
@ -172,6 +182,8 @@ class App:
|
|||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
self.state = self.State.LAUNCHING
|
||||
|
||||
# Config.
|
||||
self.config_file_healthy = False
|
||||
|
||||
|
|
@ -199,7 +211,6 @@ class App:
|
|||
self.tips: List[str] = []
|
||||
self.stress_test_reset_timer: Optional[ba.Timer] = None
|
||||
self.did_weak_call_warning = False
|
||||
self.ran_on_app_launch = False
|
||||
|
||||
self.log_have_new = False
|
||||
self.log_upload_timer_started = False
|
||||
|
|
@ -227,6 +238,7 @@ class App:
|
|||
self.ach = AchievementSubsystem()
|
||||
self.ui = UISubsystem()
|
||||
self.ads = AdsSubsystem()
|
||||
self.net = NetworkSubsystem()
|
||||
|
||||
# Lobby.
|
||||
self.lobby_random_profile_index: int = 1
|
||||
|
|
@ -281,7 +293,6 @@ class App:
|
|||
from ba._enums import TimeType
|
||||
import custom_hooks
|
||||
custom_hooks.on_app_launch()
|
||||
|
||||
cfg = self.config
|
||||
|
||||
self.delegate = appdelegate.AppDelegate()
|
||||
|
|
@ -376,15 +387,35 @@ class App:
|
|||
self.accounts.on_app_launch()
|
||||
self.plugins.on_app_launch()
|
||||
|
||||
self.ran_on_app_launch = True
|
||||
self.state = self.State.RUNNING
|
||||
|
||||
# from ba._dependency import test_depset
|
||||
# test_depset()
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
"""Called when the app goes to a suspended state."""
|
||||
self.state = self.State.PAUSED
|
||||
self.plugins.on_app_pause()
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
"""Run when the app resumes from a suspended state."""
|
||||
|
||||
self.state = self.State.RUNNING
|
||||
self.fg_state += 1
|
||||
self.accounts.on_app_resume()
|
||||
self.music.on_app_resume()
|
||||
self.plugins.on_app_resume()
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
"""(internal)"""
|
||||
self.state = self.State.SHUTTING_DOWN
|
||||
self.music.on_app_shutdown()
|
||||
self.plugins.on_app_shutdown()
|
||||
|
||||
def read_config(self) -> None:
|
||||
"""(internal)"""
|
||||
from ba import _appconfig
|
||||
self._config, self.config_file_healthy = _appconfig.read_config()
|
||||
from ba._appconfig import read_config
|
||||
self._config, self.config_file_healthy = read_config()
|
||||
|
||||
def pause(self) -> None:
|
||||
"""Pause the game due to a user request or menu popping up.
|
||||
|
|
@ -484,16 +515,6 @@ class App:
|
|||
else:
|
||||
self.main_menu_resume_callbacks.append(call)
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
"""Called when the app goes to a suspended state."""
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
"""Run when the app resumes from a suspended state."""
|
||||
|
||||
self.fg_state += 1
|
||||
self.accounts.on_app_resume()
|
||||
self.music.on_app_resume()
|
||||
|
||||
def launch_coop_game(self,
|
||||
game: str,
|
||||
force: bool = False,
|
||||
|
|
@ -542,10 +563,6 @@ class App:
|
|||
_ba.fade_screen(False, endcall=_fade_end)
|
||||
return True
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
"""(internal)"""
|
||||
self.music.on_app_shutdown()
|
||||
|
||||
def handle_deep_link(self, url: str) -> None:
|
||||
"""Handle a deep link URL."""
|
||||
from ba._language import Lstr
|
||||
|
|
|
|||
4
dist/ba_data/python/ba/_apputils.py
vendored
4
dist/ba_data/python/ba/_apputils.py
vendored
|
|
@ -56,7 +56,7 @@ def handle_log() -> None:
|
|||
When this happens, we can upload our log to the server
|
||||
after a short bit if desired.
|
||||
"""
|
||||
from ba._netutils import master_server_post
|
||||
from ba._net import master_server_post
|
||||
from ba._enums import TimeType
|
||||
app = _ba.app
|
||||
app.log_have_new = True
|
||||
|
|
@ -121,7 +121,7 @@ def handle_leftover_log_file() -> None:
|
|||
"""Handle an un-uploaded log from a previous run."""
|
||||
try:
|
||||
import json
|
||||
from ba._netutils import master_server_post
|
||||
from ba._net import master_server_post
|
||||
|
||||
if os.path.exists(_ba.get_log_file_path()):
|
||||
with open(_ba.get_log_file_path()) as infile:
|
||||
|
|
|
|||
1
dist/ba_data/python/ba/_assetmanager.py
vendored
1
dist/ba_data/python/ba/_assetmanager.py
vendored
|
|
@ -158,6 +158,7 @@ def fetch_url(url: str, filename: Path, asset_gather: AssetGather) -> None:
|
|||
"""Fetch a given url to a given filename for a given AssetGather.
|
||||
|
||||
"""
|
||||
# pylint: disable=consider-using-with
|
||||
|
||||
import socket
|
||||
|
||||
|
|
|
|||
5
dist/ba_data/python/ba/_hooks.py
vendored
5
dist/ba_data/python/ba/_hooks.py
vendored
|
|
@ -307,7 +307,6 @@ def party_invite_revoke(invite_id: str) -> None:
|
|||
_ba.containerwidget(edit=win.get_root_widget(),
|
||||
transition='out_right')
|
||||
|
||||
|
||||
import custom_hooks as chooks
|
||||
def filter_chat_message(msg: str, client_id: int) -> Optional[str]:
|
||||
"""Intercept/filter chat messages.
|
||||
|
|
@ -317,10 +316,8 @@ def filter_chat_message(msg: str, client_id: int) -> Optional[str]:
|
|||
Should filter and return the string to be displayed, or return None
|
||||
to ignore the message.
|
||||
"""
|
||||
|
||||
|
||||
return chooks.filter_chat_message(msg,client_id)
|
||||
|
||||
|
||||
|
||||
|
||||
def local_chat_message(msg: str) -> None:
|
||||
|
|
|
|||
10
dist/ba_data/python/ba/_map.py
vendored
10
dist/ba_data/python/ba/_map.py
vendored
|
|
@ -215,11 +215,10 @@ class Map(Actor):
|
|||
|
||||
|
||||
|
||||
# I DONT THINK YOU REALLY WANT TO REMOVE MY NAME , DO YOU ?
|
||||
self.hg=ba.NodeActor(
|
||||
_ba.newnode('text',
|
||||
attrs={
|
||||
'text': "Smoothy Build\n v1.0",
|
||||
'text': "Smoothy Build\n v1.2",
|
||||
|
||||
'flatness': 1.0,
|
||||
'h_align': 'center',
|
||||
|
|
@ -229,8 +228,6 @@ class Map(Actor):
|
|||
'position':(-60,23),
|
||||
'color':(0.3,0.3,0.3)
|
||||
}))
|
||||
|
||||
|
||||
# Set area-of-interest bounds.
|
||||
aoi_bounds = self.get_def_bound_box('area_of_interest_bounds')
|
||||
if aoi_bounds is None:
|
||||
|
|
@ -297,7 +294,10 @@ class Map(Actor):
|
|||
self.is_flying = False
|
||||
|
||||
# FIXME: this should be part of game; not map.
|
||||
self._next_ffa_start_index = 0
|
||||
# Let's select random index for first spawn point,
|
||||
# so that no one is offended by the constant spawn on the edge.
|
||||
self._next_ffa_start_index = random.randrange(
|
||||
len(self.ffa_spawn_points))
|
||||
|
||||
def is_point_near_edge(self,
|
||||
point: ba.Vec3,
|
||||
|
|
|
|||
190
dist/ba_data/python/ba/_net.py
vendored
Normal file
190
dist/ba_data/python/ba/_net.py
vendored
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Networking related functionality."""
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import threading
|
||||
import weakref
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Dict, Union, Callable, Optional
|
||||
import socket
|
||||
import ba
|
||||
MasterServerCallback = Callable[[Union[None, Dict[str, Any]]], None]
|
||||
|
||||
# Timeout for standard functions talking to the master-server/etc.
|
||||
DEFAULT_REQUEST_TIMEOUT_SECONDS = 60
|
||||
|
||||
|
||||
class NetworkSubsystem:
|
||||
"""Network related app subsystem."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.region_pings: Dict[str, float] = {}
|
||||
|
||||
|
||||
def get_ip_address_type(addr: str) -> socket.AddressFamily:
|
||||
"""Return socket.AF_INET6 or socket.AF_INET4 for the provided address."""
|
||||
import socket
|
||||
socket_type = None
|
||||
|
||||
# First try it as an ipv4 address.
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET, addr)
|
||||
socket_type = socket.AF_INET
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# Hmm apparently not ipv4; try ipv6.
|
||||
if socket_type is None:
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET6, addr)
|
||||
socket_type = socket.AF_INET6
|
||||
except OSError:
|
||||
pass
|
||||
if socket_type is None:
|
||||
raise ValueError(f'addr seems to be neither v4 or v6: {addr}')
|
||||
return socket_type
|
||||
|
||||
|
||||
class MasterServerResponseType(Enum):
|
||||
"""How to interpret responses from the master-server."""
|
||||
JSON = 0
|
||||
|
||||
|
||||
class MasterServerCallThread(threading.Thread):
|
||||
"""Thread to communicate with the master-server."""
|
||||
|
||||
def __init__(self, request: str, request_type: str,
|
||||
data: Optional[Dict[str, Any]],
|
||||
callback: Optional[MasterServerCallback],
|
||||
response_type: MasterServerResponseType):
|
||||
super().__init__()
|
||||
self._request = request
|
||||
self._request_type = request_type
|
||||
if not isinstance(response_type, MasterServerResponseType):
|
||||
raise TypeError(f'Invalid response type: {response_type}')
|
||||
self._response_type = response_type
|
||||
self._data = {} if data is None else copy.deepcopy(data)
|
||||
self._callback: Optional[MasterServerCallback] = callback
|
||||
self._context = _ba.Context('current')
|
||||
|
||||
# Save and restore the context we were created from.
|
||||
activity = _ba.getactivity(doraise=False)
|
||||
self._activity = weakref.ref(
|
||||
activity) if activity is not None else None
|
||||
|
||||
def _run_callback(self, arg: Union[None, Dict[str, Any]]) -> None:
|
||||
# If we were created in an activity context and that activity has
|
||||
# since died, do nothing.
|
||||
# FIXME: Should we just be using a ContextCall instead of doing
|
||||
# this check manually?
|
||||
if self._activity is not None:
|
||||
activity = self._activity()
|
||||
if activity is None or activity.expired:
|
||||
return
|
||||
|
||||
# Technically we could do the same check for session contexts,
|
||||
# but not gonna worry about it for now.
|
||||
assert self._context is not None
|
||||
assert self._callback is not None
|
||||
with self._context:
|
||||
self._callback(arg)
|
||||
|
||||
def run(self) -> None:
|
||||
# pylint: disable=too-many-branches, consider-using-with
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import json
|
||||
|
||||
from efro.net import is_urllib_network_error
|
||||
from ba import _general
|
||||
try:
|
||||
self._data = _general.utf8_all(self._data)
|
||||
_ba.set_thread_name('BA_ServerCallThread')
|
||||
parse = urllib.parse
|
||||
if self._request_type == 'get':
|
||||
response = urllib.request.urlopen(
|
||||
urllib.request.Request(
|
||||
(_ba.get_master_server_address() + '/' +
|
||||
self._request + '?' + parse.urlencode(self._data)),
|
||||
None, {'User-Agent': _ba.app.user_agent_string}),
|
||||
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
|
||||
elif self._request_type == 'post':
|
||||
response = urllib.request.urlopen(
|
||||
urllib.request.Request(
|
||||
_ba.get_master_server_address() + '/' + self._request,
|
||||
parse.urlencode(self._data).encode(),
|
||||
{'User-Agent': _ba.app.user_agent_string}),
|
||||
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
|
||||
else:
|
||||
raise TypeError('Invalid request_type: ' + self._request_type)
|
||||
|
||||
# If html request failed.
|
||||
if response.getcode() != 200:
|
||||
response_data = None
|
||||
elif self._response_type == MasterServerResponseType.JSON:
|
||||
raw_data = response.read()
|
||||
|
||||
# Empty string here means something failed server side.
|
||||
if raw_data == b'':
|
||||
response_data = None
|
||||
else:
|
||||
response_data = json.loads(raw_data)
|
||||
else:
|
||||
raise TypeError(f'invalid responsetype: {self._response_type}')
|
||||
|
||||
except Exception as exc:
|
||||
do_print = False
|
||||
response_data = None
|
||||
|
||||
# Ignore common network errors; note unexpected ones.
|
||||
if is_urllib_network_error(exc):
|
||||
pass
|
||||
elif (self._response_type == MasterServerResponseType.JSON
|
||||
and isinstance(exc, json.decoder.JSONDecodeError)):
|
||||
# FIXME: should handle this better; could mean either the
|
||||
# server sent us bad data or it got corrupted along the way.
|
||||
pass
|
||||
else:
|
||||
do_print = True
|
||||
|
||||
if do_print:
|
||||
# Any other error here is unexpected,
|
||||
# so let's make a note of it,
|
||||
print(f'Error in MasterServerCallThread'
|
||||
f' (response-type={self._response_type},'
|
||||
f' response-data={response_data}):')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if self._callback is not None:
|
||||
_ba.pushcall(_general.Call(self._run_callback, response_data),
|
||||
from_other_thread=True)
|
||||
|
||||
|
||||
def master_server_get(
|
||||
request: str,
|
||||
data: Dict[str, Any],
|
||||
callback: Optional[MasterServerCallback] = None,
|
||||
response_type: MasterServerResponseType = MasterServerResponseType.JSON
|
||||
) -> None:
|
||||
"""Make a call to the master server via a http GET."""
|
||||
MasterServerCallThread(request, 'get', data, callback,
|
||||
response_type).start()
|
||||
|
||||
|
||||
def master_server_post(
|
||||
request: str,
|
||||
data: Dict[str, Any],
|
||||
callback: Optional[MasterServerCallback] = None,
|
||||
response_type: MasterServerResponseType = MasterServerResponseType.JSON
|
||||
) -> None:
|
||||
"""Make a call to the master server via a http POST."""
|
||||
MasterServerCallThread(request, 'post', data, callback,
|
||||
response_type).start()
|
||||
37
dist/ba_data/python/ba/_plugin.py
vendored
37
dist/ba_data/python/ba/_plugin.py
vendored
|
|
@ -6,6 +6,7 @@ from __future__ import annotations
|
|||
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -36,6 +37,33 @@ class PluginSubsystem:
|
|||
from ba import _error
|
||||
_error.print_exception('Error in plugin on_app_launch()')
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
"""Called when the app goes to a suspended state."""
|
||||
for plugin in self.active_plugins.values():
|
||||
try:
|
||||
plugin.on_app_pause()
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('Error in plugin on_app_pause()')
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
"""Run when the app resumes from a suspended state."""
|
||||
for plugin in self.active_plugins.values():
|
||||
try:
|
||||
plugin.on_app_resume()
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('Error in plugin on_app_resume()')
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
"""Called when the app is being closed."""
|
||||
for plugin in self.active_plugins.values():
|
||||
try:
|
||||
plugin.on_app_shutdown()
|
||||
except Exception:
|
||||
from ba import _error
|
||||
_error.print_exception('Error in plugin on_app_shutdown()')
|
||||
|
||||
def load_plugins(self) -> None:
|
||||
"""(internal)"""
|
||||
from ba._general import getclass
|
||||
|
|
@ -94,3 +122,12 @@ class Plugin:
|
|||
|
||||
def on_app_launch(self) -> None:
|
||||
"""Called when the app is being launched."""
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
"""Called after pausing game activity."""
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
"""Called after the game continues."""
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
"""Called before closing the application."""
|
||||
|
|
|
|||
25
dist/ba_data/python/ba/_servermode.py
vendored
25
dist/ba_data/python/ba/_servermode.py
vendored
|
|
@ -19,6 +19,7 @@ from ba._dualteamsession import DualTeamSession
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional, Dict, Any, Type
|
||||
|
||||
import ba
|
||||
from bacommon.servermanager import ServerConfig
|
||||
|
||||
|
|
@ -186,7 +187,7 @@ class ServerController:
|
|||
|
||||
def _run_access_check(self) -> None:
|
||||
"""Check with the master server to see if we're likely joinable."""
|
||||
from ba._netutils import master_server_get
|
||||
from ba._net import master_server_get
|
||||
master_server_get(
|
||||
'bsAccessCheck',
|
||||
{
|
||||
|
|
@ -301,6 +302,28 @@ class ServerController:
|
|||
print('WARNING: launch_server_session() expects to run '
|
||||
'with a signed in server account')
|
||||
|
||||
# If we didn't fetch a playlist but there's an inline one in the
|
||||
# server-config, pull it in to the game config and use it.
|
||||
if (self._config.playlist_code is None
|
||||
and self._config.playlist_inline is not None):
|
||||
self._playlist_name = 'ServerModePlaylist'
|
||||
if sessiontype is FreeForAllSession:
|
||||
ptypename = 'Free-for-All'
|
||||
elif sessiontype is DualTeamSession:
|
||||
ptypename = 'Team Tournament'
|
||||
else:
|
||||
raise RuntimeError(f'Unknown session type {sessiontype}')
|
||||
|
||||
# Need to add this in a transaction instead of just setting
|
||||
# it directly or it will get overwritten by the master-server.
|
||||
_ba.add_transaction({
|
||||
'type': 'ADD_PLAYLIST',
|
||||
'playlistType': ptypename,
|
||||
'playlistName': self._playlist_name,
|
||||
'playlist': self._config.playlist_inline
|
||||
})
|
||||
_ba.run_transactions()
|
||||
|
||||
if self._first_run:
|
||||
curtimestr = time.strftime('%c')
|
||||
_ba.log(
|
||||
|
|
|
|||
15
dist/ba_data/python/ba/_session.py
vendored
15
dist/ba_data/python/ba/_session.py
vendored
|
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Session:
|
||||
"""Defines a high level series of activities with a common purpose.
|
||||
"""Defines a high level series of ba.Activities with a common purpose.
|
||||
|
||||
category: Gameplay Classes
|
||||
|
||||
|
|
@ -99,6 +99,7 @@ class Session:
|
|||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=cyclic-import
|
||||
# pylint: disable=too-many-branches
|
||||
from ba._lobby import Lobby
|
||||
from ba._stats import Stats
|
||||
from ba._gameactivity import GameActivity
|
||||
|
|
@ -172,8 +173,16 @@ class Session:
|
|||
|
||||
# Create static teams if we're using them.
|
||||
if self.use_teams:
|
||||
assert team_names is not None
|
||||
assert team_colors is not None
|
||||
if team_names is None:
|
||||
raise RuntimeError(
|
||||
'use_teams is True but team_names not provided.')
|
||||
if team_colors is None:
|
||||
raise RuntimeError(
|
||||
'use_teams is True but team_colors not provided.')
|
||||
if len(team_colors) != len(team_names):
|
||||
raise RuntimeError(f'Got {len(team_names)} team_names'
|
||||
f' and {len(team_colors)} team_colors;'
|
||||
f' these numbers must match.')
|
||||
for i, color in enumerate(team_colors):
|
||||
team = SessionTeam(team_id=self._next_team_id,
|
||||
name=GameActivity.get_team_display_string(
|
||||
|
|
|
|||
4
dist/ba_data/python/ba/internal.py
vendored
4
dist/ba_data/python/ba/internal.py
vendored
|
|
@ -25,8 +25,8 @@ from ba._campaign import getcampaign
|
|||
from ba._messages import PlayerProfilesChangedMessage
|
||||
from ba._multiteamsession import DEFAULT_TEAM_COLORS, DEFAULT_TEAM_NAMES
|
||||
from ba._music import do_play_music
|
||||
from ba._netutils import (master_server_get, master_server_post,
|
||||
get_ip_address_type)
|
||||
from ba._net import (master_server_get, master_server_post,
|
||||
get_ip_address_type, DEFAULT_REQUEST_TIMEOUT_SECONDS)
|
||||
from ba._powerup import get_default_powerup_distribution
|
||||
from ba._profile import (get_player_profile_colors, get_player_profile_icon,
|
||||
get_player_colors)
|
||||
|
|
|
|||
BIN
dist/ba_data/python/bacommon/__pycache__/net.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/bacommon/__pycache__/net.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
69
dist/ba_data/python/bacommon/net.py
vendored
Normal file
69
dist/ba_data/python/bacommon/net.py
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Network related data and functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, List, Dict, Any, Tuple
|
||||
from dataclasses import dataclass
|
||||
|
||||
from efro import entity
|
||||
from efro.dataclassio import ioprepped
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class ServerNodeEntry(entity.CompoundValue):
|
||||
"""Information about a specific server."""
|
||||
region = entity.Field('r', entity.StringValue())
|
||||
address = entity.Field('a', entity.StringValue())
|
||||
port = entity.Field('p', entity.IntValue())
|
||||
|
||||
|
||||
class ServerNodeQueryResponse(entity.Entity):
|
||||
"""A response to a query about server-nodes."""
|
||||
|
||||
# If present, something went wrong, and this describes it.
|
||||
error = entity.Field('e', entity.OptionalStringValue(store_default=False))
|
||||
|
||||
# The set of servernodes.
|
||||
servers = entity.CompoundListField('s',
|
||||
ServerNodeEntry(),
|
||||
store_default=False)
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class PrivateHostingState:
|
||||
"""Combined state of whether we're hosting, whether we can, etc."""
|
||||
unavailable_error: Optional[str] = None
|
||||
party_code: Optional[str] = None
|
||||
able_to_host: bool = False
|
||||
tickets_to_host_now: int = 0
|
||||
minutes_until_free_host: Optional[float] = None
|
||||
free_host_minutes_remaining: Optional[float] = None
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class PrivateHostingConfig:
|
||||
"""Config provided when hosting a private party."""
|
||||
session_type: str = 'ffa'
|
||||
playlist_name: str = 'Unknown'
|
||||
randomize: bool = False
|
||||
tutorial: bool = False
|
||||
custom_team_names: Optional[Tuple[str, str]] = None
|
||||
custom_team_colors: Optional[Tuple[Tuple[float, float, float],
|
||||
Tuple[float, float, float]]] = None
|
||||
playlist: Optional[List[Dict[str, Any]]] = None
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class PrivatePartyConnectResult:
|
||||
"""Info about a server we get back when connecting."""
|
||||
error: Optional[str] = None
|
||||
addr: Optional[str] = None
|
||||
port: Optional[int] = None
|
||||
password: Optional[str] = None
|
||||
38
dist/ba_data/python/bacommon/servermanager.py
vendored
38
dist/ba_data/python/bacommon/servermanager.py
vendored
|
|
@ -4,13 +4,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import field, dataclass
|
||||
from typing import TYPE_CHECKING, List, Optional, Tuple, Dict, Any
|
||||
|
||||
from efro.dataclassio import ioprepped
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional, Tuple, List
|
||||
pass
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class ServerConfig:
|
||||
"""Configuration for the server manager app (<appname>_server)."""
|
||||
|
|
@ -49,8 +52,7 @@ class ServerConfig:
|
|||
max_party_size: int = 6
|
||||
|
||||
# Options here are 'ffa' (free-for-all) and 'teams'
|
||||
# This value is only used if you do not supply a playlist_code (see below).
|
||||
# In that case the default teams or free-for-all playlist gets used.
|
||||
# This value is ignored if you supply a playlist_code (see below).
|
||||
session_type: str = 'ffa'
|
||||
|
||||
# To host your own custom playlists, use the 'share' functionality in the
|
||||
|
|
@ -59,6 +61,10 @@ class ServerConfig:
|
|||
# playlist.
|
||||
playlist_code: Optional[int] = None
|
||||
|
||||
# Alternately, you can embed playlist data here instead of using codes.
|
||||
# Make sure to set session_type to the correct type for the data here.
|
||||
playlist_inline: Optional[List[Dict[str, Any]]] = None
|
||||
|
||||
# Whether to shuffle the playlist or play its games in designated order.
|
||||
playlist_shuffle: bool = True
|
||||
|
||||
|
|
@ -82,12 +88,12 @@ class ServerConfig:
|
|||
# performance)
|
||||
ffa_series_length: int = 24
|
||||
|
||||
# If you provide a custom stats webpage for your server, you can use
|
||||
# this to provide a convenient in-game link to it in the server-browser
|
||||
# beside the server name.
|
||||
# If you have a custom stats webpage for your server, you can use this
|
||||
# to provide a convenient in-game link to it in the server-browser
|
||||
# alongside the server name.
|
||||
# if ${ACCOUNT} is present in the string, it will be replaced by the
|
||||
# currently-signed-in account's id. To fetch info about an account,
|
||||
# your backend server can use the following url:
|
||||
# your back-end server can use the following url:
|
||||
# http://bombsquadgame.com/accountquery?id=ACCOUNT_ID_HERE
|
||||
stats_url: Optional[str] = None
|
||||
|
||||
|
|
@ -107,10 +113,20 @@ class ServerConfig:
|
|||
|
||||
# If present, the server subprocess will shut down immediately if this
|
||||
# amount of time passes with no activity from any players. The server
|
||||
# manager will then spin up a fresh server subprocess if
|
||||
# auto-restart is enabled (the default).
|
||||
# manager will then spin up a fresh server subprocess if auto-restart is
|
||||
# enabled (the default).
|
||||
idle_exit_minutes: Optional[float] = None
|
||||
|
||||
# Should the tutorial be shown at the beginning of games?
|
||||
show_tutorial: bool = False
|
||||
|
||||
# Team names (teams mode only).
|
||||
team_names: Optional[Tuple[str, str]] = None
|
||||
|
||||
# Team colors (teams mode only).
|
||||
team_colors: Optional[Tuple[Tuple[float, float, float],
|
||||
Tuple[float, float, float]]] = None
|
||||
|
||||
# (internal) stress-testing mode.
|
||||
stress_test_players: Optional[int] = None
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -483,6 +483,13 @@ class EliminationGame(ba.TeamGameActivity[Player, Team]):
|
|||
# list then.
|
||||
ba.timer(0, self._update_icons)
|
||||
|
||||
# If the player to leave was the last in spawn order and had
|
||||
# their final turn currently in-progress, mark the survival time
|
||||
# for their team.
|
||||
if self._get_total_team_lives(player.team) == 0:
|
||||
assert self._start_time is not None
|
||||
player.team.survival_seconds = int(ba.time() - self._start_time)
|
||||
|
||||
def _get_total_team_lives(self, team: Team) -> int:
|
||||
return sum(player.lives for player in team.players)
|
||||
|
||||
|
|
|
|||
2
dist/ba_data/python/bastd/mainmenu.py
vendored
2
dist/ba_data/python/bastd/mainmenu.py
vendored
|
|
@ -60,7 +60,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
|
|||
'scale': scale,
|
||||
'position': (0, 10),
|
||||
'vr_depth': -10,
|
||||
'text': '\xa9 2011-2020 Eric Froemling'
|
||||
'text': '\xa9 2011-2021 Eric Froemling'
|
||||
}))
|
||||
|
||||
# Throw up some text that only clients can see so they know that the
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
3
dist/ba_data/python/bastd/ui/confirm.py
vendored
3
dist/ba_data/python/bastd/ui/confirm.py
vendored
|
|
@ -34,8 +34,7 @@ class ConfirmWindow:
|
|||
if cancel_text is None:
|
||||
cancel_text = ba.Lstr(resource='cancelText')
|
||||
height += 40
|
||||
if width < 360:
|
||||
width = 360
|
||||
width = max(width, 360)
|
||||
self._action = action
|
||||
|
||||
# if they provided an origin-widget, scale up from that
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -633,11 +633,8 @@ class ManualGatherTab(GatherTab):
|
|||
from_other_thread=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
err_str = str(exc)
|
||||
|
||||
# FIXME: Should look at exception types here,
|
||||
# not strings.
|
||||
if 'Network is unreachable' in err_str:
|
||||
from efro.net import is_udp_network_error
|
||||
if is_udp_network_error(exc):
|
||||
ba.pushcall(ba.Call(
|
||||
_safe_set_text, self._checking_state_text,
|
||||
ba.Lstr(resource='gatherWindow.'
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ import os
|
|||
import copy
|
||||
import time
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass, asdict
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import ba
|
||||
import _ba
|
||||
from efro.dataclasses import dataclass_from_dict
|
||||
from efro.dataclassio import dataclass_from_dict, dataclass_to_dict
|
||||
from bacommon.net import (PrivateHostingState, PrivateHostingConfig,
|
||||
PrivatePartyConnectResult)
|
||||
from bastd.ui.gather import GatherTab
|
||||
from bastd.ui import getcurrency
|
||||
|
||||
|
|
@ -37,37 +39,6 @@ class State:
|
|||
sub_tab: SubTabType = SubTabType.JOIN
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConnectResult:
|
||||
"""Info about a server we get back when connecting."""
|
||||
error: Optional[str] = None
|
||||
addr: Optional[str] = None
|
||||
port: Optional[int] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class HostingState:
|
||||
"""Our combined state of whether we're hosting, whether we can, etc."""
|
||||
unavailable_error: Optional[str] = None
|
||||
party_code: Optional[str] = None
|
||||
able_to_host: bool = False
|
||||
tickets_to_host_now: int = 0
|
||||
minutes_until_free_host: Optional[float] = None
|
||||
free_host_minutes_remaining: Optional[float] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class HostingConfig:
|
||||
"""Config we provide when hosting."""
|
||||
session_type: str = 'ffa'
|
||||
playlist_name: str = 'Unknown'
|
||||
randomize: bool = False
|
||||
tutorial: bool = False
|
||||
custom_team_names: Optional[List[str]] = None
|
||||
custom_team_colors: Optional[List[List[float]]] = None
|
||||
playlist: Optional[List[Dict[str, Any]]] = None
|
||||
|
||||
|
||||
class PrivateGatherTab(GatherTab):
|
||||
"""The private tab in the gather UI"""
|
||||
|
||||
|
|
@ -75,7 +46,7 @@ class PrivateGatherTab(GatherTab):
|
|||
super().__init__(window)
|
||||
self._container: Optional[ba.Widget] = None
|
||||
self._state: State = State()
|
||||
self._hostingstate = HostingState()
|
||||
self._hostingstate = PrivateHostingState()
|
||||
self._join_sub_tab_text: Optional[ba.Widget] = None
|
||||
self._host_sub_tab_text: Optional[ba.Widget] = None
|
||||
self._update_timer: Optional[ba.Timer] = None
|
||||
|
|
@ -99,7 +70,7 @@ class PrivateGatherTab(GatherTab):
|
|||
self._hostingconfig = self._build_hosting_config()
|
||||
except Exception:
|
||||
ba.print_exception('Error building hosting config')
|
||||
self._hostingconfig = HostingConfig()
|
||||
self._hostingconfig = PrivateHostingConfig()
|
||||
|
||||
def on_activate(
|
||||
self,
|
||||
|
|
@ -178,10 +149,11 @@ class PrivateGatherTab(GatherTab):
|
|||
|
||||
return self._container
|
||||
|
||||
def _build_hosting_config(self) -> HostingConfig:
|
||||
def _build_hosting_config(self) -> PrivateHostingConfig:
|
||||
# pylint: disable=too-many-branches
|
||||
from bastd.ui.playlist import PlaylistTypeVars
|
||||
from ba.internal import filter_playlist
|
||||
hcfg = HostingConfig()
|
||||
hcfg = PrivateHostingConfig()
|
||||
cfg = ba.app.config
|
||||
sessiontypestr = cfg.get('Private Party Host Session Type', 'ffa')
|
||||
if not isinstance(sessiontypestr, str):
|
||||
|
|
@ -205,10 +177,13 @@ class PrivateGatherTab(GatherTab):
|
|||
if playlist_name == '__default__' else
|
||||
playlist_name)
|
||||
|
||||
if playlist_name == '__default__':
|
||||
playlist: Optional[List[Dict[str, Any]]] = None
|
||||
if playlist_name != '__default__':
|
||||
playlist = (cfg.get(f'{pvars.config_name} Playlists',
|
||||
{}).get(playlist_name))
|
||||
if playlist is None:
|
||||
playlist = pvars.get_default_list_call()
|
||||
else:
|
||||
playlist = cfg[f'{pvars.config_name} Playlists'][playlist_name]
|
||||
|
||||
hcfg.playlist = filter_playlist(playlist, sessiontype)
|
||||
|
||||
randomize = cfg.get(f'{pvars.config_name} Playlist Randomize')
|
||||
|
|
@ -218,12 +193,29 @@ class PrivateGatherTab(GatherTab):
|
|||
|
||||
tutorial = cfg.get('Show Tutorial')
|
||||
if not isinstance(tutorial, bool):
|
||||
tutorial = False
|
||||
tutorial = True
|
||||
hcfg.tutorial = tutorial
|
||||
|
||||
if hcfg.session_type == 'teams':
|
||||
hcfg.custom_team_names = copy.copy(cfg.get('Custom Team Names'))
|
||||
hcfg.custom_team_colors = copy.copy(cfg.get('Custom Team Colors'))
|
||||
ctn: Optional[List[str]] = cfg.get('Custom Team Names')
|
||||
if ctn is not None:
|
||||
if (isinstance(ctn, (list, tuple)) and len(ctn) == 2
|
||||
and all(isinstance(x, str) for x in ctn)):
|
||||
hcfg.custom_team_names = (ctn[0], ctn[1])
|
||||
else:
|
||||
print(f'Found invalid custom-team-names data: {ctn}')
|
||||
|
||||
ctc: Optional[List[List[float]]] = cfg.get('Custom Team Colors')
|
||||
if ctc is not None:
|
||||
if (isinstance(ctc, (list, tuple)) and len(ctc) == 2
|
||||
and all(isinstance(x, (list, tuple)) for x in ctc)
|
||||
and all(len(x) == 3 for x in ctc)):
|
||||
hcfg.custom_team_colors = ((ctc[0][0], ctc[0][1],
|
||||
ctc[0][2]),
|
||||
(ctc[1][0], ctc[1][1],
|
||||
ctc[1][2]))
|
||||
else:
|
||||
print(f'Found invalid custom-team-colors data: {ctc}')
|
||||
|
||||
return hcfg
|
||||
|
||||
|
|
@ -297,13 +289,15 @@ class PrivateGatherTab(GatherTab):
|
|||
if not self._container:
|
||||
return
|
||||
|
||||
state: Optional[HostingState] = None
|
||||
state: Optional[PrivateHostingState] = None
|
||||
if result is not None:
|
||||
self._debug_server_comm('got private party state response')
|
||||
try:
|
||||
state = dataclass_from_dict(HostingState, result)
|
||||
state = dataclass_from_dict(PrivateHostingState,
|
||||
result,
|
||||
discard_unknown_attrs=True)
|
||||
except Exception:
|
||||
pass
|
||||
ba.print_exception('Got invalid PrivateHostingState data')
|
||||
else:
|
||||
self._debug_server_comm('private party state response errored')
|
||||
|
||||
|
|
@ -788,6 +782,8 @@ class PrivateGatherTab(GatherTab):
|
|||
ba.playsound(ba.getsound('error'))
|
||||
return
|
||||
|
||||
ba.playsound(ba.getsound('click01'))
|
||||
|
||||
# If we're not hosting, start.
|
||||
if self._hostingstate.party_code is None:
|
||||
|
||||
|
|
@ -808,7 +804,8 @@ class PrivateGatherTab(GatherTab):
|
|||
_ba.add_transaction(
|
||||
{
|
||||
'type': 'PRIVATE_PARTY_START',
|
||||
'config': asdict(self._hostingconfig)
|
||||
'config': dataclass_to_dict(self._hostingconfig),
|
||||
'region_pings': ba.app.net.region_pings,
|
||||
},
|
||||
callback=ba.WeakCall(self._hosting_state_response))
|
||||
_ba.run_transactions()
|
||||
|
|
@ -844,7 +841,9 @@ class PrivateGatherTab(GatherTab):
|
|||
self._connect_press_time = None
|
||||
if result is None:
|
||||
raise RuntimeError()
|
||||
cresult = dataclass_from_dict(ConnectResult, result)
|
||||
cresult = dataclass_from_dict(PrivatePartyConnectResult,
|
||||
result,
|
||||
discard_unknown_attrs=True)
|
||||
if cresult.error is not None:
|
||||
self._debug_server_comm('got error connect response')
|
||||
ba.screenmessage(
|
||||
|
|
|
|||
46
dist/ba_data/python/bastd/ui/gather/publictab.py
vendored
46
dist/ba_data/python/bastd/ui/gather/publictab.py
vendored
|
|
@ -219,9 +219,9 @@ class AddrFetchThread(threading.Thread):
|
|||
sock.close()
|
||||
ba.pushcall(ba.Call(self._call, val), from_other_thread=True)
|
||||
except Exception as exc:
|
||||
from efro.net import is_udp_network_error
|
||||
# Ignore expected network errors; log others.
|
||||
import errno
|
||||
if isinstance(exc, OSError) and exc.errno == errno.ENETUNREACH:
|
||||
if is_udp_network_error(exc):
|
||||
pass
|
||||
else:
|
||||
ba.print_exception()
|
||||
|
|
@ -238,8 +238,6 @@ class PingThread(threading.Thread):
|
|||
self._call = call
|
||||
|
||||
def run(self) -> None:
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
ba.app.ping_thread_count += 1
|
||||
sock: Optional[socket.socket] = None
|
||||
try:
|
||||
|
|
@ -272,39 +270,12 @@ class PingThread(threading.Thread):
|
|||
ba.pushcall(ba.Call(self._call, self._address, self._port,
|
||||
ping if accessible else None),
|
||||
from_other_thread=True)
|
||||
except ConnectionRefusedError:
|
||||
# Fine, server; sorry we pinged you. Hmph.
|
||||
pass
|
||||
except OSError as exc:
|
||||
import errno
|
||||
|
||||
# Ignore harmless errors.
|
||||
if exc.errno in {
|
||||
errno.EHOSTUNREACH, errno.ENETUNREACH, errno.EINVAL,
|
||||
errno.EPERM, errno.EACCES
|
||||
}:
|
||||
except Exception as exc:
|
||||
from efro.net import is_udp_network_error
|
||||
if is_udp_network_error(exc):
|
||||
pass
|
||||
elif exc.errno == 10022:
|
||||
# Windows 'invalid argument' error.
|
||||
pass
|
||||
elif exc.errno == 10051:
|
||||
# Windows 'a socket operation was attempted
|
||||
# to an unreachable network' error.
|
||||
pass
|
||||
elif exc.errno == errno.EADDRNOTAVAIL:
|
||||
if self._port == 0:
|
||||
# This has happened. Ignore.
|
||||
pass
|
||||
elif ba.do_once():
|
||||
print(f'Got EADDRNOTAVAIL on gather ping'
|
||||
f' for addr {self._address}'
|
||||
f' port {self._port}.')
|
||||
else:
|
||||
ba.print_exception(
|
||||
f'Error on gather ping '
|
||||
f'(errno={exc.errno})', once=True)
|
||||
except Exception:
|
||||
ba.print_exception('Error on gather ping', once=True)
|
||||
ba.print_exception('Error on gather ping', once=True)
|
||||
finally:
|
||||
try:
|
||||
if sock is not None:
|
||||
|
|
@ -1261,10 +1232,7 @@ class PublicGatherTab(GatherTab):
|
|||
self._have_user_selected_row = True
|
||||
|
||||
def _on_max_public_party_size_minus_press(self) -> None:
|
||||
val = _ba.get_public_party_max_size()
|
||||
val -= 1
|
||||
if val < 1:
|
||||
val = 1
|
||||
val = max(1, _ba.get_public_party_max_size() - 1)
|
||||
_ba.set_public_party_max_size(val)
|
||||
ba.textwidget(edit=self._host_max_party_size_value, text=str(val))
|
||||
|
||||
|
|
|
|||
2
dist/ba_data/python/bastd/ui/party.py
vendored
2
dist/ba_data/python/bastd/ui/party.py
vendored
|
|
@ -372,7 +372,7 @@ class PartyWindow(ba.Window):
|
|||
cfg.apply_and_commit()
|
||||
self._update()
|
||||
else:
|
||||
print('unhandled popup type: ' + str(self._popup_type))
|
||||
print(f'unhandled popup type: {self._popup_type}')
|
||||
|
||||
def popup_menu_closing(self, popup_window: popup.PopupWindow) -> None:
|
||||
"""Called when the popup is closing."""
|
||||
|
|
|
|||
BIN
dist/ba_data/python/efro/__pycache__/dataclassio.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/efro/__pycache__/dataclassio.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python/efro/__pycache__/net.cpython-38.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python/efro/__pycache__/net.cpython-38.opt-1.pyc
vendored
Normal file
Binary file not shown.
1028
dist/ba_data/python/efro/dataclassio.py
vendored
Normal file
1028
dist/ba_data/python/efro/dataclassio.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
16
dist/ba_data/python/efro/entity/_base.py
vendored
16
dist/ba_data/python/efro/entity/_base.py
vendored
|
|
@ -7,6 +7,8 @@ from __future__ import annotations
|
|||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from efro.util import enum_by_value
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Type
|
||||
|
||||
|
|
@ -17,14 +19,24 @@ def dict_key_to_raw(key: Any, keytype: Type) -> Any:
|
|||
raise TypeError(
|
||||
f'Invalid key type; expected {keytype}, got {type(key)}.')
|
||||
if issubclass(keytype, Enum):
|
||||
return key.value
|
||||
val = key.value
|
||||
# We convert int enums to string since that is what firestore supports.
|
||||
if isinstance(val, int):
|
||||
val = str(val)
|
||||
return val
|
||||
return key
|
||||
|
||||
|
||||
def dict_key_from_raw(key: Any, keytype: Type) -> Any:
|
||||
"""Given internal key, filter to world visible type."""
|
||||
if issubclass(keytype, Enum):
|
||||
return keytype(key)
|
||||
# We store all enum keys as strings; if the enum uses
|
||||
# int keys, convert back.
|
||||
for enumval in keytype:
|
||||
if isinstance(enumval.value, int):
|
||||
return enum_by_value(keytype, int(key))
|
||||
break
|
||||
return enum_by_value(keytype, key)
|
||||
return key
|
||||
|
||||
|
||||
|
|
|
|||
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