diff --git a/dist/ba_root/mods/custom_hooks.py b/dist/ba_root/mods/custom_hooks.py index c459a50..1629da0 100644 --- a/dist/ba_root/mods/custom_hooks.py +++ b/dist/ba_root/mods/custom_hooks.py @@ -8,42 +8,94 @@ # pylint: disable=protected-access from __future__ import annotations +from tools import servercheck, server_update, logger, playlist, servercontroller +from tools import notification_manager +from tools import account +from stats import mystats +from spazmod import modifyspaz +from serverdata import serverdata +from playersdata import pdata +from features import votingmachine +from features import text_on_map, announcement +from features import team_balancer, afk_check, dual_team_score as newdts +from features import map_fun +from chathandle import handlechat +from bascenev1lib.actor import playerspaz +from bascenev1lib.activity.coopscore import CoopScoreScreen +from bascenev1lib.activity import dualteamscore, multiteamscore, drawscore +from bascenev1._session import Session +from bascenev1._map import Map +from bascenev1._activitytypes import ScoreScreenActivity +from baclassic._servermode import ServerController +import setting +import bauiv1 as bui +from baclassic._appmode import ClassicAppMode +import _bascenev1 +import bascenev1 as bs +import babase +from typing import TYPE_CHECKING +import _babase import _thread import importlib import logging import os +import sys +import subprocess import time from datetime import datetime -import _babase -from typing import TYPE_CHECKING +# --- Auto dependency installer --- + + +def _check_and_install_dependencies(): + """Checks and installs ecdsa and flask to python-site-packages if missing.""" + needed = ["ecdsa", "flask", "waitress"] + missing = [] + + mods_dir = os.path.dirname(__file__) + target_dir = os.path.abspath(os.path.join( + mods_dir, "..", "..", "ba_data", "python-site-packages")) + if target_dir not in sys.path: + sys.path.insert(0, target_dir) + + for pkg in needed: + try: + importlib.import_module(pkg) + except ImportError: + missing.append(pkg) + + if missing: + logging.warning( + f"Required dependencies {missing} are missing. Attempting to install them into {target_dir}...") + try: + python_exe = sys.executable or "python3" + cmd = [ + python_exe, + "-m", + "pip", + "install", + "--target", + target_dir, + "--break-system-packages" + ] + missing + + result = subprocess.run( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if result.returncode == 0: + logging.warning( + f"Successfully installed {missing} to {target_dir}") + importlib.invalidate_caches() + else: + logging.error( + f"Failed to install dependencies {missing}. pip output: {result.stderr}") + except Exception as e: + logging.exception( + f"Exception during automatic dependency installation: {e}") + + +_check_and_install_dependencies() -import babase -import bascenev1 as bs -import _bascenev1 -from baclassic._appmode import ClassicAppMode -import bauiv1 as bui -import setting -from baclassic._servermode import ServerController -from bascenev1._activitytypes import ScoreScreenActivity -from bascenev1._map import Map -from bascenev1._session import Session -from bascenev1lib.activity import dualteamscore, multiteamscore, drawscore -from bascenev1lib.activity.coopscore import CoopScoreScreen -from bascenev1lib.actor import playerspaz -from chathandle import handlechat -from features import map_fun -from features import team_balancer, afk_check, dual_team_score as newdts -from features import text_on_map, announcement -from features import votingmachine -from playersdata import pdata -from serverdata import serverdata -from spazmod import modifyspaz -from stats import mystats -from tools import account -from tools import notification_manager -from tools import servercheck, server_update, logger, playlist, servercontroller if TYPE_CHECKING: from typing import Any @@ -275,6 +327,7 @@ org_player_join = bs._activity.Activity.on_player_join def on_player_join(self, player) -> None: """Runs when player joins the game.""" team_balancer.on_player_join() + try: from shop.shop_system import preload_player account_id = player.get_v1_account_id() @@ -369,6 +422,7 @@ def on_player_request(func) -> bool: if not (player.get_v1_account_id( ) in serverdata.clients and serverdata.clients[player.get_v1_account_id()]["verified"]): + return False for current_player in args[0].sessionplayers: if current_player.get_v1_account_id() == player.get_v1_account_id(): diff --git a/dist/ba_root/mods/plugins/elPatronPowerups.py b/dist/ba_root/mods/plugins/elPatronPowerups.py index 6f23a11..f288386 100644 --- a/dist/ba_root/mods/plugins/elPatronPowerups.py +++ b/dist/ba_root/mods/plugins/elPatronPowerups.py @@ -1,22 +1,21 @@ # ba_meta require api 8 from __future__ import annotations +import setting +from bascenev1lib.actor.bomb import BombFactory +from bascenev1lib.actor.spaz import * +from bauiv1lib.confirm import ConfirmWindow +from bascenev1lib.actor.popuptext import PopupText +from bascenev1lib.mainmenu import (MainMenuActivity, MainMenuSession) +from bauiv1lib.popup import (PopupWindow, PopupMenu) +from bascenev1lib.actor.spazbot import SpazBot +from bascenev1lib.actor import powerupbox as pupbox +from bascenev1lib.actor import bomb +from bauiv1lib.profile import browser +import bauiv1 as bui +import babase _sp_ = ('\n') -import babase -import bauiv1 as bui - -from bauiv1lib.profile import browser -from bascenev1lib.actor import bomb -from bascenev1lib.actor import powerupbox as pupbox -from bascenev1lib.actor.spazbot import SpazBot -from bauiv1lib.popup import (PopupWindow, PopupMenu) -from bascenev1lib.mainmenu import (MainMenuActivity, MainMenuSession) -from bascenev1lib.actor.popuptext import PopupText -from bauiv1lib.confirm import ConfirmWindow -from bascenev1lib.actor.spaz import * -from bascenev1lib.actor.bomb import BombFactory - if TYPE_CHECKING: pass @@ -25,12 +24,13 @@ if TYPE_CHECKING: # === Mod made by @Patron_Modz === def getlanguage(text, subs: str = None, almacen: list = []): - if almacen == []: almacen = list(range(1000)) + if almacen == []: + almacen = list(range(1000)) lang = bui.app.lang.language translate = {"Reset": - {"Spanish": "Reiniciar", - "English": "Reset", - "Portuguese": "Reiniciar"}, + {"Spanish": "Reiniciar", + "English": "Reset", + "Portuguese": "Reiniciar"}, "Nothing": {"Spanish": "Sin potenciadores", "English": "No powerups", @@ -221,7 +221,8 @@ def getlanguage(text, subs: str = None, almacen: list = []): "Portuguese": f"Você ganhou {almacen[0]} Moedas. {_sp_} Mas você excedeu o limite de {almacen[1]}"}, } languages = ['Spanish', 'Portuguese', 'English'] - if lang not in languages: lang = 'English' + if lang not in languages: + lang = 'English' if text not in translate: return text @@ -229,8 +230,6 @@ def getlanguage(text, subs: str = None, almacen: list = []): return translate[text][lang] -import setting - settings = setting.get_settings_data() @@ -313,44 +312,6 @@ for i, x in promo_codes().items(): apg.apply_and_commit() -class BearStore: - def __init__(self, - price: int = 1000, - value: str = '', - callback: Callable[[], None] = None): - - self.price = price - self.value = value - self.store = STORE[value] - self.coins = apg['Bear Coin'] - self.callback = callback - - def buy(self): - if not self.store: - if self.coins >= (self.price): - def confirm(): - STORE[self.value] = True - apg['Bear Coin'] -= int(self.price) - bs.broadcastmessage(getlanguage('Purchase'), (0, 1, 0)) - bs.getsound('cashRegister').play() - apg.apply_and_commit() - self.callback() - - ConfirmWindow(getlanguage('Confirm Purchase', subs=self.coins), - width=400, height=120, action=confirm, - ok_text=babase.Lstr(resource='okText')) - else: - bs.broadcastmessage(getlanguage('Coins 0'), (1, 0, 0)) - bs.getsound('error').play() - else: - bs.broadcastmessage(getlanguage('Double Product'), (1, 0, 0)) - bs.getsound('error').play() - - def __del__(self): - apg['Bear Coin'] = int(apg['Bear Coin']) - apg.apply_and_commit() - - class PromoCode: def __init__(self, code: str = ''): self.code = code @@ -495,55 +456,6 @@ def percentage_health_damage(): return float(percentage_text) -# === Modify class === - -class NewProfileBrowserWindow(browser.ProfileBrowserWindow): - def __init__(self, - transition: str = 'in_right', - in_main_menu: bool = True, - selected_profile: str = None, - origin_widget: bui.Widget = None): - super().__init__(transition, in_main_menu, selected_profile, - origin_widget) - - self.session = bs.get_foreground_host_session() - uiscale = bui.app.ui_v1.uiscale - width = (100 if uiscale is - babase.UIScale.SMALL else -14) - size = 50 - position = (width * 1.65, 300) - - if isinstance(self.session, MainMenuSession): - self.button = bui.buttonwidget(parent=self._root_widget, - autoselect=True, position=position, - size=(size, size), - button_type='square', - label='', - on_activate_call=babase.Call( - self.powerupmanager_window)) - - size = size * 0.60 - self.image = bui.imagewidget(parent=self._root_widget, - size=(size, size), - draw_controller=self.button, - position=( - position[0] + 10.5, position[1] + 17), - texture=bs.gettexture('powerupSpeed')) - - self.text = bui.textwidget(parent=self._root_widget, - position=( - position[0] + 25, position[1] + 10), - size=(0, 0), scale=0.45, - color=(0.7, 0.9, 0.7, 1.0), - draw_controller=self.button, maxwidth=60, - text=(f"Ultimate Powerup {_sp_}Manager"), - h_align='center', v_align='center') - - def powerupmanager_window(self): - bui.containerwidget(edit=self._root_widget, transition='out_left') - PowerupManagerWindow() - - class NewPowerupBoxFactory(pupbox.PowerupBoxFactory): def __init__(self) -> None: super().__init__() @@ -587,7 +499,8 @@ class NewPowerupBoxFactory(pupbox.PowerupBoxFactory): ptype = self._powerupdist[random.randint( 0, len(self._powerupdist) - 1)] - if ptype not in excludetypes and ptype not in powerup_disable: break + if ptype not in excludetypes and ptype not in powerup_disable: + break self._lastpoweruptype = ptype return ptype @@ -601,7 +514,7 @@ def fire_effect(self): self.fire_effect_time = None -###########BOMBS +# BOMBS Bomb._pm_old_bomb = Bomb.__init__ @@ -615,7 +528,8 @@ def _bomb_init(self, owner: bs.Node = None): self.bm_type = bomb_type - new_bomb_type = 'ice' if bomb_type in ['ice_bubble', 'impairment', 'fire', 'fly'] else bomb_type + new_bomb_type = 'ice' if bomb_type in [ + 'ice_bubble', 'impairment', 'fire', 'fly'] else bomb_type # Call original __init__ self._pm_old_bomb(position=position, @@ -641,7 +555,8 @@ def _bomb_init(self, self.shield_fire = bs.newnode('shield', owner=self.node, attrs={'color': (6.5, 6.5, 2.0), 'radius': 0.6}) self.node.connectattr('position', self.shield_fire, 'position') - self.fire_effect_time = bs.Timer(0.1, babase.Call(fire_effect, self), repeat=True) + self.fire_effect_time = bs.Timer( + 0.1, babase.Call(fire_effect, self), repeat=True) elif self.bm_type == 'impairment': self.bomb_type = self.bm_type @@ -660,8 +575,6 @@ def _bomb_init(self, self.blast_radius *= 2.2 - - def bomb_handlemessage(self, msg: Any) -> Any: assert not self.expired @@ -703,34 +616,34 @@ def bomb_handlemessage(self, msg: Any) -> Any: def powerup_translated(self, type: str): powerups_names = {'triple_bombs': babase.Lstr( resource='helpWindow.' + 'powerupBombNameText'), - 'ice_bombs': babase.Lstr( - resource='helpWindow.' + 'powerupIceBombsNameText'), - 'punch': babase.Lstr( - resource='helpWindow.' + 'powerupPunchNameText'), - 'impact_bombs': babase.Lstr( - resource='helpWindow.' + 'powerupImpactBombsNameText'), - 'land_mines': babase.Lstr( - resource='helpWindow.' + 'powerupLandMinesNameText'), - 'sticky_bombs': babase.Lstr( - resource='helpWindow.' + 'powerupStickyBombsNameText'), - 'shield': babase.Lstr( - resource='helpWindow.' + 'powerupShieldNameText'), - 'health': babase.Lstr( - resource='helpWindow.' + 'powerupHealthNameText'), - 'curse': babase.Lstr( - resource='helpWindow.' + 'powerupCurseNameText'), - 'speed': getlanguage('Speed'), - 'health_damage': getlanguage('Healing Damage'), - 'goodbye': getlanguage('Goodbye'), - 'ice_man': getlanguage('Ice Man'), - 'tank_shield': getlanguage('Tank Shield'), - 'impairment_bombs': getlanguage('Impairment Bombs'), - 'fire_bombs': getlanguage('Fire Bombs'), - 'fly_bombs': getlanguage('Fly Bombs')} + 'ice_bombs': babase.Lstr( + resource='helpWindow.' + 'powerupIceBombsNameText'), + 'punch': babase.Lstr( + resource='helpWindow.' + 'powerupPunchNameText'), + 'impact_bombs': babase.Lstr( + resource='helpWindow.' + 'powerupImpactBombsNameText'), + 'land_mines': babase.Lstr( + resource='helpWindow.' + 'powerupLandMinesNameText'), + 'sticky_bombs': babase.Lstr( + resource='helpWindow.' + 'powerupStickyBombsNameText'), + 'shield': babase.Lstr( + resource='helpWindow.' + 'powerupShieldNameText'), + 'health': babase.Lstr( + resource='helpWindow.' + 'powerupHealthNameText'), + 'curse': babase.Lstr( + resource='helpWindow.' + 'powerupCurseNameText'), + 'speed': getlanguage('Speed'), + 'health_damage': getlanguage('Healing Damage'), + 'goodbye': getlanguage('Goodbye'), + 'ice_man': getlanguage('Ice Man'), + 'tank_shield': getlanguage('Tank Shield'), + 'impairment_bombs': getlanguage('Impairment Bombs'), + 'fire_bombs': getlanguage('Fire Bombs'), + 'fly_bombs': getlanguage('Fly Bombs')} self.texts['Name'].text = powerups_names[type] -###########POWERUP +# POWERUP pupbox.PowerupBox._old_pbx_ = pupbox.PowerupBox.__init__ @@ -738,7 +651,8 @@ def _pbx_(self, position: Sequence[float] = (0.0, 1.0, 0.0), poweruptype: str = 'triple_bombs', expire: bool = True): self.news: list = [] - for x, i in powerup_dist(): self.news.append(x) + for x, i in powerup_dist(): + self.news.append(x) self.box: list = [] self.texts = {} @@ -788,16 +702,17 @@ def _pbx_(self, position: Sequence[float] = (0.0, 1.0, 0.0), n_scale = config['Powerup Scale'] style = config['Powerup Style'] - curve = bs.animate(self.node, 'mesh_scale', {0: 0, 0.14: 1.6, 0.2: n_scale}) + curve = bs.animate(self.node, 'mesh_scale', { + 0: 0, 0.14: 1.6, 0.2: n_scale}) bs.timer(0.2, curve.delete) def util_text(type: str, text: str, scale: float = 1, color: list = [1, 1, 1], position: list = [0, 0.7, 0], colors_name: bool = False): m = bs.newnode('math', owner=self.node, attrs={'input1': - (position[0], - position[1], - position[2]), + (position[0], + position[1], + position[2]), 'operation': 'add'}) self.node.connectattr('position', m, 'input2') self.texts[type] = bs.newnode('text', owner=self.node, @@ -867,7 +782,7 @@ def _pbx_(self, position: Sequence[float] = (0.0, 1.0, 0.0), self.node.mesh = bs.getmesh('egg') -###########SPAZ +# SPAZ def _speed_off_flash(self): if self.node: factory = NewPowerupBoxFactory.get() @@ -1274,7 +1189,7 @@ def new_handlemessage(self, msg: Any) -> Any: local_time = int(bs.time() * 1000) assert isinstance(local_time, int) if (self._last_hit_time is None - or local_time - self._last_hit_time > 1000): + or local_time - self._last_hit_time > 1000): self._num_times_hit += 1 self._last_hit_time = local_time @@ -1298,8 +1213,8 @@ def new_handlemessage(self, msg: Any) -> Any: if not self.shield and not self._dead: self.hitpoints -= damage bs.show_damage_count(f'-{damage}HP', - self.node.position, - msg.force_direction) + self.node.position, + msg.force_direction) bs.getsound('fuse01').play() if duration != time: @@ -1351,8 +1266,8 @@ def new_handlemessage(self, msg: Any) -> Any: hitpoints = int(self.hitpoints * 0.80) self.hitpoints -= int(hitpoints) bs.show_damage_count((f'-{int(hitpoints / 10)}%'), - self.node.position, - msg.force_direction) + self.node.position, + msg.force_direction) if self.hitpoints < 0 or hitpoints < 95: self.node.handlemessage(bs.DieMessage()) @@ -1435,7 +1350,7 @@ def new_handlemessage(self, msg: Any) -> Any: damage = int(damage - dism) bs.show_damage_count('-' + str(int(damage / 10)) + '%', - msg.pos, msg.force_direction) + msg.pos, msg.force_direction) self.node.handlemessage('hurt_sound') @@ -1465,7 +1380,7 @@ def new_handlemessage(self, msg: Any) -> Any: if damage > 350: assert msg.force_direction is not None bs.show_damage_count('-' + str(int(damage / 10)) + '%', - msg.pos, msg.force_direction) + msg.pos, msg.force_direction) if msg.hit_subtype == 'super_punch': SpazFactory.get().punch_sound_stronger.play(1.0, @@ -1667,7 +1582,7 @@ def new_handlemessage(self, msg: Any) -> Any: pass if (opposingnode.getnodetype() == 'spaz' - and not opposingnode.shattered and opposingbody == 4): + and not opposingnode.shattered and opposingbody == 4): opposingbody = 1 held = self.node.hold_node @@ -1717,7 +1632,7 @@ class PowerupManagerWindow(PopupWindow): if (STORE['Buy Firebombs'] and STORE['Buy Option'] and - STORE['Buy Percentage']): + STORE['Buy Percentage']): self.tabdefs = {"Action 1": ['powerupIceBombs', (1, 1, 1)], "Action 2": ['settingsIcon', (0, 1, 0)], "Action 3": ['inventoryIcon', (1, 1, 1)], @@ -1742,7 +1657,7 @@ class PowerupManagerWindow(PopupWindow): self._backButton = b = bui.buttonwidget(parent=self._root_widget, autoselect=True, position=( - 60, self._height - 15), + 60, self._height - 15), size=(130, 60), scale=0.8, text_scale=1.2, label=babase.Lstr( @@ -1769,7 +1684,7 @@ class PowerupManagerWindow(PopupWindow): tag = self.listdef[index] position = ( - 620 + (tab2 * 120), self._height - 50 * 2.5 - (tab * 120)) + 620 + (tab2 * 120), self._height - 50 * 2.5 - (tab * 120)) if tag == 'About': text = babase.Lstr(resource='gatherWindow.aboutText') @@ -1788,7 +1703,7 @@ class PowerupManagerWindow(PopupWindow): self.text = bui.textwidget(parent=self._root_widget, position=( - position[0] + 55, position[1] + 30), + position[0] + 55, position[1] + 30), size=(0, 0), scale=1, color=bui.app.ui_v1.title_color, draw_controller=self.tab_buttons[ @@ -1837,7 +1752,7 @@ class PowerupManagerWindow(PopupWindow): self._scrollwidget = bui.scrollwidget(parent=self._root_widget, position=( - self._width * 0.08, 51 * 1.8), + self._width * 0.08, 51 * 1.8), size=(self._sub_width - 140, self._scroll_height + 60 * 1.2)) @@ -1845,12 +1760,12 @@ class PowerupManagerWindow(PopupWindow): if self._scrollwidget: self._scrollwidget.delete() self._scrollwidget = bui.hscrollwidget(parent=self._root_widget, - position=( - self._width * 0.08, - 51 * 1.8), size=( - self._sub_width - 140, self._scroll_height + 60 * 1.2), - capture_arrows=True, - claims_left_right=True) + position=( + self._width * 0.08, + 51 * 1.8), size=( + self._sub_width - 140, self._scroll_height + 60 * 1.2), + capture_arrows=True, + claims_left_right=True) bui.textwidget(edit=self.titletext, text=babase.Lstr(resource='storeText')) elif tab == 'About': @@ -1879,7 +1794,7 @@ class PowerupManagerWindow(PopupWindow): self.button_cls_power = bui.buttonwidget(parent=self._root_widget, position=( - 500, self._width * 0.61), + 500, self._width * 0.61), size=(50, 50), autoselect=True, scale=1, label=('%'), @@ -1898,14 +1813,15 @@ class PowerupManagerWindow(PopupWindow): self.button_coin = bui.buttonwidget(parent=self._root_widget, icon=bs.gettexture('coin'), position=( - 550, self._width * 0.614), + 550, self._width * 0.614), size=(160, 40), textcolor=(0, 1, 0), color=(0, 1, 6), scale=1, label=str(apg['Bear Coin']), text_scale=1, autoselect=True, - on_activate_call=None) # self._percentage_window) + # self._percentage_window) + on_activate_call=None) self.list_cls_power.append(self.button_coin) try: @@ -1999,11 +1915,11 @@ class PowerupManagerWindow(PopupWindow): position = (90, v - posi) t = bui.textwidget(parent=c, position=( - position[0] - 30, position[1] - 15), size=(width, 50), - h_align="center", - color=(bui.app.ui_v1.title_color), - text=label, v_align="center", - maxwidth=width * 1.3) + position[0] - 30, position[1] - 15), size=(width, 50), + h_align="center", + color=(bui.app.ui_v1.title_color), + text=label, v_align="center", + maxwidth=width * 1.3) self.powprev = bui.imagewidget(parent=c, position=(position[0] - 70, @@ -2023,12 +1939,12 @@ class PowerupManagerWindow(PopupWindow): dipos += 100 textwidget = bui.textwidget(parent=c, position=( - position[0] + 190, position[1] - 15), size=(width, 50), - h_align="center", - color=cls_pow_color()[apperance], - text=str(apperance), - v_align="center", - maxwidth=width * 1.3) + position[0] + 190, position[1] - 15), size=(width, 50), + h_align="center", + color=cls_pow_color()[apperance], + text=str(apperance), + v_align="center", + maxwidth=width * 1.3) self.listpower[power] = textwidget posi += 90 @@ -2075,7 +1991,7 @@ class PowerupManagerWindow(PopupWindow): for direc in ['-', '+']: bui.buttonwidget(parent=c, autoselect=True, position=( - position[0] + 310 + dipos, position[1] - 100), + position[0] + 310 + dipos, position[1] - 100), size=(100, 100), repeat=True, scale=0.4, label=direc, button_type='square', text_scale=4, @@ -2085,54 +2001,54 @@ class PowerupManagerWindow(PopupWindow): txt_scale = config['Powerup Scale'] self.txt_scale = bui.textwidget(parent=c, position=( - position[0] + 230, position[1] - 105), size=(width, 50), - scale=1.1, h_align="center", - color=(0, 1, 0), - text=str(txt_scale), - v_align="center", - maxwidth=width * 1.3) + position[0] + 230, position[1] - 105), size=(width, 50), + scale=1.1, h_align="center", + color=(0, 1, 0), + text=str(txt_scale), + v_align="center", + maxwidth=width * 1.3) text = getlanguage('Powerup Scale') wt = (len(text) * 0.80) t = bui.textwidget(parent=c, position=( - position[0] - 60 + wt, position[1] - 100), size=(width, 50), - maxwidth=width * 0.9, - scale=1.1, h_align="center", - color=bui.app.ui_v1.title_color, text=text, - v_align="center") + position[0] - 60 + wt, position[1] - 100), size=(width, 50), + maxwidth=width * 0.9, + scale=1.1, h_align="center", + color=bui.app.ui_v1.title_color, text=text, + v_align="center") position = (position[0] - 20, position[1] + 40) self.check = bui.checkboxwidget(parent=c, position=( - position[0] + 30, position[1] - 230), value=config['Powerup Name'], - on_value_change_call=babase.Call( - self._switches, 'Powerup Name'), - maxwidth=self._scroll_width * 0.9, - text=getlanguage('Powerup Name'), - autoselect=True) + position[0] + 30, position[1] - 230), value=config['Powerup Name'], + on_value_change_call=babase.Call( + self._switches, 'Powerup Name'), + maxwidth=self._scroll_width * 0.9, + text=getlanguage('Powerup Name'), + autoselect=True) self.check = bui.checkboxwidget(parent=c, position=( - position[0] + 30, position[1] - 230 * 1.3), - value=config['Powerup With Shield'], - on_value_change_call=babase.Call( - self._switches, - 'Powerup With Shield'), - maxwidth=self._scroll_width * 0.9, - text=getlanguage( - 'Powerup With Shield'), - autoselect=True) + position[0] + 30, position[1] - 230 * 1.3), + value=config['Powerup With Shield'], + on_value_change_call=babase.Call( + self._switches, + 'Powerup With Shield'), + maxwidth=self._scroll_width * 0.9, + text=getlanguage( + 'Powerup With Shield'), + autoselect=True) if STORE['Buy Option']: self.check = bui.checkboxwidget(parent=c, position=( - position[0] + 30, position[1] - 230 * 1.6), - value=config['Powerup Time'], - on_value_change_call=babase.Call( - self._switches, - 'Powerup Time'), - maxwidth=self._scroll_width * 0.9, - text=getlanguage( - 'Powerup Time'), - autoselect=True) + position[0] + 30, position[1] - 230 * 1.6), + value=config['Powerup Time'], + on_value_change_call=babase.Call( + self._switches, + 'Powerup Time'), + maxwidth=self._scroll_width * 0.9, + text=getlanguage( + 'Powerup Time'), + autoselect=True) elif tab == 'Action 3': sub_height = 300 @@ -2158,17 +2074,17 @@ class PowerupManagerWindow(PopupWindow): i = bui.imagewidget(parent=c, position=( - position[0] + 100, position[1] - 205), + position[0] + 100, position[1] - 205), size=(80, 80), texture=bs.gettexture('lock')) else: t = bui.textwidget(parent=c, position=( - position[0] - 14, position[1] + 70), size=(30 + width, 50), - h_align="center", - text=f"{getlanguage('Tank Shield PTG')} ({getlanguage('Tank Shield')})", - color=bui.app.ui_v1.title_color, - v_align="center", maxwidth=width * 1.5, - scale=1.5) + position[0] - 14, position[1] + 70), size=(30 + width, 50), + h_align="center", + text=f"{getlanguage('Tank Shield PTG')} ({getlanguage('Tank Shield')})", + color=bui.app.ui_v1.title_color, + v_align="center", maxwidth=width * 1.5, + scale=1.5) b = bui.buttonwidget(parent=c, autoselect=True, position=position, size=(100, 100), @@ -2198,24 +2114,24 @@ class PowerupManagerWindow(PopupWindow): color = (0, 1, 0.8) self.tank_text = bui.textwidget(parent=c, position=( - position[0] - 14, position[1] + 5), - size=(30 + width, 50), - h_align="center", - text=str(porcentaje) + '%', - color=color, - v_align="center", - maxwidth=width * 1.3, scale=2) + position[0] - 14, position[1] + 5), + size=(30 + width, 50), + h_align="center", + text=str(porcentaje) + '%', + color=color, + v_align="center", + maxwidth=width * 1.3, scale=2) # -----> position = (110, v - 160 * 1.6) t = bui.textwidget(parent=c, position=( - position[0] - 14, position[1] + 70), size=(30 + width, 50), - h_align="center", - text=f"{getlanguage('Healing Damage PTG')}{_sp_}({getlanguage('Healing Damage')})", - color=bui.app.ui_v1.title_color, - v_align="center", maxwidth=width * 1.3, - scale=1.4) + position[0] - 14, position[1] + 70), size=(30 + width, 50), + h_align="center", + text=f"{getlanguage('Healing Damage PTG')}{_sp_}({getlanguage('Healing Damage')})", + color=bui.app.ui_v1.title_color, + v_align="center", maxwidth=width * 1.3, + scale=1.4) b = bui.buttonwidget(parent=c, autoselect=True, position=position, size=(100, 100), @@ -2245,13 +2161,13 @@ class PowerupManagerWindow(PopupWindow): color = (0, 1, 0.8) self.hlg_text = bui.textwidget(parent=c, position=( - position[0] - 14, position[1] + 5), - size=(30 + width, 50), - h_align="center", - text=str(porcentaje) + '%', - color=color, - v_align="center", - maxwidth=width * 1.3, scale=2) + position[0] - 14, position[1] + 5), + size=(30 + width, 50), + h_align="center", + text=str(porcentaje) + '%', + color=color, + v_align="center", + maxwidth=width * 1.3, scale=2) elif tab == 'Percentage': sub_height = len(self.default_power_list) * 90 @@ -2329,11 +2245,11 @@ class PowerupManagerWindow(PopupWindow): position = (90, v - posi) t = bui.textwidget(parent=c, position=( - position[0] - 30, position[1] - 15), size=(width, 50), - h_align="center", - color=(bui.app.ui_v1.title_color), - text=label, v_align="center", - maxwidth=width * 1.3) + position[0] - 30, position[1] - 15), size=(width, 50), + h_align="center", + color=(bui.app.ui_v1.title_color), + text=label, v_align="center", + maxwidth=width * 1.3) self.powprev = bui.imagewidget(parent=c, position=(position[0] - 70, @@ -2342,10 +2258,10 @@ class PowerupManagerWindow(PopupWindow): ptg = str(self.total_percentage(power)) t = bui.textwidget(parent=c, position=( - position[0] + 170, position[1] - 10), size=(width, 50), - h_align="center", color=(0, 1, 0), - text=(f'{ptg}%'), v_align="center", - maxwidth=width * 1.3) + position[0] + 170, position[1] - 10), size=(width, 50), + h_align="center", color=(0, 1, 0), + text=(f'{ptg}%'), v_align="center", + maxwidth=width * 1.3) posi += 90 @@ -2387,21 +2303,21 @@ class PowerupManagerWindow(PopupWindow): txt_scale = 2 b = bui.buttonwidget(parent=c, autoselect=True, position=( - position[0] + 210 - n_pos, position[1]), - size=(250, 80), scale=0.7, label=text, - text_scale=txt_scale, icon=icon, - color=color, - iconscale=1.7, - on_activate_call=babase.Call( - self._buy_object, store, p)) + position[0] + 210 - n_pos, position[1]), + size=(250, 80), scale=0.7, label=text, + text_scale=txt_scale, icon=icon, + color=color, + iconscale=1.7, + on_activate_call=babase.Call( + self._buy_object, store, p)) s = 180 b = bui.buttonwidget(parent=c, autoselect=True, position=( - position[0] + 210 - n_pos, position[1] + 55), - size=(s, s + 30), scale=1, label='', - color=color, button_type='square', - on_activate_call=babase.Call( - self._buy_object, store, p)) + position[0] + 210 - n_pos, position[1] + 55), + size=(s, s + 30), scale=1, label='', + color=color, button_type='square', + on_activate_call=babase.Call( + self._buy_object, store, p)) s -= 80 i = bui.imagewidget(parent=c, draw_controller=b, @@ -2410,10 +2326,10 @@ class PowerupManagerWindow(PopupWindow): size=(s, s), texture=bs.gettexture(preview)) t = bui.textwidget(parent=c, position=( - position[0] + 270 - n_pos, position[1] + 101), - h_align="center", - color=(bui.app.ui_v1.title_color), - text=label, v_align="center", maxwidth=130) + position[0] + 270 - n_pos, position[1] + 101), + h_align="center", + color=(bui.app.ui_v1.title_color), + text=label, v_align="center", maxwidth=130) n_pos += 280 index += 1 @@ -2440,15 +2356,15 @@ class PowerupManagerWindow(PopupWindow): v_align="center", maxwidth=width * 1.3) self.promocode_text = bui.textwidget(parent=c, position=( - position[0] + 80, position[1] - 100), size=(width + 60, 50), - scale=1, - editable=True, - h_align="center", color=( - bui.app.ui_v1.title_color), text='', v_align="center", - maxwidth=width * 1.3, - max_chars=30, - description=babase.Lstr( - resource='settingsWindowAdvanced.enterPromoCodeText')) + position[0] + 80, position[1] - 100), size=(width + 60, 50), + scale=1, + editable=True, + h_align="center", color=( + bui.app.ui_v1.title_color), text='', v_align="center", + maxwidth=width * 1.3, + max_chars=30, + description=babase.Lstr( + resource='settingsWindowAdvanced.enterPromoCodeText')) self.promocode_button = bui.buttonwidget( parent=c, position=(position[0] + 160, position[1] - 170), diff --git a/dist/ba_root/mods/repository/profiles.py b/dist/ba_root/mods/repository/profiles.py index 11e6df2..631f99d 100644 --- a/dist/ba_root/mods/repository/profiles.py +++ b/dist/ba_root/mods/repository/profiles.py @@ -37,6 +37,46 @@ def init_db(): ) """) + # Schema migration helper: ensure all required columns are present in case the table existed previously + try: + conn = get_connection() + cur = conn.cursor() + cur.execute("PRAGMA table_info(profiles)") + existing_cols = {row[1] for row in cur.fetchall()} + conn.close() + except Exception as e: + print(f"Error checking table schema: {e}") + existing_cols = set() + + if existing_cols: + expected_columns = { + "account_id": "TEXT", + "display_string": "TEXT", + "profiles": "TEXT", + "name": "TEXT", + "isBan": "INTEGER", + "isMuted": "INTEGER", + "accountAge": "TEXT", + "creationDate": "TEXT", + "registerOn": "REAL", + "canStartKickVote": "INTEGER", + "spamCount": "INTEGER", + "lastSpam": "REAL", + "totaltimeplayer": "REAL", + "warnCount": "INTEGER", + "lastWarned": "REAL", + "verified": "INTEGER", + "rejoincount": "INTEGER", + "lastJoin": "REAL", + "deviceUUID": "TEXT" + } + for col_name, col_type in expected_columns.items(): + if col_name not in existing_cols: + try: + run_query(f"ALTER TABLE profiles ADD COLUMN {col_name} {col_type}") + except Exception as e: + print(f"Error adding column {col_name} to profiles: {e}") + # Ensure v2Tag and account_id have unique indices for conflict resolution run_query("CREATE UNIQUE INDEX IF NOT EXISTS idx_profiles_v2Tag ON profiles(v2Tag)") run_query("CREATE UNIQUE INDEX IF NOT EXISTS idx_profiles_account_id ON profiles(account_id)") diff --git a/dist/ba_root/mods/stats/stats.json b/dist/ba_root/mods/stats/stats.json index 0756a22..46f425e 100644 --- a/dist/ba_root/mods/stats/stats.json +++ b/dist/ba_root/mods/stats/stats.json @@ -1,5 +1,5 @@ { - "startDate": "25-03-2026", + "startDate": "15-07-2026", "stats": { "pb-IF4VAk4a": { "rank": 1, @@ -13,6 +13,19 @@ "avg_score": 0.0, "aid": "pb-IF4VAk4a", "last_seen": "2022-04-26 17:01:13.715014" + }, + "a-1828": { + "rank": 2, + "name": "Smoothy", + "scores": 0, + "total_damage": 0.0, + "kills": 0, + "deaths": 0, + "games": 1, + "kd": 0.0, + "avg_score": 0.0, + "last_seen": "2026-07-15 08:46:34.946691", + "aid": "a-1828" } } } \ No newline at end of file diff --git a/dist/ba_root/mods/stats/stats.json.backup b/dist/ba_root/mods/stats/stats.json.backup index 0756a22..46f425e 100644 --- a/dist/ba_root/mods/stats/stats.json.backup +++ b/dist/ba_root/mods/stats/stats.json.backup @@ -1,5 +1,5 @@ { - "startDate": "25-03-2026", + "startDate": "15-07-2026", "stats": { "pb-IF4VAk4a": { "rank": 1, @@ -13,6 +13,19 @@ "avg_score": 0.0, "aid": "pb-IF4VAk4a", "last_seen": "2022-04-26 17:01:13.715014" + }, + "a-1828": { + "rank": 2, + "name": "Smoothy", + "scores": 0, + "total_damage": 0.0, + "kills": 0, + "deaths": 0, + "games": 1, + "kd": 0.0, + "avg_score": 0.0, + "last_seen": "2026-07-15 08:46:34.946691", + "aid": "a-1828" } } } \ No newline at end of file diff --git a/dist/ba_root/mods/tools/servercheck.py b/dist/ba_root/mods/tools/servercheck.py index a279936..b811915 100644 --- a/dist/ba_root/mods/tools/servercheck.py +++ b/dist/ba_root/mods/tools/servercheck.py @@ -28,6 +28,9 @@ blacklist = pdata.get_blacklist() # Get settings settings = setting.get_settings_data() +# Track IP join statistics for rate limiting/spam protection +ipjoin: Dict[str, IPJoin] = {} + @dataclass class PlayerData: @@ -186,8 +189,9 @@ def on_player_join_server(pbid: str, player_data: Optional[Dict[str, Any]], ip: return if ip in ipjoin: - last_join = ipjoin[ip]["lastJoin"] - join_count = ipjoin[ip]["count"] + ip_info = ipjoin[ip] + last_join = ip_info.last_join + join_count = ip_info.count if now - last_join < 15: join_count += 1 if join_count > 2: @@ -203,10 +207,10 @@ def on_player_join_server(pbid: str, player_data: Optional[Dict[str, Any]], ip: return else: join_count = 0 - ipjoin[ip]["count"] = join_count - ipjoin[ip]["lastJoin"] = now + ip_info.count = join_count + ip_info.last_join = now else: - ipjoin[ip] = {"lastJoin": now, "count": 0} + ipjoin[ip] = IPJoin(last_join=now, count=0) if pbid in serverdata.clients: serverdata.clients[pbid]["lastJoin"] = now @@ -234,12 +238,12 @@ def handle_existing_player(pbid: str, player_data: Dict[str, Any], ip: str, devi serverdata.recents = serverdata.recents[-20:] if check_ban(ip, device_id, pbid): - _babase.chatmessage( + _bascenev1.chatmessage( 'sad, your account is flagged contact server owner for unban', clients=[client_id]) bs.disconnect_client(client_id) return - if get_account_age(player_data["accountAge"]) < settings["minAgeToJoinInHours"]: + if get_account_age(player_data["creationDate"]) < settings["minAgeToJoinInHours"]: bs.broadcastmessage( "New Accounts not allowed here, come back later", color=(1, 0, 0), @@ -283,6 +287,7 @@ def handle_new_player_data(pbid: str, display_string: str, client_id: int) -> No """ Handles the joining process for a player with no existing data. """ + thread = FetchThread( target=my_acc_age, callback=save_age, @@ -374,18 +379,21 @@ def get_account_creation_date(pb_id: str) -> Optional[str]: Gets the account creation date for a given player ID. """ if _bascenev1.protocol_version() > 35: + try: req = urllib.request.Request( - f"https://www.ballistica.net/api/v1/accounts?ids={pb_id}", + f"https://www.ballistica.net/api/v1/accounts/{pb_id}", headers={ "Authorization": f"Bearer {settings['accountApiToken']}" }, ) with urllib.request.urlopen(req) as response: response_json_str = response.read().decode('utf-8') - accounts = json.loads(response_json_str) - if accounts: - account = dataclass_from_json(AccountResponse, accounts[0]) + account = json.loads(response_json_str) + + if account: + account = dataclass_from_json( + AccountResponse, response_json_str) return str(account.create_time) except (urllib.error.URLError, ValueError) as e: logger.log(