mirror of
https://github.com/bombsquad-community/plugin-manager.git
synced 2025-10-08 14:54:36 +00:00
added random join plugin
This commit is contained in:
parent
2c8b17629b
commit
f213f2429b
2 changed files with 329 additions and 1 deletions
313
plugins/utilities/random_join.py
Normal file
313
plugins/utilities/random_join.py
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue