From f213f2429bcf1ed5380fa4da4f87736750485878 Mon Sep 17 00:00:00 2001 From: * Date: Tue, 27 Dec 2022 03:46:21 +0530 Subject: [PATCH 1/3] added random join plugin --- plugins/utilities.json | 17 +- plugins/utilities/random_join.py | 313 +++++++++++++++++++++++++++++++ 2 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 plugins/utilities/random_join.py diff --git a/plugins/utilities.json b/plugins/utilities.json index 3eea4a5..cb5357f 100644 --- a/plugins/utilities.json +++ b/plugins/utilities.json @@ -3,6 +3,21 @@ "description": "Utilities", "plugins_base_url": "https://github.com/bombsquad-community/plugin-manager/{content_type}/{tag}/plugins/utilities", "plugins": { + "random_join": { + "description": "Come visit the unknown servers around all the world! Plugin designed not to join servers with similar names more frequently than rare ones. Have fun!", + "external_url": "", + "authors": [ + {"name": "maxick", + "email": "", + "discord": "maxick#9227"}, + {"name": "LoupGarou", + "email": "LoupGarou5418@outlook.com", + "discord": "ʟօʊքɢǟʀօʊ#3063"} + ], + "versions": { + "1.0.0": null + } + }, "share_replay": { "description": "Export replays to mods folder and share them with friends or have a backup", "external_url": "", @@ -591,4 +606,4 @@ } } } -} \ No newline at end of file +} diff --git a/plugins/utilities/random_join.py b/plugins/utilities/random_join.py new file mode 100644 index 0000000..01eb704 --- /dev/null +++ b/plugins/utilities/random_join.py @@ -0,0 +1,313 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, TypeVar + +import _ba +import ba +import ba.internal +import random +from bastd.ui.gather.publictab import PublicGatherTab, PartyEntry,PingThread +if TYPE_CHECKING: + from typing import Callable + +ClassType = TypeVar('ClassType') +MethodType = TypeVar('Methodtype') + + +def override(cls: ClassType) -> Callable[[MethodType], MethodType]: + def decorator(newfunc: MethodType) -> MethodType: + funcname = newfunc.__code__.co_name + if hasattr(cls, funcname): + oldfunc = getattr(cls, funcname) + setattr(cls, f'_old_{funcname}', oldfunc) + + setattr(cls, funcname, newfunc) + return newfunc + + return decorator + +# Can this stuff break mro? (P.S. yes, so we're not using super() anymore). +# Although it gives nice auto-completion. +# And anyways, why not just GatherPublicTab = NewGatherPublicTab? +# But hmm, if we imagine someone used `from blah.blah import Blah`, using +# `blah.Blah = NewBlah` AFTERWARDS would be meaningless. +class NewPublicGatherTab(PublicGatherTab,PingThread): + + @override(PublicGatherTab) + def _build_join_tab(self, region_width: float, + region_height: float, + oldfunc: Callable = None) -> None: + # noinspection PyUnresolvedReferences + self._old__build_join_tab(region_width, region_height) + + # Copy-pasted from original function. + c_width = region_width + c_height = region_height - 20 + sub_scroll_height = c_height - 125 + sub_scroll_width = 830 + v = c_height - 35 + v -= 60 + + self._random_join_button = ba.buttonwidget( + parent=self._container, + label='random', + size=(90, 45), + position=(710, v + 10), + on_activate_call=ba.WeakCall(self._join_random_server), + ) + ba.widget(edit=self._random_join_button, up_widget=self._host_text, + left_widget=self._filter_text) + + # We could place it somewhere under plugin settings which is kind of + # official way to customise plugins. Although it's too deep: + # Gather Window -> Main Menu -> Settings -> Advanced -(scroll)-> + # Plugins -(scroll probably)-> RandomJoin Settings. + self._random_join_settings_button = ba.buttonwidget( + parent=self._container, + icon=ba.gettexture('settingsIcon'), + size=(40, 40), + position=(820, v + 13), + on_activate_call=ba.WeakCall(self._show_random_join_settings), + ) + + @override(PublicGatherTab) + def _show_random_join_settings(self) -> None: + RandomJoinSettingsPopup( + origin_widget=self._random_join_settings_button) + + @override(PublicGatherTab) + def _get_parties_list(self) -> list[PartyEntry]: + if (self._parties_sorted and + (randomjoin.maximum_ping == 9999 or + # Ensure that we've pinged at least 10%. + len([p for k, p in self._parties_sorted + if p.ping is not None]) > len(self._parties_sorted) / 10)): + randomjoin.cached_parties = [p for k, p in self._parties_sorted] + return randomjoin.cached_parties + + @override(PublicGatherTab) + def _join_random_server(self) -> None: + name_prefixes = set() + parties = [p for p in self._get_parties_list() if + (p.size >= randomjoin.minimum_players + and p.size < p.size_max and (randomjoin.maximum_ping == 9999 + or (p.ping is not None + and p.ping <= randomjoin.maximum_ping)))] + + if not parties: + ba.screenmessage('No suitable servers found; wait', + color=(1, 0, 0)) + ba.playsound(ba.getsound('error')) + return + + for party in parties: + name_prefixes.add(party.name[:6]) + + random.choice(list(name_prefixes)) + + party = random.choice( + [p for p in parties if p.name[:6] in name_prefixes]) + + ba.internal.connect_to_party(party.address, party.port) + + +class RandomJoinSettingsPopup(ba.Window): + def __init__(self, origin_widget: ba.Widget) -> None: + c_width = 600 + c_height = 400 + uiscale = ba.app.ui.uiscale + super().__init__(root_widget=ba.containerwidget( + scale=( + 1.8 + if uiscale is ba.UIScale.SMALL + else 1.55 + if uiscale is ba.UIScale.MEDIUM + else 1.0 + ), + scale_origin_stack_offset=origin_widget.get_screen_space_center(), + stack_offset=(0, -10) + if uiscale is ba.UIScale.SMALL + else (0, 15) + if uiscale is ba.UIScale.MEDIUM + else (0, 0), + size=(c_width, c_height), + transition='in_scale', + )) + + ba.textwidget( + parent=self._root_widget, + size=(0, 0), + h_align='center', + v_align='center', + text='Random Join Settings', + scale=1.5, + color=(0.6, 1.0, 0.6), + maxwidth=c_width * 0.8, + position=(c_width * 0.5, c_height - 60), + ) + + v = c_height - 120 + ba.textwidget( + parent=self._root_widget, + size=(0, 0), + h_align='right', + v_align='center', + text='Maximum ping', + maxwidth=c_width * 0.3, + position=(c_width * 0.4, v), + ) + self._maximum_ping_edit = ba.textwidget( + parent=self._root_widget, + size=(c_width * 0.3, 40), + h_align='left', + v_align='center', + text=str(randomjoin.maximum_ping), + editable=True, + description='Maximum ping (ms)', + position=(c_width * 0.6, v - 20), + autoselect=True, + max_chars=4, + ) + v -= 60 + ba.textwidget( + parent=self._root_widget, + size=(0, 0), + h_align='right', + v_align='center', + text='Minimum players', + maxwidth=c_width * 0.3, + position=(c_width * 0.4, v), + ) + self._minimum_players_edit = ba.textwidget( + parent=self._root_widget, + size=(c_width * 0.3, 40), + h_align='left', + v_align='center', + text=str(randomjoin.minimum_players), + editable=True, + description='Minimum number of players', + position=(c_width * 0.6, v - 20), + autoselect=True, + max_chars=4, + ) + v -= 60 + + # Cancel button. + self.cancel_button = btn = ba.buttonwidget( + parent=self._root_widget, + label=ba.Lstr(resource='cancelText'), + size=(180, 60), + color=(1.0, 0.2, 0.2), + position=(40, 30), + on_activate_call=self._cancel, + autoselect=True, + ) + ba.containerwidget(edit=self._root_widget, cancel_button=btn) + + # Save button. + self.savebtn = btn = ba.buttonwidget( + parent=self._root_widget, + label=ba.Lstr(resource='saveText'), + size=(180, 60), + position=(c_width - 200, 30), + on_activate_call=self._save, + autoselect=True, + ) + ba.containerwidget(edit=self._root_widget, start_button=btn) + + def _save(self) -> None: + errored = False + minimum_players: int | None = None + maximum_ping: int | None = None + try: + minimum_players = int( + ba.textwidget(query=self._minimum_players_edit)) + except ValueError: + ba.screenmessage('"Minimum players" should be integer', + color=(1, 0, 0)) + ba.playsound(ba.getsound('error')) + errored = True + try: + maximum_ping = int( + ba.textwidget(query=self._maximum_ping_edit)) + except ValueError: + ba.screenmessage('"Maximum ping" should be integer', + color=(1, 0, 0)) + ba.playsound(ba.getsound('error')) + errored = True + if errored: + return + + assert minimum_players is not None + assert maximum_ping is not None + + if minimum_players < 0: + ba.screenmessage('"Minimum players" should be at least 0', + color=(1, 0, 0)) + ba.playsound(ba.getsound('error')) + errored = True + + if maximum_ping <= 0: + ba.screenmessage('"Maximum ping" should be greater than 0', + color=(1, 0, 0)) + ba.playsound(ba.getsound('error')) + ba.screenmessage('(use 9999 as dont-care value)', + color=(1, 0, 0)) + errored = True + + if errored: + return + + randomjoin.maximum_ping = maximum_ping + randomjoin.minimum_players = minimum_players + + randomjoin.commit_config() + ba.playsound(ba.getsound('shieldUp')) + self._transition_out() + + def _cancel(self) -> None: + ba.playsound(ba.getsound('shieldDown')) + self._transition_out() + + def _transition_out(self) -> None: + ba.containerwidget(edit=self._root_widget, transition='out_scale') + + +class RandomJoin: + def __init__(self) -> None: + self.cached_parties: list[PartyEntry] = [] + self.maximum_ping: int = 9999 + self.minimum_players: int = 2 + self.load_config() + + def load_config(self) -> None: + cfg = ba.app.config.get('Random Join', { + 'maximum_ping': self.maximum_ping, + 'minimum_players': self.minimum_players, + }) + try: + self.maximum_ping = cfg['maximum_ping'] + self.minimum_players = cfg['minimum_players'] + except KeyError: + ba.screenmessage('Error: RandomJoin config is broken, resetting..', + color=(1, 0, 0), log=True) + ba.playsound(ba.getsound('error')) + self.commit_config() + + def commit_config(self) -> None: + ba.app.config['Random Join'] = { + 'maximum_ping': self.maximum_ping, + 'minimum_players': self.minimum_players, + } + ba.app.config.commit() + + +randomjoin = RandomJoin() + + +# ba_meta require api 7 +# ba_meta export ba.Plugin +class RandomJoinPlugin(ba.Plugin): + def on_app_running(self) -> None: + # I feel bad that all patching logic happens not here. + pass From 2454845a384a6220939659038a8b97effa1ccadf Mon Sep 17 00:00:00 2001 From: Loup-Garou911XD Date: Mon, 26 Dec 2022 22:17:50 +0000 Subject: [PATCH 2/3] [ci] auto-format --- plugins/utilities/random_join.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/utilities/random_join.py b/plugins/utilities/random_join.py index 01eb704..955b1a2 100644 --- a/plugins/utilities/random_join.py +++ b/plugins/utilities/random_join.py @@ -6,7 +6,7 @@ import _ba import ba import ba.internal import random -from bastd.ui.gather.publictab import PublicGatherTab, PartyEntry,PingThread +from bastd.ui.gather.publictab import PublicGatherTab, PartyEntry, PingThread if TYPE_CHECKING: from typing import Callable @@ -31,8 +31,10 @@ def override(cls: ClassType) -> Callable[[MethodType], MethodType]: # And anyways, why not just GatherPublicTab = NewGatherPublicTab? # But hmm, if we imagine someone used `from blah.blah import Blah`, using # `blah.Blah = NewBlah` AFTERWARDS would be meaningless. -class NewPublicGatherTab(PublicGatherTab,PingThread): - + + +class NewPublicGatherTab(PublicGatherTab, PingThread): + @override(PublicGatherTab) def _build_join_tab(self, region_width: float, region_height: float, @@ -87,13 +89,13 @@ class NewPublicGatherTab(PublicGatherTab,PingThread): @override(PublicGatherTab) def _join_random_server(self) -> None: - name_prefixes = set() + name_prefixes = set() parties = [p for p in self._get_parties_list() if (p.size >= randomjoin.minimum_players and p.size < p.size_max and (randomjoin.maximum_ping == 9999 - or (p.ping is not None - and p.ping <= randomjoin.maximum_ping)))] - + or (p.ping is not None + and p.ping <= randomjoin.maximum_ping)))] + if not parties: ba.screenmessage('No suitable servers found; wait', color=(1, 0, 0)) From 09206d636ad0a58c010cbc661ddf8886da2b7442 Mon Sep 17 00:00:00 2001 From: Loup-Garou911XD Date: Mon, 26 Dec 2022 22:17:51 +0000 Subject: [PATCH 3/3] [ci] apply-version-metadata --- plugins/utilities.json | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/utilities.json b/plugins/utilities.json index cb5357f..8f801af 100644 --- a/plugins/utilities.json +++ b/plugins/utilities.json @@ -7,15 +7,24 @@ "description": "Come visit the unknown servers around all the world! Plugin designed not to join servers with similar names more frequently than rare ones. Have fun!", "external_url": "", "authors": [ - {"name": "maxick", + { + "name": "maxick", "email": "", - "discord": "maxick#9227"}, - {"name": "LoupGarou", + "discord": "maxick#9227" + }, + { + "name": "LoupGarou", "email": "LoupGarou5418@outlook.com", - "discord": "ʟօʊքɢǟʀօʊ#3063"} + "discord": "ʟօʊքɢǟʀօʊ#3063" + } ], "versions": { - "1.0.0": null + "1.0.0": { + "api_version": 7, + "commit_sha": "2454845", + "released_on": "26-12-2022", + "md5sum": "7bac6bfe837ff89e7da10a0ab45691d1" + } } }, "share_replay": { @@ -606,4 +615,4 @@ } } } -} +} \ No newline at end of file