1.6.5 before python3.9

This commit is contained in:
imayushsaini 2021-10-26 23:24:50 +05:30
parent 7cb8323a5d
commit 162b04b6b5
296 changed files with 6445 additions and 491 deletions

View file

@ -29,8 +29,8 @@ from ba._coopgame import CoopGameActivity
from ba._coopsession import CoopSession
from ba._dependency import (Dependency, DependencyComponent, DependencySet,
AssetPackage)
from ba._enums import (TimeType, Permission, TimeFormat, SpecialChar,
InputType, UIScale)
from ba._generated.enums import (TimeType, Permission, TimeFormat, SpecialChar,
InputType, UIScale)
from ba._error import (
print_exception, print_error, ContextError, NotFoundError,
PlayerNotFoundError, SessionPlayerNotFoundError, NodeNotFoundError,

View file

@ -121,7 +121,7 @@ class AccountSubsystem:
def cache_tournament_info(self, info: Any) -> None:
"""(internal)"""
from ba._enums import TimeType, TimeFormat
from ba._generated.enums import TimeType, TimeFormat
for entry in info:
cache_entry = self.tournament_info[entry['tournamentID']] = (
copy.deepcopy(entry))
@ -206,7 +206,7 @@ class AccountSubsystem:
def show_post_purchase_message(self) -> None:
"""(internal)"""
from ba._language import Lstr
from ba._enums import TimeType
from ba._generated.enums import TimeType
cur_time = _ba.time(TimeType.REAL)
if (self.last_post_purchase_message_time is None
or cur_time - self.last_post_purchase_message_time > 3.0):
@ -237,7 +237,7 @@ class AccountSubsystem:
def add_pending_promo_code(self, code: str) -> None:
"""(internal)"""
from ba._language import Lstr
from ba._enums import TimeType
from ba._generated.enums import TimeType
# If we're not signed in, queue up the code to run the next time we
# are and issue a warning if we haven't signed in within the next

View file

@ -388,7 +388,7 @@ class AchievementSubsystem:
def _test(self) -> None:
"""For testing achievement animations."""
from ba._enums import TimeType
from ba._generated.enums import TimeType
def testcall1() -> None:
self.achievements[0].announce_completion()
@ -489,7 +489,7 @@ class Achievement:
def announce_completion(self, sound: bool = True) -> None:
"""Kick off an announcement for this achievement's completion."""
from ba._enums import TimeType
from ba._generated.enums import TimeType
app = _ba.app
# Even though there are technically achievements when we're not
@ -619,7 +619,7 @@ class Achievement:
"""
# pylint: disable=cyclic-import
from ba._language import Lstr
from ba._enums import SpecialChar
from ba._generated.enums import SpecialChar
from ba._coopsession import CoopSession
from bastd.actor.image import Image
from bastd.actor.text import Text
@ -923,7 +923,7 @@ class Achievement:
from ba._general import WeakCall
from ba._language import Lstr
from ba._messages import DieMessage
from ba._enums import TimeType, SpecialChar
from ba._generated.enums import TimeType, SpecialChar
app = _ba.app
app.ach.last_achievement_display_time = _ba.time(TimeType.REAL)

View file

@ -112,6 +112,11 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# transitions).
inherits_tint = False
# Whether players should be allowed to join in the middle of this
# activity. Note that Sessions may not allow mid-activity-joins even
# if the activity says its ok.
allow_mid_activity_joins: bool = True
# If the activity fades or transitions in, it should set the length of
# time here so that previous activities will be kept alive for that
# long (avoiding 'holes' in the screen)
@ -275,7 +280,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
(internal)
"""
from ba._enums import TimeType
from ba._generated.enums import TimeType
# Create a real-timer that watches a weak-ref of this activity
# and reports any lingering references keeping it alive.

View file

@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
import _ba
from ba._activity import Activity
from ba._music import setmusic, MusicType
from ba._enums import InputType, UIScale
from ba._generated.enums import InputType, UIScale
# False-positive from pylint due to our class-generics-filter.
from ba._player import EmptyPlayer # pylint: disable=W0611
from ba._team import EmptyTeam # pylint: disable=W0611

View file

@ -7,15 +7,15 @@ from __future__ import annotations
import weakref
from typing import TYPE_CHECKING, TypeVar, overload
import _ba
from ba._messages import DieMessage, DeathType, OutOfBoundsMessage, UNHANDLED
from ba._error import print_exception, ActivityNotFoundError
import _ba
if TYPE_CHECKING:
from typing import Any, Optional, Literal
import ba
T = TypeVar('T', bound='Actor')
TA = TypeVar('TA', bound='Actor')
class Actor:
@ -94,7 +94,7 @@ class Actor:
return UNHANDLED
def autoretain(self: T) -> T:
def autoretain(self: TA) -> TA:
"""Keep this Actor alive without needing to hold a reference to it.
This keeps the ba.Actor in existence by storing a reference to it

View file

@ -33,7 +33,7 @@ class AdsSubsystem:
def do_remove_in_game_ads_message(self) -> None:
"""(internal)"""
from ba._language import Lstr
from ba._enums import TimeType
from ba._generated.enums import TimeType
# Print this message once every 10 minutes at most.
tval = _ba.time(TimeType.REAL)
@ -70,7 +70,7 @@ class AdsSubsystem:
# pylint: disable=too-many-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals
from ba._enums import TimeType
from ba._generated.enums import TimeType
app = _ba.app
show = True

View file

@ -275,6 +275,7 @@ class App:
self.pro_sale_start_val: Optional[int] = None
self.delegate: Optional[ba.AppDelegate] = None
self._asyncio_timer: Optional[ba.Timer] = None
def on_app_launch(self) -> None:
"""Runs after the app finishes bootstrapping.
@ -290,9 +291,10 @@ class App:
from bastd import appdelegate
from bastd import maps as stdmaps
from bastd.actor import spazappearance
from ba._enums import TimeType
from ba._generated.enums import TimeType
import custom_hooks
custom_hooks.on_app_launch()
cfg = self.config
self.delegate = appdelegate.AppDelegate()
@ -582,7 +584,8 @@ class App:
"""
import urllib.request
try:
val = urllib.request.urlopen('https://example.com').read()
with urllib.request.urlopen('https://example.com') as url:
val = url.read()
print('HTTPS TEST SUCCESS', len(val))
except Exception as exc:
print('HTTPS TEST FAIL:', exc)

View file

@ -97,7 +97,7 @@ def read_config() -> Tuple[AppConfig, bool]:
"""Read the game config."""
import os
import json
from ba._enums import TimeType
from ba._generated.enums import TimeType
config_file_healthy = False
@ -107,7 +107,7 @@ def read_config() -> Tuple[AppConfig, bool]:
config_contents = ''
try:
if os.path.exists(config_file_path):
with open(config_file_path) as infile:
with open(config_file_path, encoding='utf-8') as infile:
config_contents = infile.read()
config = AppConfig(json.loads(config_contents))
else:
@ -140,7 +140,7 @@ def read_config() -> Tuple[AppConfig, bool]:
prev_path = config_file_path + '.prev'
try:
if os.path.exists(prev_path):
with open(prev_path) as infile:
with open(prev_path, encoding='utf-8') as infile:
config_contents = infile.read()
config = AppConfig(json.loads(config_contents))
else:

View file

@ -57,7 +57,7 @@ def handle_log() -> None:
after a short bit if desired.
"""
from ba._net import master_server_post
from ba._enums import TimeType
from ba._generated.enums import TimeType
app = _ba.app
app.log_have_new = True
if not app.log_upload_timer_started:
@ -124,7 +124,7 @@ def handle_leftover_log_file() -> None:
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:
with open(_ba.get_log_file_path(), encoding='utf-8') as infile:
info = json.loads(infile.read())
infile.close()
do_send = should_submit_debug_info()
@ -225,7 +225,7 @@ def print_live_object_warnings(when: Any,
def print_corrupt_file_error() -> None:
"""Print an error if a corrupt file is found."""
from ba._general import Call
from ba._enums import TimeType
from ba._generated.enums import TimeType
_ba.timer(2.0,
lambda: _ba.screenmessage(
_ba.app.lang.get_resource('internal.corruptFileText').

View file

@ -4,7 +4,8 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict
from dataclasses import dataclass, field
from pathlib import Path
import threading
import urllib.request
@ -14,21 +15,29 @@ import time
import os
import sys
from efro import entity
from typing_extensions import Annotated
from efro.dataclassio import (ioprepped, IOAttrs, dataclass_from_json,
dataclass_to_json)
if TYPE_CHECKING:
from bacommon.assets import AssetPackageFlavor
from typing import List
class FileValue(entity.CompoundValue):
@ioprepped
@dataclass
class FileValue:
"""State for an individual file."""
class State(entity.Entity):
@ioprepped
@dataclass
class State:
"""Holds all persistent state for the asset-manager."""
files = entity.CompoundDictField('files', str, FileValue())
files: Annotated[Dict[str, FileValue],
IOAttrs('files')] = field(default_factory=dict)
class AssetManager:
@ -101,8 +110,8 @@ class AssetManager:
try:
state_path = self.state_path
if state_path.exists():
with open(self.state_path) as infile:
self._state = State.from_json_str(infile.read())
with open(self.state_path, encoding='utf-8') as infile:
self._state = dataclass_from_json(State, infile.read())
return
except Exception:
logging.exception('Error loading existing AssetManager state')
@ -113,8 +122,8 @@ class AssetManager:
print('ASSET-MANAGER SAVING STATE')
try:
with open(self.state_path, 'w') as outfile:
outfile.write(self._state.to_json_str())
with open(self.state_path, 'w', encoding='utf-8') as outfile:
outfile.write(dataclass_to_json(self._state))
except Exception:
logging.exception('Error writing AssetManager state')

71
dist/ba_data/python/ba/_asyncio.py vendored Normal file
View file

@ -0,0 +1,71 @@
# Released under the MIT License. See LICENSE for details.
#
"""Asyncio related functionality.
Exploring the idea of allowing Python coroutines to run gracefully
besides our internal event loop. They could prove useful for networking
operations or possibly game logic.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import asyncio
if TYPE_CHECKING:
from typing import Optional
import ba
# Our timer and event loop for the ballistica game thread.
_asyncio_timer: Optional[ba.Timer] = None
_asyncio_event_loop: Optional[asyncio.AbstractEventLoop] = None
def setup_asyncio() -> None:
"""Setup asyncio functionality for our game thread."""
# pylint: disable=global-statement
import _ba
from ba._generated.enums import TimeType
assert _ba.in_game_thread()
# Create our event-loop. We don't expect there to be one
# running on this thread before we do.
try:
asyncio.get_running_loop()
print('Found running asyncio loop; unexpected.')
except RuntimeError:
pass
global _asyncio_event_loop # pylint: disable=invalid-name
_asyncio_event_loop = asyncio.new_event_loop()
# Ideally we should integrate asyncio into our C++ Thread class's
# low level event loop so that asyncio timers/sockets/etc. could
# be true first-class citizens. For now, though, we can explicitly
# pump an asyncio loop periodically which gets us a decent
# approximation of that, which should be good enough for
# all but extremely time sensitive uses.
# See https://stackoverflow.com/questions/29782377/
# is-it-possible-to-run-only-a-single-step-of-the-asyncio-event-loop
def run_cycle() -> None:
assert _asyncio_event_loop is not None
_asyncio_event_loop.call_soon(_asyncio_event_loop.stop)
_asyncio_event_loop.run_forever()
global _asyncio_timer # pylint: disable=invalid-name
_asyncio_timer = _ba.Timer(1.0 / 30.0,
run_cycle,
timetype=TimeType.REAL,
repeat=True)
async def aio_test() -> None:
print('TEST AIO TASK STARTING')
assert _asyncio_event_loop is not None
assert asyncio.get_running_loop() is _asyncio_event_loop
await asyncio.sleep(2.0)
print('TEST AIO TASK ENDING')
if bool(False):
_asyncio_event_loop.create_task(aio_test())

View file

@ -57,7 +57,7 @@ def run_stress_test(playlist_type: str = 'Random',
"""Run a stress test."""
from ba import modutils
from ba._general import Call
from ba._enums import TimeType
from ba._generated.enums import TimeType
_ba.screenmessage(
'Beginning stress test.. use '
"'End Game' to stop testing.",
@ -93,7 +93,7 @@ def start_stress_test(args: Dict[str, Any]) -> None:
from ba._general import Call
from ba._dualteamsession import DualTeamSession
from ba._freeforallsession import FreeForAllSession
from ba._enums import TimeType, TimeFormat
from ba._generated.enums import TimeType, TimeFormat
appconfig = _ba.app.config
playlist_type = args['playlist_type']
if playlist_type == 'Random':
@ -127,7 +127,7 @@ def start_stress_test(args: Dict[str, Any]) -> None:
def _reset_stress_test(args: Dict[str, Any]) -> None:
from ba._general import Call
from ba._enums import TimeType
from ba._generated.enums import TimeType
_ba.set_stress_testing(False, args['player_count'])
_ba.screenmessage('Resetting stress test...')
session = _ba.get_foreground_host_session()
@ -144,7 +144,7 @@ def run_gpu_benchmark() -> None:
def run_media_reload_benchmark() -> None:
"""Kick off a benchmark to test media reloading speeds."""
from ba._general import Call
from ba._enums import TimeType
from ba._generated.enums import TimeType
_ba.reload_media()
_ba.show_progress_bar()

View file

@ -70,7 +70,7 @@ class CoopGameActivity(GameActivity[PlayerType, TeamType]):
from efro.util import asserttype
from ba._gameutils import timestring, animate
from ba._nodeactor import NodeActor
from ba._enums import TimeFormat
from ba._generated.enums import TimeFormat
display_type = self.get_score_type()
if scores is not None:

View file

@ -90,6 +90,16 @@ class CoopSession(Session):
"""Get the game instance currently being played."""
return self._current_game_instance
def should_allow_mid_activity_joins(self, activity: ba.Activity) -> bool:
# pylint: disable=cyclic-import
from ba._gameactivity import GameActivity
# Disallow any joins in the middle of the game.
if isinstance(activity, GameActivity):
return False
return True
def _update_on_deck_game_instances(self) -> None:
# pylint: disable=cyclic-import
from ba._gameactivity import GameActivity
@ -156,35 +166,44 @@ class CoopSession(Session):
from ba._general import WeakCall
super().on_player_leave(sessionplayer)
# If all our players leave we wanna quit out of the session.
_ba.timer(2.0, WeakCall(self._end_session_if_empty))
_ba.timer(2.0, WeakCall(self._handle_empty_activity))
def _end_session_if_empty(self) -> None:
def _handle_empty_activity(self) -> None:
"""Handle cases where all players have left the current activity."""
from ba._gameactivity import GameActivity
activity = self.getactivity()
if activity is None:
return # Hmm what should we do in this case?
# If there's still players in the current activity, we're good.
# If there are still players in the current activity, we're good.
if activity.players:
return
# If there's *no* players left in the current activity but there *is*
# in the session, restart the activity to pull them into the game
# (or quit if they're just in the lobby).
# If there are *not* players in the current activity but there
# *are* in the session:
if not activity.players and self.sessionplayers:
# Special exception for tourney games; don't auto-restart these.
if self.tournament_id is not None:
self.end()
else:
# Don't restart joining activities; this probably means there's
# someone with a chooser up in that case.
if not activity.is_joining_activity:
# If we're in a game, we should restart to pull in players
# currently waiting in the session.
if isinstance(activity, GameActivity):
# Never restart tourney games however; just end the session
# if all players are gone.
if self.tournament_id is not None:
self.end()
else:
self.restart()
# Hmm; no players anywhere. lets just end the session.
# Hmm; no players anywhere. Let's end the entire session if we're
# running a GUI (or just the current game if we're running headless).
else:
self.end()
if not _ba.app.headless_mode:
self.end()
else:
if isinstance(activity, GameActivity):
with _ba.Context(activity):
activity.end_game()
def _on_tournament_restart_menu_press(
self, resume_callback: Callable[[], Any]) -> None:
@ -248,12 +267,14 @@ class CoopSession(Session):
else:
outcome = '' if results is None else results.get('outcome', '')
# If at any point we have no in-game players, quit out of the session
# (this can happen if someone leaves in the tutorial for instance).
active_players = [p for p in self.sessionplayers if p.in_game]
if not active_players:
self.end()
return
# If we're running with a gui and at any point we have no
# in-game players, quit out of the session (this can happen if
# someone leaves in the tutorial for instance).
if not _ba.app.headless_mode:
active_players = [p for p in self.sessionplayers if p.in_game]
if not active_players:
self.end()
return
# If we're in a between-round activity or a restart-activity,
# hop into a round.

View file

@ -385,7 +385,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
# pylint: disable=cyclic-import
from bastd.ui.continues import ContinuesWindow
from ba._coopsession import CoopSession
from ba._enums import TimeType
from ba._generated.enums import TimeType
try:
if _ba.get_account_misc_read_val('enableContinues', False):
@ -653,7 +653,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
def _show_tip(self) -> None:
# pylint: disable=too-many-locals
from ba._gameutils import animate, GameTip
from ba._enums import SpecialChar
from ba._generated.enums import SpecialChar
# If there's any tips left on the list, display one.
if self.tips:
@ -1009,7 +1009,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
If the time-limit expires, end_game() will be called.
"""
from ba._nodeactor import NodeActor
from ba._enums import TimeType
from ba._generated.enums import TimeType
if duration <= 0.0:
return
self._tournament_time_limit = int(duration)

View file

@ -107,7 +107,7 @@ class GameResults:
"""
from ba._gameutils import timestring
from ba._language import Lstr
from ba._enums import TimeFormat
from ba._generated.enums import TimeFormat
from ba._score import ScoreType
if not self._game_set:
raise RuntimeError("Can't get team-score-str until game is set.")

View file

@ -8,7 +8,7 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING
import _ba
from ba._enums import TimeType, TimeFormat, SpecialChar, UIScale
from ba._generated.enums import TimeType, TimeFormat, SpecialChar, UIScale
from ba._error import ActivityNotFoundError
if TYPE_CHECKING:
@ -301,6 +301,7 @@ def timestring(timeval: float,
# We add seconds if its non-zero *or* we haven't added anything else.
if centi:
# pylint: disable=consider-using-f-string
sval = (timeval / 1000.0 % 60.0)
if sval >= 0.005 or not bits:
bits.append('${S}')

View file

@ -13,7 +13,7 @@ from typing import TYPE_CHECKING, TypeVar, Protocol
from efro.terminal import Clr
import _ba
from ba._error import print_error, print_exception
from ba._enums import TimeType
from ba._generated.enums import TimeType
if TYPE_CHECKING:
from types import FrameType

View file

@ -0,0 +1,2 @@
# Released under the MIT License. See LICENSE for details.
#

View file

@ -0,0 +1,198 @@
# Released under the MIT License. See LICENSE for details.
"""Enum vals generated by batools.pythonenumsmodule; do not edit by hand."""
from enum import Enum
class InputType(Enum):
"""Types of input a controller can send to the game.
Category: Enums
"""
UP_DOWN = 2
LEFT_RIGHT = 3
JUMP_PRESS = 4
JUMP_RELEASE = 5
PUNCH_PRESS = 6
PUNCH_RELEASE = 7
BOMB_PRESS = 8
BOMB_RELEASE = 9
PICK_UP_PRESS = 10
PICK_UP_RELEASE = 11
RUN = 12
FLY_PRESS = 13
FLY_RELEASE = 14
START_PRESS = 15
START_RELEASE = 16
HOLD_POSITION_PRESS = 17
HOLD_POSITION_RELEASE = 18
LEFT_PRESS = 19
LEFT_RELEASE = 20
RIGHT_PRESS = 21
RIGHT_RELEASE = 22
UP_PRESS = 23
UP_RELEASE = 24
DOWN_PRESS = 25
DOWN_RELEASE = 26
class UIScale(Enum):
"""The overall scale the UI is being rendered for. Note that this is
independent of pixel resolution. For example, a phone and a desktop PC
might render the game at similar pixel resolutions but the size they
display content at will vary significantly.
Category: Enums
'large' is used for devices such as desktop PCs where fine details can
be clearly seen. UI elements are generally smaller on the screen
and more content can be seen at once.
'medium' is used for devices such as tablets, TVs, or VR headsets.
This mode strikes a balance between clean readability and amount of
content visible.
'small' is used primarily for phones or other small devices where
content needs to be presented as large and clear in order to remain
readable from an average distance.
"""
LARGE = 0
MEDIUM = 1
SMALL = 2
class TimeType(Enum):
"""Specifies the type of time for various operations to target/use.
Category: Enums
'sim' time is the local simulation time for an activity or session.
It can proceed at different rates depending on game speed, stops
for pauses, etc.
'base' is the baseline time for an activity or session. It proceeds
consistently regardless of game speed or pausing, but may stop during
occurrences such as network outages.
'real' time is mostly based on clock time, with a few exceptions. It may
not advance while the app is backgrounded for instance. (the engine
attempts to prevent single large time jumps from occurring)
"""
SIM = 0
BASE = 1
REAL = 2
class TimeFormat(Enum):
"""Specifies the format time values are provided in.
Category: Enums
"""
SECONDS = 0
MILLISECONDS = 1
class Permission(Enum):
"""Permissions that can be requested from the OS.
Category: Enums
"""
STORAGE = 0
class SpecialChar(Enum):
"""Special characters the game can print.
Category: Enums
"""
DOWN_ARROW = 0
UP_ARROW = 1
LEFT_ARROW = 2
RIGHT_ARROW = 3
TOP_BUTTON = 4
LEFT_BUTTON = 5
RIGHT_BUTTON = 6
BOTTOM_BUTTON = 7
DELETE = 8
SHIFT = 9
BACK = 10
LOGO_FLAT = 11
REWIND_BUTTON = 12
PLAY_PAUSE_BUTTON = 13
FAST_FORWARD_BUTTON = 14
DPAD_CENTER_BUTTON = 15
OUYA_BUTTON_O = 16
OUYA_BUTTON_U = 17
OUYA_BUTTON_Y = 18
OUYA_BUTTON_A = 19
OUYA_LOGO = 20
LOGO = 21
TICKET = 22
GOOGLE_PLAY_GAMES_LOGO = 23
GAME_CENTER_LOGO = 24
DICE_BUTTON1 = 25
DICE_BUTTON2 = 26
DICE_BUTTON3 = 27
DICE_BUTTON4 = 28
GAME_CIRCLE_LOGO = 29
PARTY_ICON = 30
TEST_ACCOUNT = 31
TICKET_BACKING = 32
TROPHY1 = 33
TROPHY2 = 34
TROPHY3 = 35
TROPHY0A = 36
TROPHY0B = 37
TROPHY4 = 38
LOCAL_ACCOUNT = 39
ALIBABA_LOGO = 40
FLAG_UNITED_STATES = 41
FLAG_MEXICO = 42
FLAG_GERMANY = 43
FLAG_BRAZIL = 44
FLAG_RUSSIA = 45
FLAG_CHINA = 46
FLAG_UNITED_KINGDOM = 47
FLAG_CANADA = 48
FLAG_INDIA = 49
FLAG_JAPAN = 50
FLAG_FRANCE = 51
FLAG_INDONESIA = 52
FLAG_ITALY = 53
FLAG_SOUTH_KOREA = 54
FLAG_NETHERLANDS = 55
FEDORA = 56
HAL = 57
CROWN = 58
YIN_YANG = 59
EYE_BALL = 60
SKULL = 61
HEART = 62
DRAGON = 63
HELMET = 64
MUSHROOM = 65
NINJA_STAR = 66
VIKING_HELMET = 67
MOON = 68
SPIDER = 69
FIREBALL = 70
FLAG_UNITED_ARAB_EMIRATES = 71
FLAG_QATAR = 72
FLAG_EGYPT = 73
FLAG_KUWAIT = 74
FLAG_ALGERIA = 75
FLAG_SAUDI_ARABIA = 76
FLAG_MALAYSIA = 77
FLAG_CZECH_REPUBLIC = 78
FLAG_AUSTRALIA = 79
FLAG_SINGAPORE = 80
OCULUS_LOGO = 81
STEAM_LOGO = 82
NVIDIA_LOGO = 83
FLAG_IRAN = 84
FLAG_POLAND = 85
FLAG_ARGENTINA = 86
FLAG_PHILIPPINES = 87
FLAG_CHILE = 88
MIKIROG = 89

View file

@ -22,6 +22,19 @@ if TYPE_CHECKING:
import ba
def finish_bootstrapping() -> None:
"""Do final bootstrapping related bits."""
from ba._asyncio import setup_asyncio
assert _ba.in_game_thread()
# Kick off our asyncio event handling, allowing us to use coroutines
# in our game thread alongside our internal event handling.
setup_asyncio()
# Ok, bootstrapping is done; time to get the show started.
_ba.app.on_app_launch()
def reset_to_main_menu() -> None:
"""Reset the game to the main menu gracefully."""
_ba.app.return_to_main_menu_session_gracefully()
@ -316,9 +329,46 @@ 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 get_not_allowed_to_kick() -> str:
""" account_ids who cant start kick vote
return comma (,) seperated account ids as string.
"""
return "pb-IF4TVWwZUQ==,pb-IF4T=="
def kick_vote_started(by:str,to:str) -> None:
"""
get account ids of who started kick vote for whom ,
do what ever u want logging to files , whatever.
"""
print(by+">"+to)
def get_server_name() -> str:
""" return what u want show for name of your host Id
"""
return "BCS"
def get_host_name() -> str:
""" return device id of your host , to be view by id revealer
"""
return "bySmoothy"
def is_open_kick_vote() -> bool:
""" return True if want to use transparent kick vote system , to make kick vote starter name public .
or return False if want to use old default kick vote sys.
"""
return True
def on_kicked(account_id:str) -> None:
""" oh damm ..this player kicked by votes , no one likes him :( , 2 min silence sleep(1000*60*2)
"""
print(account_id+" kicked ...sad")
def on_kick_vote_end() -> None:
""" kick vote terminated , either player left the server , or time over
"""
print("kick vote end")
def local_chat_message(msg: str) -> None:
if (_ba.app.ui.party_window is not None

View file

@ -37,7 +37,7 @@ class LanguageSubsystem:
# We don't yet support full unicode display on windows or linux :-(.
if (language in {
'Chinese', 'ChineseTraditional', 'Persian', 'Korean', 'Arabic',
'Hindi', 'Vietnamese'
'Hindi', 'Vietnamese', 'Thai'
} and not _ba.can_display_full_unicode()):
return False
return True
@ -79,6 +79,7 @@ class LanguageSubsystem:
'ar': 'Arabic',
'zh': 'Chinese',
'tr': 'Turkish',
'th': 'Thai',
'id': 'Indonesian',
'sr': 'Serbian',
'uk': 'Ukrainian',
@ -161,7 +162,8 @@ class LanguageSubsystem:
else:
switched = False
with open('ba_data/data/languages/english.json') as infile:
with open('ba_data/data/languages/english.json',
encoding='utf-8') as infile:
lenglishvalues = json.loads(infile.read())
# None implies default.
@ -173,7 +175,7 @@ class LanguageSubsystem:
else:
lmodfile = 'ba_data/data/languages/' + language.lower(
) + '.json'
with open(lmodfile) as infile:
with open(lmodfile, encoding='utf-8') as infile:
lmodvalues = json.loads(infile.read())
except Exception:
from ba import _error

View file

@ -36,6 +36,10 @@ class Level:
self._index: Optional[int] = None
self._score_version_string: Optional[str] = None
def __repr__(self) -> str:
cls = type(self)
return f"<{cls.__module__}.{cls.__name__} '{self._name}'>"
@property
def name(self) -> str:
"""The unique name for this Level."""

View file

@ -12,7 +12,7 @@ import _ba
from ba._error import print_exception, print_error, NotFoundError
from ba._gameutils import animate, animate_array
from ba._language import Lstr
from ba._enums import SpecialChar, InputType
from ba._generated.enums import SpecialChar, InputType
from ba._profile import get_player_profile_colors
if TYPE_CHECKING:

View file

@ -218,7 +218,7 @@ class Map(Actor):
self.hg=ba.NodeActor(
_ba.newnode('text',
attrs={
'text': "Smoothy Build\n v1.2",
'text': "BCS Build\n v1.3",
'flatness': 1.0,
'h_align': 'center',

View file

@ -278,7 +278,7 @@ class DirectoryScan:
else:
fpath = pathlib.Path(moduledir, subpath, '__init__.py')
ispackage = True
with fpath.open() as infile:
with fpath.open(encoding='utf-8') as infile:
flines = infile.readlines()
meta_lines = {
lnum: l[1:].split()

View file

@ -102,7 +102,7 @@ class MasterServerCallThread(threading.Thread):
import urllib.error
import json
from efro.net import is_urllib_network_error
from efro.error import is_urllib_network_error
from ba import _general
try:
self._data = _general.utf8_all(self._data)

View file

@ -30,7 +30,7 @@ def get_player_profile_icon(profilename: str) -> str:
(non-account profiles only)
"""
from ba._enums import SpecialChar
from ba._generated.enums import SpecialChar
appconfig = _ba.app.config
icon: str

View file

@ -13,9 +13,10 @@ from bacommon.servermanager import (ServerCommand, StartServerModeCommand,
ChatMessageCommand, ScreenMessageCommand,
ClientListCommand, KickCommand)
import _ba
from ba._enums import TimeType
from ba._generated.enums import TimeType
from ba._freeforallsession import FreeForAllSession
from ba._dualteamsession import DualTeamSession
from ba._coopsession import CoopSession
if TYPE_CHECKING:
from typing import Optional, Dict, Any, Type
@ -289,11 +290,14 @@ class ServerController:
return FreeForAllSession
if self._config.session_type == 'teams':
return DualTeamSession
if self._config.session_type == 'coop':
return CoopSession
raise RuntimeError(
f'Invalid session_type: "{self._config.session_type}"')
def _launch_server_session(self) -> None:
"""Kick off a host-session based on the current server config."""
# pylint: disable=too-many-branches
app = _ba.app
appcfg = app.config
sessiontype = self._get_session_type()
@ -311,6 +315,8 @@ class ServerController:
ptypename = 'Free-for-All'
elif sessiontype is DualTeamSession:
ptypename = 'Team Tournament'
elif sessiontype is CoopSession:
ptypename = 'Coop'
else:
raise RuntimeError(f'Unknown session type {sessiontype}')
@ -340,6 +346,11 @@ class ServerController:
appcfg['Team Tournament Playlist Selection'] = self._playlist_name
appcfg['Team Tournament Playlist Randomize'] = (
self._config.playlist_shuffle)
elif sessiontype is CoopSession:
app.coop_session_args = {
'campaign': self._config.coop_campaign,
'level': self._config.coop_level,
}
else:
raise RuntimeError(f'Unknown session type {sessiontype}')

View file

@ -63,10 +63,6 @@ class Session:
team instead of their own profile colors. This only applies if
use_teams is enabled.
allow_mid_activity_joins
Whether players should be allowed to join in the middle of
activities.
customdata
A shared dictionary for objects to use as storage on this session.
Ensure that keys here are unique to avoid collisions.
@ -74,7 +70,6 @@ class Session:
"""
use_teams: bool = False
use_team_colors: bool = True
allow_mid_activity_joins: bool = True
# Note: even though these are instance vars, we annotate them at the
# class level so that docs generation can access their types.
@ -210,18 +205,26 @@ class Session:
raise NodeNotFoundError()
return node
def should_allow_mid_activity_joins(self, activity: ba.Activity) -> bool:
"""Ask ourself if we should allow joins during an Activity.
Note that for a join to be allowed, both the Session and Activity
have to be ok with it (via this function and the
Activity.allow_mid_activity_joins property.
"""
del activity # Unused.
return True
def on_player_request(self, player: ba.SessionPlayer) -> bool:
"""Called when a new ba.Player wants to join the Session.
This should return True or False to accept/reject.
"""
from tools import whitelist
whitelist.handle_player_request(player)
# Limit player counts *unless* we're in a stress test.
if _ba.app.stress_test_reset_timer is None:
if len(self.sessionplayers) >= self.max_players:
# Print a rejection message *only* to the client trying to
# join (prevents spamming everyone else in the game).
_ba.playsound(_ba.getsound('error'))
@ -278,7 +281,7 @@ class Session:
assert isinstance(player, (Player, type(None)))
# Remove them from any current Activity.
if activity is not None:
if player is not None and activity is not None:
if player in activity.players:
activity.remove_player(sessionplayer)
else:
@ -335,7 +338,7 @@ class Session:
def _launch_end_session_activity(self) -> None:
"""(internal)"""
from ba._activitytypes import EndSessionActivity
from ba._enums import TimeType
from ba._generated.enums import TimeType
with _ba.Context(self):
curtime = _ba.time(TimeType.REAL)
if self._ending:
@ -368,7 +371,7 @@ class Session:
will replace the old.
"""
from ba._general import Call
from ba._enums import TimeType
from ba._generated.enums import TimeType
# Only pay attention if this is coming from our current activity.
if activity is not self._activity_retained:
@ -432,7 +435,7 @@ class Session:
(on_transition_in, etc) to get it. (so you can't do
session.setactivity(foo) and then ba.newnode() to add a node to foo)
"""
from ba._enums import TimeType
from ba._generated.enums import TimeType
# Make sure we don't get called recursively.
_rlock = self._SetActivityScopedLock(self)
@ -658,7 +661,8 @@ class Session:
# However, if we're not allowing mid-game joins, don't actually pass;
# just announce the arrival and say they'll partake next round.
if pass_to_activity:
if not self.allow_mid_activity_joins:
if not (activity.allow_mid_activity_joins
and self.should_allow_mid_activity_joins(activity)):
pass_to_activity = False
with _ba.Context(self):
_ba.screenmessage(

View file

@ -61,7 +61,7 @@ def get_store_items() -> Dict[str, Dict]:
(internal)
"""
# pylint: disable=cyclic-import
from ba._enums import SpecialChar
from ba._generated.enums import SpecialChar
from bastd import maps
if _ba.app.store_items is None:
from bastd.game import ninjafight
@ -440,7 +440,7 @@ def get_available_sale_time(tab: str) -> Optional[int]:
# pylint: disable=too-many-locals
try:
import datetime
from ba._enums import TimeType, TimeFormat
from ba._generated.enums import TimeType, TimeFormat
app = _ba.app
sale_times: List[Optional[int]] = []

View file

@ -15,7 +15,7 @@ if TYPE_CHECKING:
def get_tournament_prize_strings(entry: Dict[str, Any]) -> List[str]:
"""Given a tournament entry, return strings for its prize levels."""
# pylint: disable=too-many-locals
from ba._enums import SpecialChar
from ba._generated.enums import SpecialChar
from ba._gameutils import get_trophy_string
range1 = entry.get('prizeRange1')
range2 = entry.get('prizeRange2')

View file

@ -7,7 +7,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import _ba
from ba._enums import UIScale
from ba._generated.enums import UIScale
if TYPE_CHECKING:
from typing import Optional, Dict, Any, Callable, List, Type
@ -70,7 +70,7 @@ class UISubsystem:
def on_app_launch(self) -> None:
"""Should be run on app launch."""
from ba.ui import UIController, ui_upkeep
from ba._enums import TimeType
from ba._generated.enums import TimeType
# IMPORTANT: If tweaking UI stuff, make sure it behaves for small,
# medium, and large UI modes. (doesn't run off screen, etc).
@ -107,7 +107,7 @@ class UISubsystem:
def set_main_menu_window(self, window: ba.Widget) -> None:
"""Set the current 'main' window, replacing any existing."""
existing = self._main_menu_window
from ba._enums import TimeType
from ba._generated.enums import TimeType
from inspect import currentframe, getframeinfo
# Let's grab the location where we were called from to report

View file

@ -69,7 +69,7 @@ class _MacMusicAppThread(threading.Thread):
"""Run the Music.app thread."""
from ba._general import Call
from ba._language import Lstr
from ba._enums import TimeType
from ba._generated.enums import TimeType
_ba.set_thread_name('BA_MacMusicAppThread')
_ba.mac_music_app_init()

View file

@ -40,7 +40,7 @@ def get_human_readable_user_scripts_path() -> str:
def _request_storage_permission() -> bool:
"""If needed, requests storage permission from the user (& return true)."""
from ba._language import Lstr
from ba._enums import Permission
from ba._generated.enums import Permission
if not _ba.have_permission(Permission.STORAGE):
_ba.playsound(_ba.getsound('error'))
_ba.screenmessage(Lstr(resource='storagePermissionAccessText'),
@ -73,7 +73,7 @@ def show_user_scripts() -> None:
usd: Optional[str] = app.python_directory_user
if usd is not None and os.path.isdir(usd):
file_name = usd + '/about_this_folder.txt'
with open(file_name, 'w') as outfile:
with open(file_name, 'w', encoding='utf-8') as outfile:
outfile.write('You can drop files in here to mod the game.'
' See settings/advanced'
' in the game for more info.')

View file

@ -10,7 +10,7 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING, cast, Type
import _ba
from ba._enums import TimeType
from ba._generated.enums import TimeType
from ba._general import print_active_refs
if TYPE_CHECKING:

Some files were not shown because too many files have changed in this diff Show more