Added plugins

This commit is contained in:
BroBordd 2025-08-03 13:53:51 +03:00
parent 0bffcc0a67
commit 38417928cd
9 changed files with 3791 additions and 31 deletions

View file

@ -55,11 +55,11 @@
},
"sandbox": {
"description": "Spawn, control and bots, change music, add teams, spawn objects, change game values in real time, and much more!",
"external_url": "",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "thehero2012008@gmail.com",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
@ -86,15 +86,16 @@
},
"updown": {
"description": "Adds UP and DOWN buttons in party window to recall messages in chat.",
"external_url": "",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "thehero2012008@gmail.com",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"1.1.4": null,
"1.1.3": {
"api_version": 9,
"commit_sha": "e604a3c",
@ -128,12 +129,12 @@
}
},
"rejoin": {
"description": "Adds a button in pause menu, which rejoins current server once clicked! if didn't work, just click again and again. if still, then party is full.",
"external_url": "",
"description": "Deprecated - Better use Power plugin. Adds a button in pause menu, which rejoins current server once clicked! if didn't work, just click again and again. if still, then party is full.",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "thehero2012008@gmail.com",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
@ -164,17 +165,108 @@
}
}
},
"topmsg": {
"description": "When chat is muted, see new chat messages on top right! (like kill logs)",
"external_url": "",
"power": {
"description": "With one click. Experimental. Adds a dev console tab with some features I find useful. Power is mainly focused on the multiplayer side. Can be considered a good tool to have around.",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "thehero2012008@gmail.com",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"2.7": null
}
}
},
"plugtools": {
"description": "Live Plugin Action. Beta. Adds a dev console tab for plugin management. For full features, read the first lines in the py file, or look in the source.",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"1.5": null
}
}
},
"camera": {
"description": "Say cheese. Adds a button to pause menu. Camera is advanced Camera allows you to change camera position and target with a very easy graphical visualization of how it would look like.",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"1.0": null
}
}
},
"finder": {
"description": "Find anyone. Experimental. Useful if you are looking for someone, or just messing around. For full features, either check first lines of py file, or check source. Combine with Power plugin for better control.",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"1.0": null
}
}
},
"path": {
"description": "Where it's going to be. Experimental. Path tries to predict the next position of bomb. Path relies on velocity to operate. Optionally pass spaz node (holder) to assist prediction.",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"1.0": null
}
}
},
"replay": {
"description": "Simple replay player. Experimental. Adds a button to pause menu and watch menu. For full features, either read first lines of py file, or check source.",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"1.5": null
}
}
},
"topmsg": {
"description": "When chat is muted, see new chat messages on top right! (like kill logs)",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
"versions": {
"1.1.2": null,
"1.1.1": {
"api_version": 9,
"commit_sha": "e604a3c",
@ -197,11 +289,11 @@
},
"sorry": {
"description": "Send a random sorry to chat, preventing revenge attempts from teammates you kill by mistake.",
"external_url": "",
"external_url": "https://brobordd.github.io/byBordd",
"authors": [
{
"name": "BrotherBoard",
"email": "thehero2012008@gmail.com",
"email": "brobordd@gmail.com",
"discord": "BrotherBoard"
}
],
@ -2140,4 +2232,4 @@
}
}
}
}
}

586
plugins/utilities/camera.py Executable file
View file

@ -0,0 +1,586 @@
# Copyright 2025 - Solely by BrotherBoard
# Bug? Feedback? Telegram >> @GalaxyA14user
"""
Camera v1.0 - Say cheese.
Adds a button to pause menu. Camera is advanced
Camera allows you to change camera position and
target with a very easy graphical visualization
of how it would look like.
"""
from _babase import (
get_display_resolution as GDR,
clipboard_is_supported as CIS,
set_camera_position as SCP,
clipboard_set_text as COPY,
set_camera_manual as SSCM,
set_camera_target as SCT
)
from bascenev1 import (
get_foreground_host_activity as ga,
OutOfBoundsMessage,
gettexture as gbt,
getsound as gbs,
timer as tick,
Material,
getmesh,
newnode,
animate,
emitfx
)
from bauiv1 import (
get_special_widget as gsw,
containerwidget as cw,
screenmessage as push,
buttonwidget as bw,
imagewidget as iw,
textwidget as tw,
gettexture as gt,
apptimer as teck,
getsound as gs,
app as APP
)
from bauiv1lib.ingamemenu import InGameMenuWindow as igm
from babase import Plugin, InputType as IT
from math import sqrt, dist
class Camera:
__doc__ = 'A simple camera.'
__ins__ = None
__lst__ = None
__yes__ = False
def __init__(s) -> None:
c = s.__class__
if c.__yes__:
note('Stopped camera!',True)
c.__ins__.done(talk=False)
return
c.__ins__ = s
c.__yes__ = True
if c.__lst__: SCM(False)
s.stage = 0
p = (0,1,0)
s.tex = 'achievementCrossHair'
s.kids = []
s.okay = []
with ga().context:
s.M = Material()
s.M.add_actions(
conditions=(('they_are_older_than', 0)),
actions=(
('modify_part_collision', 'collide', False),
('modify_part_collision', 'physical', False),
('modify_part_collision', 'friction', 0),
('modify_part_collision', 'stiffness', 0),
('modify_part_collision', 'damping', 0)
)
)
n = newnode(
'prop',
delegate=s,
attrs={
'mesh': getmesh('tnt'),
'color_texture': gbt(s.tex),
'body': 'crate',
'reflection': 'soft',
'density': 4.0,
'reflection_scale': [1.5],
'shadow_size': 0.6,
'position': p,
'gravity_scale': 0,
'materials': [s.M],
'is_area_of_interest': True
}
)
tick(0.15, animate(n,'mesh_scale',{0:2,0.1:0.5}).delete)
gbs('powerup01').play(position=p)
s.step = 0.01
s.node = n
s.wait = 0.001
s.mode = 4
s.llr = s.lud = 0.0
s.overlay = Overlay()
LN({
'UP_DOWN': lambda a: s.manage(a),
'LEFT_RIGHT': lambda a: s.manage(a,1),
'PICK_UP_PRESS': lambda: s.start(2),
'JUMP_PRESS': lambda: s.start(0),
'PICK_UP_RELEASE': lambda: s.stop(2),
'JUMP_RELEASE': lambda: s.stop(0),
'BOMB_PRESS': s.done,
'BOMB_RELEASE': lambda: s.overlay.release(1),
'PUNCH_PRESS': s.mark,
'PUNCH_RELEASE': lambda: s.overlay.release(3),
})
s.move()
"""Write a tip"""
def tip(s,t,p,h='left',b=True):
n = newnode(
'text',
attrs={
'in_world': True,
'scale': 0.01,
'flatness': 1,
'color': (1,1,1),
'shadow': 1.0,
'position': p,
'text': t,
'h_align': h
}
)
if b: s.kids.append(n)
return n
"""Create a dot"""
def dot(s,p,b=True,tex='black'):
n = newnode(
'prop',
delegate=s,
attrs={
'mesh': getmesh('tnt'),
'color_texture': gbt(tex),
'body': 'crate',
'mesh_scale': 0.1,
'position': p,
'gravity_scale': 0,
'materials': [s.M],
}
)
if b: s.kids.append(n)
return n
"""Draw a line"""
def line(s,p1,p2,i=2,tex='black'):
x1,y1,z1 = p1
x2,y2,z2 = p2
n = dist(p1,p2)*i
for i in range(1,int(n+1)):
t = i/n
x = x1+t*(x2-x1)
y = y1+t*(y2-y1)
z = z1+t*(z2-z1)
s.kids.append(s.dot((x,y,z),tex=tex))
"""Mark"""
def mark(s):
if not s.stage:
s.stage = 1
p = s.getpos()
s.p1 = (p[0]-0.01,p[1],p[2])
s.okay.append(s.dot(s.p1,b=False))
s.okay.append(s.tip(f'Camera position\n{tuple([round(i,2) for i in s.p1])}',s.p1,b=False))
else:
[i.delete() for i in s.kids]
s.kids.clear()
p2 = s.p2 = s.getpos()
r = GDR()
w = r[0]/r[1]
h = 1
vd = sub(p2, s.p1)
vd_n = norm(vd)
t_up = (0, 1, 0)
r_v = cross(vd_n, t_up)
r_v_n = norm(r_v)
up_v = cross(r_v_n, vd_n)
up_v_n = norm(up_v)
hw = w / 2.0
hh = h / 2.0
tr = add(p2, add(scale(r_v_n, hw), scale(up_v_n, hh)))
tl = add(p2, add(scale(r_v_n, -hw), scale(up_v_n, hh)))
br = add(p2, add(scale(r_v_n, hw), scale(up_v_n, -hh)))
bl = add(p2, add(scale(r_v_n, -hw), scale(up_v_n, -hh)))
s.line(s.p1, tr)
s.line(s.p1, tl)
s.line(s.p1, br)
s.line(s.p1, bl)
m = 4
j = {'tex':'crossOutMask'}
s.line(tr, tl, m, **j)
s.line(tl, bl, m, **j)
s.line(bl, br, m, **j)
s.line(br, tr, m, **j)
s.tip(f'Your display\n{r[0]}x{r[1]} px\n{tuple([round(i,2) for i in p2])}',tr,'right')
s.stage = 2
s.overlay.press(3)
tick(0.25, animate(s.node,'mesh_scale',{0:0.5,0.1:0.2,0.2:0.5}).delete)
gbs('gunCocking').play(position=s.node.position)
"""Handle events"""
def handlemessage(s, m):
if isinstance(m, OutOfBoundsMessage):
p = s.getpos()
gbs('shatter').play(position=p)
emitfx(
scale=1,
count=30,
spread=0.1,
position=p,
chunk_type='ice'
)
s.destroy()
note('Out of bounds!')
"""Destroy"""
def destroy(s):
with ga().context:
n = s.node
s.mode = 2
n.delete()
s.reset()
"""Reset input"""
def reset(s):
s.__class__.__yes__ = False
me = getme()
if not me: return
me.resetinput()
with ga().context: me.actor.connect_controls_to_player()
[i.delete() for i in (s.kids+s.okay)]
"""Manage movement"""
def manage(s,a,lr=0):
if lr: s.llr = a; return
s.lud = a
"""Move"""
def move(s):
m = getme(1)
if (not m) or m._dead: s.destroy()
try: p = s.getpos()
except:
s.overlay.destroy()
return
s.setpos((p[0]+s.llr*s.step,p[1],p[2]-s.lud*s.step))
s.overlay.up(*p,s.llr,s.lud)
SCT(*p)
teck(s.wait,s.move)
"""Start elevating"""
def start(s,i):
s.overlay.press(i)
s.mode = i
s.loop(i)
"""Keep elevating"""
def loop(s,i):
if s.mode != i: return
try: p = list(s.node.position)
except: return
p[1] += s.step if i else -s.step
s.node.position = tuple(p)
teck(s.wait, lambda: s.loop(i))
"""Stop elevating"""
def stop(s,i):
s.overlay.release(i)
s.mode = 4
"""Get position"""
def getpos(s):
return s.node.position
"""Set position"""
def setpos(s,p):
s.node.position = p
"""Done"""
def done(s,talk=True):
s.overlay.press(1)
s.overlay.destroy()
try: p = s.node.position
except: return
with ga().context:
gbs('laser').play(position=p)
tick(0.2,animate(s.node,'mesh_scale',{0:0.5,0.08:1,0.2:0}).delete)
tick(0.2,s.node.delete)
s.reset()
if s.stage > 1 and talk:
SCM(True)
SCP(*s.p1)
SCT(*s.p2)
var('lp1',s.p1)
var('lp2',s.p2)
nice('Applied!')
elif talk:
note('Incomplete camera setup\nNo changes applied.')
if s.__class__.__ins__ == s: s.__class__.__ins__ = None
"""Controls overlay"""
class Overlay:
__lst__ = None
"""Place nodes"""
def __init__(s):
s.__class__.__lst__ = str(ga())
s.colors = [
[(0.2,0.6,0.2),(0.4,1,0.4)],
[(0.6,0,0),(1,0,0)],
[(0.2,0.6,0.6),(0.4,1,1)],
[(0.6,0.6,0.2),(1,1,0.4)],
[(0.3,0.23,0.5),(0.2,0.13,0.3)]
]
s.pics = []
s.texts = []
s.pos = []
s.nub = []
s.old = [0,0,0]
s.dead = False
with ga().context:
for i in range(4):
j = ['Jump','Bomb','PickUp','Punch'][i]
k = [600,650,600,550][i]
l = [170,220,270,220][i]
c = s.colors[i][0]
n = newnode(
'image',
attrs={
'texture': gbt('button'+j),
'absolute_scale': True,
'position': (k,l),
'scale': (60,60),
'color': c
}
)
s.pics.append(n)
j = ['Down','Done','Up','Mark'][i]
k = [600,680,600,515][i]
l = [115,220,325,220][i]
h = ['center','left','center','right'][i]
v = ['bottom','center','top','center'][i]
n = newnode(
'text',
attrs={
'text': j,
'position': (k,l),
'color': c,
'h_align': h,
'v_align': v
}
)
s.texts.append(n)
for i in range(3):
c = s.colors[[1,0,2][i]][0]
n = newnode(
'text',
attrs={
'text': '0',
'position': (640,155-30*i),
'color': c,
'h_align': 'left'
}
)
s.pos.append(n)
s.np = (790,140)
for i in [0,1]:
j = [110,60][i]
n = newnode(
'image',
attrs={
'texture': gbt('nub'),
'absolute_scale': True,
'position': s.np,
'scale': (j,j),
'color': s.colors[4][i]
}
)
s.nub.append(n)
s.fade()
"""Color overlays"""
def set(s,i,c):
s.pics[i].color = s.texts[i].color = c
"""Color position"""
def pset(s,i,c):
s.pos[i].color = c
"""Simulate pressed"""
def press(s,i):
s.set(i,s.colors[i][1])
s.pics[i].opacity = 1.0
"""Simulate released"""
def release(s,i):
s.set(i,s.colors[i][0])
s.pics[i].opacity = 0.7
"""Get all nodes"""
def nodes(s):
return s.pics+s.texts+s.pos+s.nub
"""Update position"""
def up(s,x,y,z,lr,ud):
new = [x,y,z]
for i in range(3):
c = s.colors[[1,0,2][i]]
if s.old[i] == new[i]: s.pset(i,c[0]); continue
t = s.pos[i]
t.text = str(round(new[i],5))
s.pset(i,c[1])
s.old = new
[setattr(s.nub[i],'opacity',[[0.5,0.2],[0.7,0.3]][bool(lr or ud)][i]) for i in [0,1]]
p = s.np
m = sqrt(lr**2+ud**2) or 1
d = 25*min(sqrt(lr**2+ud**2),1)
lr /= m
ud /= m
s.nub[1].position = (p[0]+lr*d,p[1]+ud*d)
"""Fade"""
def fade(s,i=0):
if str(ga()) != s.__class__.__lst__: return
mem = s.nodes()
[tick(1, animate(n,'opacity',{0:i,0.5:abs(i-0.7)}).delete) for n in mem]
"""Destroy overlay"""
def destroy(s):
if s.dead: return
s.dead = True
with ga().context:
tick(0.2,lambda:s.fade(0.7))
tick(2,lambda: [n.delete() for n in s.nodes()])
# Mini tools
note = lambda t, b=False: (push(t,color=(1,1,0)),gs('block').play() if b else None)
nice = lambda t: (push(t,color=(0,1,0)),gs('dingSmallHigh').play())
SCM = lambda b: (setattr(Camera,'__lst__',b),SSCM(b))
scale = lambda v,s: (v[0]*s,v[1]*s,v[2]*s)
cross = lambda a,b: (a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0])
sub = lambda a,b: (a[0]-b[0],a[1]-b[1],a[2]-b[2])
add = lambda a,b: (a[0]+b[0],a[1]+b[1],a[2]+b[2])
def getme(actor=0):
for p in ga().players:
if p.sessionplayer.inputdevice.client_id == -1:
return p.actor if actor else p
def LN(d): me = getme(); [me.assigninput(getattr(IT,k), d[k]) for k in d]
def RESUME():
u = APP.ui_v1
c = APP.classic
c.resume()
u.clear_main_window()
[z() for z in c.main_menu_resume_callbacks]
c.main_menu_resume_callbacks.clear()
def norm(v):
a,b,c = v
l = sqrt(a**2+b**2+c**2)
return (0, 0, 0) if l == 0 else (a/l,b/l,c/l)
def var(s,v=None):
c = APP.config
s = 'cam_'+s
if v is None: return c.get(s,v)
c[s] = v
c.commit()
# brobord collide grass
# ba_meta require api 9
# ba_meta export babase.Plugin
class byBordd(Plugin):
has_settings_ui = lambda s: True
show_settings_ui = lambda s, src: s.ui(source=src)
col = (0.18,0.18,0.18)
def __init__(s):
o = igm._refresh_in_game
def e(f,*a,**k):
r = o(f,*a,**k)
b = bw(
label='',
size=(90,40),
button_type='square',
parent=f._root_widget,
color=(0.18,0.18,0.18),
position=(f._width-20,0),
)
bw(b,on_activate_call=lambda:s.ui(source=b,main=True))
iw(
size=(40,40),
texture=gt('tv'),
parent=f._root_widget,
position=(f._width-20,5)
)
tw(
maxwidth=50,
text='Camera',
h_align='left',
parent=f._root_widget,
position=(f._width+15,0)
)
return r
igm._refresh_in_game = e
"""The UI"""
def ui(s,source=None,main=False):
s.main = main
off = source.get_screen_space_center() if source else (0,0)
w = cw(
color=s.col,
size=(350,305),
stack_offset=off,
transition='in_scale',
parent=gsw('overlay_stack'),
scale_origin_stack_offset=off
)
s.back = lambda b=True: (cw(w,transition=['out_right','out_scale'][bool(source) and b]),gs('swish').play() if b else None)
cw(w,on_outside_click_call=s.back)
b = Camera.__yes__
t = [
('Camera is ready!',(0,1,1)),
('Camera is running!',(0,1,0)),
][b]
tw(
parent=w,
text=t[0],
scale=1.5,
color=t[1],
h_align='center',
position=(155,250)
)
for i in range(4):
j = [
('3D Camera mapper',s.start),
('Last mapped config',s.load),
('Last dev command',s.copy),
('Reset all settings',s.reset)
][i]
tw(
parent=w,
text=j[0],
maxwidth=195,
position=(20,30+55*i)
)
k = [
(['Start','Stop'][b],'cursor'),
('Load','achievementOutline'),
('Copy','file'),
('Reset','replayIcon')
][i]
bw(
parent=w,
label=k[0],
color=s.col,
size=(120,50),
icon=gt(k[1]),
enable_sound=not i,
textcolor=(1,1,1),
button_type='square',
on_activate_call=j[1],
position=(220,20+55*i)
)
"""Gather last"""
def gather(s):
return var('lp1'),var('lp2')
"""Reset"""
def reset(s):
SCM(False)
nice('Resetored original settings!')
"""Copy last"""
def copy(s):
if not CIS(): note('Unsupported!',True); return
g = s.gather()
if not g[1]: note('Apply something first!',True); return
g = [tuple([round(i,2) for i in j]) for j in g]
COPY(f'from _babase import set_camera_manual as SCM, set_camera_target as SCT, set_camera_position as SCP; SCM(True); SCP(*{g[0]}); SCT(*{g[1]})')
nice('Copied command!\nPaste it in dev console anytime to load config!')
"""Load last"""
def load(s):
g = s.gather()
if not g[1]: note('Apply something first!',True); return
if Camera.__yes__: note('Stop camera first!',True); return
SCM(True)
SCP(*g[0])
SCT(*g[1])
nice('Loaded last config!')
"""Start camera"""
def start(s):
a = ga()
if not a: note('Only mapping requires you to be the host!\nYou still can load previous config though',True); return
if not getme(): note('Join the game first!',True); return
s.back(False)
RESUME() if s.main else None
with a.context: Camera()

471
plugins/utilities/finder.py Executable file
View file

@ -0,0 +1,471 @@
# Copyright 2025 - Solely by BrotherBoard
# Intended for personal use only
# Bug? Feedback? Telegram >> GalaxyA14user
"""
Finder v1.0 - Find anyone
Experimental. Feedback is appreciated.
Useful if you are looking for someone, or just messing around.
Features:
- Fetch servers: Pings all servers, then sorts them by lowest
- Ability to cycle through x servers to collect users
- Ability to connect to servers by player name there
Combine with Power plugin for better control.
"""
from socket import socket, SOCK_DGRAM
from random import uniform as uf
from babase import Plugin, app
from threading import Thread
from time import time, sleep
from bauiv1 import (
get_ip_address_type as IPT,
clipboard_set_text as COPY,
get_special_widget as zw,
containerwidget as ocw,
screenmessage as push,
buttonwidget as obw,
scrollwidget as sw,
imagewidget as iw,
textwidget as tw,
gettexture as gt,
apptimer as teck,
getsound as gs,
getmesh as gm,
Call
)
from bascenev1 import (
disconnect_from_host as BYE,
connect_to_party as CON,
protocol_version as PT,
get_game_roster as GGR
)
class Finder:
COL1 = (0,0.3,0.3)
COL2 = (0,0.55,0.55)
COL3 = (0,0.7,0.7)
COL4 = (0,1,1)
COL5 = (1,1,0)
MAX = 0.3
TOP = 15
VER = '1.0'
MEM = []
BST = []
SL = None
def __init__(s,src):
s.thr = []
s.ikids = []
s.busy = False
s.s1 = s.snd('powerup01')
c = s.__class__
# parent
z = (460,400)
s.p = cw(
scale_origin_stack_offset=src.get_screen_space_center(),
size=z,
oac=s.bye
)[0]
# footing
sw(
parent=s.p,
size=z,
border_opacity=0
)
# fetch
tw(
parent=s.p,
text='Fetch Servers',
color=s.COL4,
position=(19,359)
)
bw(
parent=s.p,
position=(360,343),
size=(80,39),
label='Fetch',
color=s.COL2,
textcolor=s.COL4,
oac=s.fresh
)
tw(
parent=s.p,
text='Fetches, pings, and sorts public servers.',
color=s.COL3,
scale=0.8,
position=(15,330),
maxwidth=320
)
# separator
iw(
parent=s.p,
size=(429,1),
position=(17,330),
texture=gt('white'),
color=s.COL2
)
# cycle
tw(
parent=s.p,
text='Cycle Servers',
color=s.COL4,
position=(19,294)
)
bw(
parent=s.p,
position=(360,278),
size=(80,39),
label='Cycle',
color=s.COL2,
textcolor=s.COL4,
oac=s.find
)
tw(
parent=s.p,
text='Cycles through best servers and saves their players.',
color=s.COL3,
scale=0.8,
position=(15,265),
maxwidth=320,
v_align='center'
)
# separator
iw(
parent=s.p,
size=(429,1),
position=(17,265),
texture=gt('white'),
color=s.COL2
)
# top
tw(
parent=s.p,
text='Server Cycle Limit',
color=s.COL4,
position=(19,230)
)
s.top = tw(
parent=s.p,
position=(398,228),
size=(80,50),
text=str(c.TOP),
color=s.COL4,
editable=True,
h_align='center',
v_align='center',
corner_scale=0.1,
scale=10,
allow_clear_button=False,
shadow=0,
flatness=1,
)
tw(
parent=s.p,
text='Maximum number of servers to cycle.',
color=s.COL3,
scale=0.8,
position=(15,201),
maxwidth=320
)
# separator
iw(
parent=s.p,
size=(429,1),
position=(17,200),
texture=gt('white'),
color=s.COL2
)
# players
pl = s.plys()
sy = max(len(pl)*30,140)
p1 = sw(
parent=s.p,
position=(20,18),
size=(205,172),
border_opacity=0.4
)
p2 = ocw(
parent=p1,
size=(205,sy),
background=False
)
0 if pl else tw(
parent=s.p,
position=(90,100),
text='Cycle some servers\nto collect players',
color=s.COL4,
maxwidth=175,
h_align='center'
)
s.kids = []
for _,g in enumerate(pl):
p,a = g
s.kids.append(tw(
parent=p2,
size=(200,30),
selectable=True,
click_activate=True,
color=s.COL3,
text=p,
position=(0,sy-30-30*_),
maxwidth=175,
on_activate_call=Call(s.hl,_,p),
v_align='center'
))
# info
iw(
parent=s.p,
position=(235,18),
size=(205,172),
texture=gt('scrollWidget'),
mesh_transparent=gm('softEdgeOutside'),
opacity=0.4
)
s.tip = tw(
parent=s.p,
position=(310,98),
text='Select something to\nview server info',
color=s.COL4,
maxwidth=170,
h_align='center'
) if c.SL is None else 0
def hl(s,_,p):
[tw(t,color=s.COL3) for t in s.kids]
tw(s.kids[_],color=s.COL4)
s.info(p)
def info(s,p):
[_.delete() for _ in s.ikids]
s.ikids.clear()
s.tip and s.tip.delete()
bst = s.__class__.BST
for _ in bst:
for r in _['roster']:
if r['display_string'] == p:
i = _
break
for _ in range(3):
t = str(i['nap'[_]])
s.ikids.append(tw(
parent=s.p,
position=(250,155-40*_),
h_align='center',
v_align='center',
maxwidth=175,
text=t,
color=s.COL4,
size=(175,30),
selectable=True,
click_activate=True,
on_activate_call=Call(s.copy,t)
))
s.ikids.append(bw(
parent=s.p,
position=(253,30),
size=(166,30),
label='Connect',
color=s.COL2,
textcolor=s.COL4,
oac=Call(CON,i['a'],i['p'],False)
))
def copy(s,t):
s.ding(1,1)
TIP('Copied to clipboard!')
COPY(t)
def plys(s):
z = []
me = app.plus.get_v1_account_name()
me = [me,'\ue063'+me]
for _ in s.__class__.BST:
a = _['a']
if (r:=_.get('roster',{})):
for p in r:
ds = p['display_string']
0 if ds in me else z.append((ds,a))
return sorted(z,key=lambda _: _[0].startswith('\ue030Server'))
def snd(s,t):
l = gs(t)
l.play()
teck(uf(0.14,0.18),l.stop)
return l
def bye(s):
s.s1.stop()
ocw(s.p,transition='out_scale')
l = s.snd('laser')
f = lambda: teck(0.01,f) if s.p else l.stop()
f()
def ding(s,i,j):
a = ['Small','']
x,y = a[i],a[j]
s.snd('ding'+x)
teck(0.1,gs('ding'+y).play)
def fresh(s):
if s.busy: BTW("Still busy!"); return
TIP('Fetching servers...')
s.ding(1,0)
s.busy = True
p = app.plus
p.add_v1_account_transaction(
{
'type': 'PUBLIC_PARTY_QUERY',
'proto': PT(),
'lang': 'English'
},
callback=s.kang,
)
p.run_v1_account_transactions()
def kang(s,r):
c = s.__class__
c.MEM = r['l']
s.thr = []
for _ in s.__class__.MEM:
t = Thread(target=Call(s.ping,_))
s.thr.append(t)
t.start()
teck(s.MAX*4,s.join)
def join(s):
c = s.__class__
[t.join() for t in s.thr]
far = s.MAX*3000
c.MEM = [_ for _ in c.MEM if _['ping']]
c.MEM.sort(key=lambda _: _['ping'])
s.thr.clear()
TIP(f'Loaded {len(c.MEM)} servers!')
s.ding(0,1)
s.busy = False
def find(s):
if s.busy: BTW("Still busy!"); return
c = s.__class__
if not c.MEM:
BTW('Fetch some servers first!')
return
t = tw(query=s.top)
if not t.isdigit():
BTW('Invalid cycle limit!')
return
top = int(t)
if not (0 < top < len(c.MEM)):
BTW('Cycle count is too '+['big','small'][top<=0]+'!')
return
c.TOP = top
s.ding(1,0)
TIP('Starting cycle...')
s.busy = True
s.ci = s.lr = 0
c.BST = c.MEM[:top]
s.cycle()
def cycle(s):
_ = s.__class__.BST[s.ci]
s.ca = _['a']
CON(s.ca,_['p'],False)
s.wait()
def wait(s,i=5):
r = GGR()
if (r != s.lr) and r: s.__class__.BST[s.ci]['roster'] = s.lr = r; return s.next()
if not i: s.__class__.BST[s.ci]['roster'] = []; return s.next()
teck(0.1,Call(s.wait,i-1))
def next(s):
s.ci += 1
if s.ci >= len(s.__class__.BST):
BYE()
teck(0.5,s.yay)
return
s.cycle()
def yay(s):
TIP('Cycle finished!')
s.ding(0,1)
s.busy = False
zw('squad_button').activate()
teck(0.3,byBordd.up)
def ping(s,_):
sock = ping = None
a,p = _['a'],_['p']
sock = socket(IPT(a),SOCK_DGRAM)
try: sock.connect((a,p))
except: ping = None
else:
st = time()
sock.settimeout(s.MAX)
yes = False
for _i in range(3):
try:
sock.send(b'\x0b')
r = sock.recv(10)
except: r = None
if r == b'\x0c':
yes = True
break
sleep(s.MAX)
ping = (time()-st)*1000 if yes else None
finally:
_['ping'] = ping
sock.close()
# Patches
bw = lambda *,oac=None,**k: obw(
texture=gt('white'),
on_activate_call=oac,
enable_sound=False,
**k
)
cw = lambda *,size=None,oac=None,**k: (p:=ocw(
parent=zw('overlay_stack'),
background=False,
transition='in_scale',
size=size,
on_outside_click_call=oac,
**k
)) and (p,iw(
parent=p,
texture=gt('softRect'),
size=(size[0]*1.2,size[1]*1.2),
position=(-size[0]*0.1,-size[1]*0.1),
opacity=0.55,
color=(0,0,0)
),iw(
parent=p,
size=size,
texture=gt('white'),
color=Finder.COL1
))
# Global
BTW = lambda t: (push(t,color=(1,1,0)),gs('block').play())
TIP = lambda t: push(t,Finder.COL3)
# ba_meta require api 9
# ba_meta export babase.Plugin
class byBordd(Plugin):
BTN = None
@classmethod
def up(c):
c.BTN.activate() if c.BTN.exists() else None
def __init__(s):
from bauiv1lib import party
p = party.PartyWindow
a = '__init__'
o = getattr(p,a)
setattr(p,a,lambda z,*a,**k:(o(z,*a,**k),s.make(z))[0])
def make(s,z):
sz = (80,30)
p = z._root_widget
x,y = (-60,z._height-45)
iw(
parent=p,
size=(sz[0]*1.34,sz[1]*1.4),
position=(x-sz[0]*0.14,y-sz[1]*0.20),
texture=gt('softRect'),
opacity=0.2,
color=(0,0,0)
)
s.b = s.__class__.BTN = bw(
parent=p,
position=(x,y),
label='Finder',
color=Finder.COL1,
textcolor=Finder.COL3,
size=sz,
oac=lambda:Finder(s.b)
)

88
plugins/utilities/path.py Executable file
View file

@ -0,0 +1,88 @@
# Copyright 2025 - Solely by BrotherBoard
# Bug? Feedback? Telegram >> @GalaxyA14user
"""
Path v1.0 - Where it's going to be.
Experimental. Path tries to predict the next position of bomb.
Path relies on velocity to operate.
Optionally pass spaz node (holder) to assist prediction.
Feedback is appreciated.
"""
from babase import Plugin
from bascenev1 import (
timer as tick,
newnode
)
class Path:
def __init__(s,node,holder=None):
if node.body == 'crate': return
s.node,s.kids = node,[]
s.me = holder
s.spy()
def spy(s):
n = s.node
if not n.exists():
[_.delete() for _ in s.kids]
s.kids.clear()
return
[_.delete() for _ in s.kids]; s.kids.clear()
ip = n.position
iv = n.velocity
if s.me and s.me.hold_node == n:
mv = s.me.velocity
iv = (iv[0]+mv[0],iv[1]+mv[1],iv[2]+mv[2])
dots = 200
ti = 1.2
tpd = ti / dots
tick(0.01, s.spy)
for i in range(dots):
t = i * tpd
px = ip[0] + iv[0] * t
py = ip[1] + iv[1] * t + 0.5 * -24 * t**2
pz = ip[2] + iv[2] * t
if py <=0:
l = newnode(
'locator',
owner=n,
attrs={
'shape': 'circleOutline',
'size': [1],
'color': (1,1,0),
'draw_beauty': False,
'additive': True,
'position':(px,py,pz)
}
)
s.kids.append(l)
break
dot_node = newnode(
'text',
owner=n,
attrs={
'text':'.',
'scale':0.02,
'position':(px, py, pz),
'flatness':1,
'in_world':True,
'color':(1-i*4/dots,0,0),
'shadow':0
}
)
s.kids.append(dot_node)
# brobord collide grass
# ba_meta require api 9
# ba_meta export babase.Plugin
class byBordd(Plugin):
def __init__(s):
_ = __import__('bascenev1lib').actor.bomb.Bomb
o = _.__init__
_.__init__ = lambda z,*a,**k: (o(z,*a,**k),Path(z.node))[0]

475
plugins/utilities/plugtools.py Executable file
View file

@ -0,0 +1,475 @@
# Copyright 2025 - Solely by BrotherBoard
# Intended for personal use only
# Bug? Feedback? Telegram >> @BroBordd
"""
PlugTools v1.5 - Live Plugin Action
Beta. Feedback is appreciated.
Adds a dev console tab for plugin management.
Features vary between:
- Dynamic Control: Enables immediate loading and reloading of plugins.
- Real-time Monitoring: Reports status of plugin files (new, modified, deleted).
- Plugin Overview: Displays operational state (enabled/disabled) and integrity (original/modified).
- Plugin Data: Provides file path, size, timestamps, and code structure analysis.
- Navigation: Offers controls to browse the plugin list.
- Logging: Has a built-in log display with proper indentation.
"""
from os.path import (
splitext,
getmtime,
getctime,
basename,
getsize,
isfile,
exists,
join
)
from os import (
scandir,
access,
R_OK,
stat
)
from babase import (
PluginSpec,
Plugin,
Call,
env,
app
)
from babase._devconsole import (
DevConsoleTabEntry as ENT,
DevConsoleTab as TAB
)
from bauiv1 import (
get_string_width as sw,
SpecialChar as sc,
charstr as cs,
apptimer as teck,
screenmessage as push,
getsound as gs
)
from traceback import format_exc as ERR
from datetime import datetime
from importlib import reload
from typing import override
from sys import modules
from gc import collect
from ast import (
FunctionDef,
ImportFrom,
Attribute,
ClassDef,
Import,
parse,
walk,
Name
)
class PlugTools(TAB):
KEY = 'PT_BY'
def __init__(s):
s.bys = META()
s.bad = []
s.logs = 'No errors'
s.mem = {_:MT(_) for _ in s.bys}
s.eye = look()
s.e = False
s.spy()
def spy(s):
b = 0
for _ in s.bys.copy():
if not exists(PAT(_)):
s.bys.remove(_)
push(f'Plugin {_} suddenly disappeared!\nAnd so, was removed from list.',color=(1,1,0))
gs('block').play()
s.eye = look()
if s.hl() == _: s.hl(None)
b = 1
sp = app.plugins.plugin_specs.get(_,0)
if not sp: continue
p = app.plugins
if getattr(sp,'enabled',False):
o = s.sp.plugin
if o in p.active_plugins:
p.active_plugins.remove(o)
if o in p.plugin_specs:
p.plugin_specs.pop(o)
del s.sp.plugin,o
collect()
try: reload(modules[NAM(_,0)])
except: pass
continue
if MT(_) != s.mem[_] and _ not in s.bad:
s.bad.append(_)
push(f'Plugin {_} was modified!\nSee if you want to take action.',color=(1,1,0))
gs('dingSmall').play()
b = 1
if hasattr(s,'sp'):
e = getattr(s.sp,'enabled',False)
if e != s.e:
s.e = e
b = 1
eye = look()
s1 = set(s.eye)
s2 = set(eye)
df = list(s2-s1)
nu = []
if df:
for dd in df:
try: _ = kang(dd)
except:
eye.remove(dd)
continue
nu.append(_)
s.bys.append(_)
s.mem[_] = 0
s.bad.append(_)
s.eye = eye
b = 1
if nu:
l = len(nu)
push(f"Found {l} new plugin{['s',''][l==1]}:\n{', '.join(nu)}\nSee what to do with {['it','them'][l!=1]}",color=(1,1,0))
gs('dingSmallHigh').play()
if b:
try: s.request_refresh()
except RuntimeError: pass
teck(0.1,s.spy)
@override
def refresh(s):
# Preload
by = s.hl()
if by not in s.bys:
by = None
s.hl(None)
s.by = by
s.sp = app.plugins.plugin_specs.get(by,0) if by else 0
s.i = getattr(s,'i',0 if by is None else s.bys.index(by)//10)
# UI
w = s.width
x = -w/2
z = x+w
# Bools
e = s.e = getattr(s.sp,'enabled',False)
m = by in s.bad
d = by is None
# Buttons
sx = w*0.2
mx = sx*0.98
z -= sx
s.button(
'Metadata',
pos=(z,50),
size=(mx,43),
call=s.metadata,
disabled=d
)
s.button(
['Load','Reload'][e],
pos=(z,5),
size=(mx,43),
call=s._load,
disabled=d
)
# Separator
s.button(
'',
pos=(z-(w*0.006),5),
size=(2,90)
)
# Plugin info
sx = w*0.1
z -= sx
az = z+sx/2.23
t = 'Entry' if d else by
tw = GSW(t)
mx = sx*0.9
s.text(
t,
pos=(az,80),
scale=1 if tw<mx else mx/tw,
)
t = 'State' if d else ['Disabled','Enabled'][e]
tw = GSW(t)
s.text(
t,
pos=(az,50),
scale=1 if tw<mx else mx/tw,
)
t = 'Purity' if d else ['Original','Modified'][m]
tw = GSW(t)
s.text(
t,
pos=(az,20),
scale=1 if tw<mx else mx/tw,
)
# Separator
s.button(
'',
pos=(z-(w*0.0075),5),
size=(2,90)
)
# Next
sx = w*0.03
mx = sx*0.6
z -= sx
s.button(
cs(sc.RIGHT_ARROW),
pos=(z,5),
size=(mx,90),
call=s.next,
disabled=(s.i+1)*10 > len(s.bys)
)
# Plugins
sx = w*0.645/5
mx = sx*0.99
zx = mx*0.9
z -= sx*5
for i in range(5):
for j in range(2):
k = j*5+i+s.i*10
if k >= len(s.bys): break
t = s.bys[k]
tw = GSW(t)
s.button(
t,
size=(mx,43),
pos=(z+sx*i,50-45*j),
label_scale=1 if tw<zx else zx/tw,
call=Call(s.hl,t),
style=[['blue','blue_bright'],['purple','purple_bright']][t in s.bad][t==by]
)
# Prev
sx = w*0.03
mx = sx*0.6
z -= sx*0.7
s.button(
cs(sc.LEFT_ARROW),
pos=(z,5),
size=(mx,90),
call=s.prev,
disabled=s.i==0
)
if s.height <= 100: return
# Expanded logs
t = s.logs
h = 25
pos = (x+10,s.height)
z = len(t)
p = list(pos)
m = max(t.replace('\\n','') or [''],key=GSW)
l = GSW(str(m))/1.2
ln = t.split('\\n')
mm = max(ln,key=GSW)
sk = 0.8
ml = (s.height-100) * 0.04
ww = (l*sk)*len(mm)
sk = sk if ww<s.width else (s.width*0.98/ww)*sk
zz = len(ln)
sk = sk if zz<=ml else (ml/zz)*sk
xf = 0
for i in range(z):
p[0] += [l*sk,0][i==0]
if xf: xf = 0; continue
j = t[i]
k = t[i+1] if (i+1) < z else j
if j == '\\' and k == 'n':
p[0] = pos[0]-(l*1.5)*sk
p[1] -= h*(sk*1.28)
xf = 1
continue
s.text(
j,
pos=tuple(p),
h_align='center',
v_align='top',
scale=sk
)
def hl(s,i=None):
i and deek()
c = app.config
if i is None: return c.get(s.KEY,None)
c[s.KEY] = i
c.commit()
s.request_refresh()
def _load(s):
h = ['load','reload'][s.e]
ex,er = s.load()
if ex:
k = f': {ex}' if str(ex).strip() else ''
j = f'Error {h}ing {s.by}'
push(f'{j}{k}\nExpand dev console to see more.\nTraceback dumped to terminal too.',color=(1,0,0))
gs('error').play()
m = j+':\n'+er
print('[PlugTools] '+m)
s.logs = m.replace('\n','\\n')
s.request_refresh()
return
s.logs = 'No errors'
if ex is False: return
push(h.title()+'ed '+s.by,color=(0,1,0))
gs('gunCocking').play()
s.request_refresh()
def load(s):
_ = s.by
if _ in s.bad:
s.bad.remove(_)
s.mem[_] = MT(_)
p = app.plugins
if s.e:
if hasattr(s.sp,'plugin'):
o = s.sp.plugin
if o in p.active_plugins:
p.active_plugins.remove(o)
del s.sp.plugin
collect()
try: m = reload(modules[NAM(_,0)])
except KeyError:
gs('block').play()
push(f"{s.by} is malformed!\nAre you sure there's no errors?",color=(1,1,0))
return (False,0)
except Exception as ex: return (ex,ERR())
else: m = __import__(NAM(_,0))
try: cls = getattr(m,_.split('.',1)[1])
except Exception as ex: return (ex,ERR())
try: ins = cls()
except Exception as ex: return (ex,ERR())
try: ins.on_app_running()
except Exception as ex: return (ex,ERR())
s.sp = PluginSpec(class_path=_,loadable=True)
s.sp.enabled = True
s.sp.plugin = ins
p.plugin_specs[_] = s.sp
p.active_plugins.append(ins)
return (0,0)
def metadata(s):
f = PAT(s.sp.class_path)
info = []
if exists(f):
info.append(f'File Path: {f}')
info.append("File Exists: Yes")
info.append(f"File Size: {getsize(f)} bytes")
try:
with open(f, 'r', encoding='utf-8', errors='ignore') as file:
lines = file.readlines()
content = "".join(lines) # Read entire content for AST parsing and char count
line_count = len(lines)
char_count = len(content)
info.append(f"Line Count: {line_count}")
info.append(f"Character Count: {char_count}")
# Python specific programmatic analysis
function_count = 0
class_count = 0
import_statement_count = 0
comment_lines = 0
blank_lines = 0
try:
tree = parse(content) # Use parse directly
for node in walk(tree): # Use walk directly
if isinstance(node, FunctionDef): # Use FunctionDef directly
function_count += 1
elif isinstance(node, ClassDef): # Use ClassDef directly
class_count += 1
elif isinstance(node, (Import, ImportFrom)): # Use Import, ImportFrom directly
import_statement_count += 1
# Iterate through physical lines for comments and blank lines
for line in lines:
stripped_line = line.strip()
if not stripped_line:
blank_lines += 1
elif stripped_line.startswith('#'):
comment_lines += 1
info.append(f"Function Definitions: {function_count}")
info.append(f"Class Definitions: {class_count}")
info.append(f"Import Statements: {import_statement_count}")
info.append(f"Comment Lines: {comment_lines}")
info.append(f"Blank Lines: {blank_lines}")
except SyntaxError as se:
info.append(f"Python Syntax Error: {se}")
except Exception as ast_e:
info.append(f"Error analyzing Python file structure: {ast_e}")
except Exception as e:
info.append(f"Could not read file content for analysis: {e}")
creation_time = datetime.fromtimestamp(getctime(f))
info.append(f"Creation Time: {creation_time}")
mod_time = datetime.fromtimestamp(getmtime(f))
info.append(f"Last Modified: {mod_time}")
else:
info.append(f'File Path: {f}')
info.append("File Exists: No")
push('\n'.join(info))
gs('powerup01').play()
def next(s):
deek()
s.i += 1
s.request_refresh()
def prev(s):
deek()
s.i -= 1
s.request_refresh()
MT = lambda _: stat(PAT(_))
GSW = lambda s: sw(s,suppress_warning=True)
NAM = lambda _,py=1: _.split('.',1)[0]+['','.py'][py]
PAT = lambda _: join(ROOT,NAM(_))
ROOT = env()['python_directory_user']
META = lambda: app.meta.scanresults.exports_by_name('babase.Plugin')
def look():
python_files = []
try:
with scandir(ROOT) as entries:
for entry in entries:
if entry.is_file() and entry.name.endswith(".py"):
if access(entry.path, R_OK):
python_files.append(entry.path)
except FileNotFoundError:
pass
except PermissionError:
pass
return python_files
def kang(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
source_code = f.read()
tree = parse(source_code)
lines = source_code.splitlines()
export_line_num = -1
for i, line in enumerate(lines):
if line.strip() == '# ba_meta export babase.Plugin':
export_line_num = i + 1
break
if export_line_num == -1:
return None
filename_without_ext = splitext(basename(file_path))[0]
for node in tree.body:
if isinstance(node, ClassDef):
if node.lineno > export_line_num:
for base in node.bases:
if (isinstance(base, Name) and base.id == 'Plugin') or \
(isinstance(base, Attribute) and base.attr == 'Plugin' and isinstance(base.value, Name) and base.value.id == 'babase'):
return f"{filename_without_ext}.{node.name}"
return None
deek = lambda: gs('deek').play()
# brobord collide grass
# ba_meta require api 9
# ba_meta export babase.Plugin
class byBordd(Plugin):
def __init__(s):
C = PlugTools
N = C.__name__
E = ENT(N,C)
I = app.devconsole
I.tabs = [_ for _ in I.tabs if _.name != N]+[E]
I._tab_instances[N] = E.factory()

793
plugins/utilities/power.py Executable file
View file

@ -0,0 +1,793 @@
# Copyright 2025 - Solely by BrotherBoard
# Intended for personal use only
# Bug? Feedback? Telegram >> @GalaxyA14user
"""
Power v2.7 - With one click
Experimental. Feedback is appreciated.
Adds a dev console tab with some features I find useful.
Power is mainly focused on the multiplayer side.
Can be considered a good tool to have around.
"""
from datetime import datetime as DT
from typing import override
from babase import (
clipboard_is_supported as CIS,
clipboard_set_text as CST,
Plugin,
app
)
from babase._devconsole import (
DevConsoleTabEntry as ENT,
DevConsoleTab as TAB
)
from bascenev1 import (
get_connection_to_host_info_2 as HOST,
disconnect_from_host as LEAVE,
disconnect_client as DISC,
broadcastmessage as push,
get_chat_messages as GCM,
connect_to_party as CON,
get_game_roster as ROST,
chatmessage as chat
)
from bauiv1 import (
get_string_width as sw,
SpecialChar as sc,
apptimer as teck,
charstr as cs,
Call
)
class Power(TAB):
def __init__(s):
s.j = [None,None,None]; s.ji = 1
[setattr(s,_,None) for _ in 'cpnh']
[setattr(s,_,{}) for _ in ['rr','hi']]
[setattr(s,_,[]) for _ in ['cm','og','r','ls']]
[setattr(s,_,0) for _ in ['ii','eii','ci','re','ri','eri','li','lii']]
teck(3,s.spy)
def rf(s):
try: s.request_refresh()
except RuntimeError: pass
def spy(s):
_ = 0
r = ROST()
if r != s.r:
s.rr = {i['display_string']:(i['client_id'],i['players']) for i in r}
s.r = r
_ = 1
h = HOST()
if h != s.h:
s.ri = 0
s.h = h
_ = 1
t = getattr(s.h, 'name', 'Not in a server')
a = getattr(s.h, 'address', '127.0.0.1')
p = getattr(s.h, 'port', '43210')
if s.h:
tt = t if t.strip() else '...'
if t.strip() or not any(key[1] == a for key in s.hi):
s.hi[(tt, a)] = (tt, p)
if tt != '...':
if ('...', a) in s.hi:
del s.hi[('...', a)]
ng = GCM()
if s.og != ng:
s.og = ng
ls = ng[-1]
ch = s.cm[0][1] if len(s.cm) else 0
if ch and ls == s.cm[0][0]: s.cm[0] = (ls,ch+1)
else: s.cm.insert(0,(ls,1))
if s.ci: s.ci += 1
_ = 1
_ and s.rf()
teck(0.1,s.spy)
@override
def refresh(s):
sf = s.width / 1605.3
zf = s.height / 648
x = -s.width/2
T,B = s.text,s.button
if len(s.r) and s.ri >= len(s.r): s.ri = len(s.r) - 1
if len(s.r) and s.eri >= len(s.r): s.eri = len(s.r) - 1
if s.j[0] == 'JRejoin' and s.ji <= s.re:
s.ji = s.re + 1
push('Job time cannot be less than rejoin time\nwhen job is JRejoin. Updated job time to '+str(s.ji),color=(1,1,0))
if s.height > 100:
B(
cs(sc.UP_ARROW),
pos=(x + 10 * sf, 606*zf),
size=(280*sf,35*zf),
disabled=s.eri <= 0,
call=Call(s.mv,'eri',-1)
)
B(
cs(sc.DOWN_ARROW),
pos=(x + 10 * sf, 290*zf),
size=(280*sf,35*zf),
disabled=s.eri >= len(s.r)-7,
call=Call(s.mv,'eri',1)
)
nt = "No roster detected\nJoin some public party"
w = GSW(nt)
0 if len(s.r) else T(
nt,
pos=(x + 150 * sf, 495*zf),
h_align='center',
v_align='top',
scale=1 if w<(290*sf) else (290*sf)/w
)
for i,z in enumerate(s.rr.items()):
if i < s.eri: continue
if i>=(s.eri+7): break
n,g = z
c,p = g
w = GSW(n)
B(
n,
size=(280 * sf, 37*zf),
pos=(x + 10 * sf, (564-39*(i-s.eri))*zf),
style=[['blue','blue_bright'],['purple','purple_bright']][not p][s.c==c],
call=Call(s.prv,c,p,n),
label_scale=1 if w < 280 * sf else (280 * sf)/w
)
B(
'',
size=(280 * sf, 2),
pos=(x + 10 * sf, 280*zf),
style='bright'
)
bb = s.c is None
B(
'Bomb' if bb else (['Client','Host'][s.c==-1]+f' {s.c}'),
pos=(x + 10 * sf, 230*zf),
size=(280 * sf, 40*zf),
disabled=bb,
call=Call(push,str(s.n))
)
B(
'Mention',
size=(280 * sf, 40*zf),
pos=(x + 10 * sf, 185*zf),
call=Call(chat,str(s.n)),
disabled=bb
)
B(
'Players',
size=(280 * sf, 40*zf),
pos=(x + 10 * sf, 140*zf),
call=Call(push,'\n'.join([' '.join([f'{i}={j}' for i,j in _.items()]) for _ in s.p]) if s.p else ''),
disabled=bb or (not s.p)
)
B(
'Kick',
size=(280 * sf, 40*zf),
pos=(x + 10 * sf, 95*zf),
call=Call(KICK,lambda:s.rr[s.n][0]),
disabled=bb or (s.c==-1)
)
B(
'JKick',
size=(280 * sf, 40*zf),
pos=(x + 10 * sf, 50*zf),
call=Call(s.job,Call(KICK,lambda:s.rr[s.n][0]),['JKick',s.c,s.n]),
disabled=bb or (s.c==-1)
)
B(
'Vote',
size=(280 * sf, 40*zf),
pos=(x + 10 * sf, 5*zf),
call=Call(chat,'1'),
disabled=not s.r
)
B(
'',
size=(2, 635*zf),
pos=(x + 300 * sf, 5*zf),
style='bright'
)
t = getattr(s.h,'name','Not in a server')
a = getattr(s.h,'address','127.0.0.1')
p = getattr(s.h,'port','43210')
w = GSW(t)
B(
t if t.strip() else 'Loading...',
size=(400 * sf, 35*zf),
pos=(x + 311 * sf, 606*zf),
disabled=not s.h,
label_scale=1 if w < 390 * sf else (390 * sf)/w,
call=Call(push,f"{t}\nHosted on build {getattr(s.h,'build_number','0')}" if t.strip() else 'Server is still loading...\nIf it remains stuck on this\nthen either party is full, or a network issue.'),
)
w = GSW(a)
B(
a,
size=(300 * sf, 35*zf),
pos=(x + 311 * sf, 568*zf),
call=Call(COPY,a),
disabled=not s.h,
label_scale=1 if w < 290 * sf else (290 * sf)/w
)
w = GSW(str(p))
B(
str(p),
size=(97 * sf, 35*zf),
pos=(x + 614 * sf, 568*zf),
disabled=not s.h,
call=Call(COPY,str(p)),
label_scale=1 if w < 90 * sf else (90 * sf)/w
)
B(
'Leave',
size=(400 * sf, 35*zf),
pos=(x + 311 * sf, 530*zf),
call=LEAVE,
disabled=not s.h
)
B(
'Rejoin',
size=(200 * sf, 35*zf),
pos=(x + 311 * sf, 492*zf),
call=Call(REJOIN,a,p,lambda:s.re),
disabled=not s.h
)
B(
'JRejoin',
size=(197 * sf, 35*zf),
pos=(x + 514 * sf, 492*zf),
call=Call(s.job,Call(REJOIN,a,p,lambda:s.re),['JRejoin',a,str(p)]),
disabled=not s.h
)
B(
'+',
size=(131 * sf, 35*zf),
pos=(x + 579 * sf, 454*zf),
call=Call(s.mv,'re',1)
)
B(
str(s.re or 0.1),
size=(131 * sf, 35*zf),
pos=(x + 444 * sf, 454*zf),
call=Call(push,f"Rejoins after {s.re or 0.1} second{['','s'][s.re!=1]}\nKeep this 0.1 unless server kicks fast rejoins\nLife in server = job time - rejoin time")
)
B(
'-',
size=(131 * sf, 35*zf),
pos=(x + 311 * sf, 454*zf),
disabled=s.re<=0.5,
call=Call(s.mv,'re',-1)
)
B(
'',
size=(2, 635*zf),
pos=(x + 720 * sf, 5*zf),
style='bright'
)
B(
'',
size=(400 * sf, 2),
pos=(x + 311 * sf, 445*zf),
style='bright'
)
for i,e in enumerate(s.hi.items()):
if i < s.eii: continue
if i >= (s.eii+9): break
g,v = e
_,a = g
n,p = v
w = GSW(n)
B(
n,
size=(400 * sf, 37*zf),
pos=(x + 311 * sf, (358-39*(i-s.eii))*zf),
label_scale=1 if w < 290 * sf else (290 * sf)/w,
call=Call(JOIN,a,p,False),
disabled=n == '...'
)
nt = "Server join history\nServers you join are saved here"
w = GSW(nt)
0 if len(s.hi) else T(
nt,
pos=(x + 510 * sf, 265*zf),
v_align='top',
scale=1 if w<(380*sf) else (380*sf)/w
)
B(
cs(sc.DOWN_ARROW),
pos=(x + 311 * sf, 8*zf),
size=(398*sf, 35*zf),
disabled=s.eii >= len(s.hi)-9,
call=Call(s.mv,'eii',1)
)
B(
cs(sc.UP_ARROW),
pos=(x + 311 * sf, 400*zf),
size=(400*sf, 35*zf),
disabled=s.eii <= 0,
call=Call(s.mv,'eii',-1)
)
bb = s.j[0] is None
B(
'No job' if bb else 'Job',
size=(300 * sf, 35*zf),
pos=(x + 727 * sf, 606*zf),
call=Call(push,s.j[0]),
disabled=bb
)
w = 0 if bb else GSW(str(s.j[1]))
B(
'Target' if bb else str(s.j[1]),
size=(300 * sf, 35*zf),
pos=(x + 727 * sf, 568*zf),
call=Call(push,s.j[2]),
disabled=bb,
label_scale=1 if w<110 * sf else (110 * sf)/w
)
B(
'Stop',
size=(300 * sf, 35*zf),
pos=(x + 727 * sf, 530*zf),
call=Call(s.job,None,[None,None,None]),
disabled=bb
)
B(
'+',
size=(96 * sf, 35*zf),
pos=(x + 931 * sf, 492*zf),
call=Call(s.mv,'ji',1)
)
B(
str(s.ji or 0.1),
size=(100 * sf, 35*zf),
pos=(x + 828 * sf, 492*zf),
call=Call(push,f"Job runs every {s.ji or 0.1} second{['','s'][s.ji!=1]}")
)
B(
'-',
size=(98 * sf, 35*zf),
pos=(x + 727 * sf, 492*zf),
disabled=s.ji<=0.5,
call=Call(s.mv,'ji',-1)
)
B(
'Power',
size=(300 * sf, 35*zf),
pos=(x + 727 * sf, 454*zf),
call=Call(push,'Power v2.5 FullUI\nCollapse dev console to switch to MinUI')
)
B(
'',
size=(300 * sf, 2),
pos=(x + 727 * sf, 445*zf),
style='bright'
)
B(
'',
size=(2, 635*zf),
pos=(x + 1034 * sf, 5*zf),
style='bright'
)
0 if len(s.cm) else T(
'Chat is still empty.\nHurry up and fill it with nonesense',
pos=(x+1320 * sf, 330 * zf)
)
for i,g in enumerate(s.cm):
if i < s.ci: continue
if i >= s.ci+15: break
i = i - s.ci
m,_ = g
sn,ms = m.split(': ',1)
w = GSW(sn)
w = [w,30*sf][w<30*sf]
s1 = [w,200*sf][w>200*sf]
B(
sn,
size=(s1,35*zf),
pos=(x + 1040*sf, (48+37*i)*zf),
style='purple',
label_scale=1 if w<(s1-10*sf) else (s1-10*sf)/w,
call=Call(s.chk,sn)
)
s2 = 555*sf - s1 - 53*(_>1)
B(
'',
size=(s2,35*zf),
pos=(x + 1045*sf+s1, (48+37*i)*zf),
style='black'
)
w = GSW(ms)
T(
ms,
pos=(x + s1+(1050)*sf, (48+17+37*i)*zf),
scale=1 if w<(s2-10*sf) else (s2-10*sf)/w,
h_align='left'
)
z = f'x{_}'
w = GSW(z)
_>1 and B(
z,
pos=(x+s1+s2+(1050)*sf,(48+37*i)*zf),
size=(50*sf,35*zf),
label_scale=1 if w<(40*sf) else (40*sf)/w,
style='yellow_bright'
)
B(
cs(sc.DOWN_ARROW),
pos=(x+1042*sf,8*zf),
size=(555*sf,35*zf),
call=Call(s.mv,'ci',-1),
disabled=s.ci <= 0 or not s.cm
)
B(
cs(sc.UP_ARROW),
pos=(x+1042*sf,606*zf),
size=(555*sf,35*zf),
call=Call(s.mv,'ci',1),
disabled=(s.ci >= len(s.cm)-15) or not s.cm
)
B(
cs(sc.DOWN_ARROW),
pos=(x+727*sf,8*zf),
size=(300*sf,35*zf),
disabled=(s.li >= len(s.ls)-16) or not s.ls,
call=Call(s.mv,'li',1)
)
B(
cs(sc.UP_ARROW),
pos=(x+727*sf,400*zf),
size=(300*sf,35*zf),
disabled=s.li<=0,
call=Call(s.mv,'li',-1)
)
0 if s.ls else T(
'Job logs here\nLike you even care',
pos=(x+875*sf,232*zf)
)
for _,g in enumerate(s.ls):
if _ < s.li: continue
if _ >= s.li+16: break
_ = _ - s.li
l,t = g
B(
'',
pos=(x+727*sf,(376-_*22)*zf),
size=(300*sf,20*zf),
label_scale=0.7,
corner_radius=0,
style='black',
call=Call(push,t)
)
T(
l,
pos=(x+732*sf,(386-_*22)*zf),
scale=0.6,
h_align='left'
)
else:
B(
cs(sc.DOWN_ARROW),
pos=(x + 10 * sf, 10),
size=(30 * sf, s.height-17),
disabled=(s.ri >= len(s.r)-3) or not s.r,
call=Call(s.mv,'ri',1)
)
B(
cs(sc.UP_ARROW),
pos=(x + 250 * sf, 10),
size=(30 * sf, s.height-17),
disabled=(s.ri <= 0) or not s.r,
call=Call(s.mv,'ri',-1)
)
nt = "No roster\nYou're alone"
w = GSW(nt)
0 if len(s.r) else T(
nt,
pos=(x + 147 * sf, s.height-17),
h_align='center',
v_align='top',
scale=1 if w<(200*sf) else (200*sf)/w
)
for i,z in enumerate(s.rr.items()):
if i < s.ri: continue
if i>=(s.ri+3): break
n,g = z
c,p = g
w = GSW(n)
B(
n,
size=(210 * sf, 27),
pos=(x + 40 * sf, s.height-35-27*(i-s.ri)),
style=[['blue','blue_bright'],['purple','purple_bright']][not p][s.c==c],
call=Call(s.prv,c,p,n),
label_scale=1 if w < 200 * sf else (200 * sf)/w
)
bb = s.c is None
B(
'Bomb' if bb else (['Client','Host'][s.c==-1]+f' {s.c}'),
pos=(x + 287 * sf, s.height-34),
size=(120 * sf, 27),
disabled=bb,
call=Call(push,str(s.n))
)
B(
'Mention',
size=(120 * sf, 27),
pos=(x + 287 * sf, s.height-90),
call=Call(chat,str(s.n)),
disabled=bb
)
B(
'Players',
size=(120 * sf, 27),
pos=(x + 287 * sf, s.height-62),
call=Call(push,'\n'.join([' '.join([f'{i}={j}' for i,j in _.items()]) for _ in s.p]) if s.p else ''),
disabled=bb or (not s.p)
)
B(
'Kick',
size=(120 * sf, 27),
pos=(x + 407 * sf, s.height-34),
call=Call(KICK,lambda:s.rr[s.n][0]),
disabled=bb or (s.c==-1)
)
B(
'JKick',
size=(120 * sf, 27),
pos=(x + 407 * sf, s.height-62),
call=Call(s.job,Call(KICK,lambda:s.rr[s.n][0]),['JKick',s.c,s.n]),
disabled=bb or (s.c==-1)
)
B(
'Vote',
size=(120 * sf, 27),
pos=(x + 407 * sf, s.height-90),
call=Call(chat,'1'),
disabled=not s.r
)
B(
'',
size=(2, s.height-17),
pos=(x + 535 * sf, 10),
style='bright'
)
bb = s.j[0] is None
B(
'No job' if bb else 'Job',
size=(120 * sf, 27),
pos=(x + 544 * sf, s.height-34),
call=Call(push,s.j[0]),
disabled=bb
)
w = 0 if bb else GSW(str(s.j[1]))
B(
'Target' if bb else str(s.j[1]),
size=(120 * sf, 27),
pos=(x + 544 * sf, s.height-62),
call=Call(push,s.j[2]),
disabled=bb,
label_scale=1 if w<110 * sf else (110 * sf)/w
)
B(
'Stop',
size=(120 * sf, 27),
pos=(x + 544 * sf, s.height-90),
call=Call(s.job,None,[None,None,None]),
disabled=bb
)
B(
'+',
size=(50 * sf, 27),
pos=(x + 664 * sf, s.height-34),
call=Call(s.mv,'ji',1)
)
B(
str(s.ji or 0.1),
size=(50 * sf, 27),
pos=(x + 664 * sf, s.height-62),
call=Call(push,f"Job runs every {s.ji or 0.1} second{['','s'][s.ji!=1]}")
)
B(
'-',
size=(50 * sf, 27),
pos=(x + 664 * sf, s.height-90),
disabled=s.ji<=0.5,
call=Call(s.mv,'ji',-1)
)
B(
'',
size=(2, s.height-17),
pos=(x + 722 * sf, 10),
style='bright'
)
t = getattr(s.h,'name','Not in a server')
a = getattr(s.h,'address','127.0.0.1')
p = getattr(s.h,'port','43210')
w = GSW(t)
B(
t if t.strip() else 'Loading...',
size=(300 * sf, 27),
pos=(x + 732 * sf, s.height-34),
disabled=not s.h,
label_scale=1 if w < 290 * sf else (290 * sf)/w,
call=Call(push,f"{t}\nHosted on build {getattr(s.h,'build_number','0')}" if t.strip() else 'Server is still loading...\nIf it remains stuck on this\nthen either party is full, or a network issue.'),
)
w = GSW(a)
B(
a,
size=(200 * sf, 27),
pos=(x + 732 * sf, s.height-62),
call=Call(COPY,a),
disabled=not s.h,
label_scale=1 if w < 190 * sf else (190 * sf)/w
)
w = GSW(str(p))
B(
str(p),
size=(97 * sf, 27),
pos=(x + 935 * sf, s.height-62),
disabled=not s.h,
call=Call(COPY,str(p)),
label_scale=1 if w < 90 * sf else (90 * sf)/w
)
B(
'Leave',
size=(100 * sf, 27),
pos=(x + 732 * sf, s.height-90),
call=LEAVE,
disabled=not s.h
)
B(
'Rejoin',
size=(97 * sf, 27),
pos=(x + 835 * sf, s.height-90),
call=Call(REJOIN,a,p,lambda:s.re),
disabled=not s.h
)
B(
'JRejoin',
size=(97 * sf, 27),
pos=(x + 935 * sf, s.height-90),
call=Call(s.job,Call(REJOIN,a,p,lambda:s.re),['JRejoin',a,str(p)]),
disabled=not s.h
)
B(
'+',
size=(50 * sf, 27),
pos=(x + 1035 * sf, s.height-34),
call=Call(s.mv,'re',1)
)
B(
str(s.re or 0.1),
size=(50 * sf, 27),
pos=(x + 1035 * sf, s.height-62),
call=Call(push,f"Rejoins after {s.re or 0.1} second{['','s'][s.re!=1]}\nKeep this 0.1 unless server kicks fast rejoins\nLife in server = job time - rejoin time")
)
B(
'-',
size=(50 * sf, 27),
pos=(x + 1035 * sf, s.height-90),
disabled=s.re<=0.5,
call=Call(s.mv,'re',-1)
)
B(
'',
size=(2, s.height-17),
pos=(x + 1092 * sf, 10),
style='bright'
)
for i,e in enumerate(s.hi.items()):
if i < s.ii: continue
if i >= (s.ii+3): break
g,v = e
_,a = g
n,p = v
w = GSW(n)
B(
n,
size=(300 * sf, 27),
pos=(x + 1134 * sf, s.height-34-28*(i-s.ii)),
label_scale=1 if w < 290 * sf else (290 * sf)/w,
call=Call(JOIN,a,p,False),
disabled=n == '...'
)
nt = "Your server join history\nwill appear here. Hi."
w = GSW(nt)
0 if len(s.hi) else T(
nt,
pos=(x + 1285 * sf, s.height-17),
h_align='center',
v_align='top',
scale=1 if w<(280*sf) else (280*sf)/w
)
B(
cs(sc.DOWN_ARROW),
pos=(x + 1102 * sf, 10),
size=(30 * sf, s.height-17),
disabled=s.ii >= len(s.hi)-3,
call=Call(s.mv,'ii',1)
)
B(
cs(sc.UP_ARROW),
pos=(x + 1436 * sf, 10),
size=(30 * sf, s.height-17),
disabled=s.ii <= 0,
call=Call(s.mv,'ii',-1)
)
B(
'Force leave',
call=FORCE,
pos=(x + 1469 * sf, s.height-34),
size=(130 * sf, 27),
label_scale=0.9
)
B(
'Laugh',
call=Call(chat,'hahaha'),
pos=(x + 1469 * sf, s.height-62),
size=(130 * sf, 27)
)
B(
'Power',
call=Call(push,'Power v2.5 MinUI\nExpand dev console to switch to FullUI. thanks.'),
pos=(x + 1469 * sf, s.height-90),
size=(130 * sf, 27)
)
def log(s,t):
s.ls.append((t,NOW()))
if s.lii < 99:
s.lii += 1
if s.li == s.lii-17: s.li += 1
else: s.ls.pop(0)
s.rf()
def mv(s,a,i):
setattr(s,a,getattr(s,a)+i)
s.rf()
def job(s,f,j):
s.j = j
s.lf = f
s.hd = j[1] if s.j[0] == 'JRejoin' else j[2]
if f is not None:
s._job(f)
push('Job started',color=(1,1,0))
else: push('Job stopped',color=(1,1,0))
s.rf()
def _job(s,f):
if f != s.lf: return
s.log(f'[{s.lii:02}] [{s.j[0]}] {s.hd}')
f(); teck(s.ji or 0.1,Call(s._job,f))
def prv(s,c,p,n):
s.c,s.p,s.n = c,p,n
s.rf()
def chk(s,pn):
y = 0
for n,g in s.rr.items():
c,p = g
if n == pn: y = 1
else:
for _ in p:
if pn in [_['name'],_['name_full']]: y = 1
if y: s.prv(c,p,n); break
HAS = app.ui_v1.has_main_window
SAVE = app.classic.save_ui_state
KICK = lambda f: DISC(f())
FORCE = lambda: teck(0.7 if HAS() else 0.1,lambda: 0 if HAS() else app.classic.return_to_main_menu_session_gracefully())
JOIN = lambda *a: (SAVE() or 1) and CON(*a)
GSW = lambda s: sw(s,suppress_warning=True)
REJOIN = lambda a,p,f: ((LEAVE() if getattr(HOST(),'name','') else 0) or 1) and teck(f() or 0.1,Call(JOIN,a,p,False))
COPY = lambda s: ((CST(s) or 1) if CIS() else push('Clipboard not supported!')) and push('Copied!',color=(0,1,0))
NOW = lambda: DT.now().strftime("%H:%M:%S")
# brobord collide grass
# ba_meta require api 9
# ba_meta export babase.Plugin
class byBordd(Plugin):
def __init__(s):
C = Power
N = C.__name__
E = ENT(N,C)
I = app.devconsole
I.tabs = [_ for _ in I.tabs if _.name != N]+[E]
I._tab_instances[N] = E.factory()

1251
plugins/utilities/replay.py Executable file

File diff suppressed because it is too large Load diff

37
plugins/utilities/topmsg.py Normal file → Executable file
View file

@ -1,26 +1,31 @@
from babase import app, Plugin as p
from bascenev1 import gettexture as x, apptimer as z
from bascenev1 import broadcastmessage as push, get_foreground_host_activity as ga, get_chat_messages as gcm
# Copyright 2025 - Solely by BrotherBoard
# Intended for personal use only
# Bug? Feedback? Telegram >> @BroBordd
"""
TopMsg v1.1.2 - Chat top right
When chat is muted, shows chat messages top right.
Prevents spam and flooding screen.
Does not repeat messages.
"""
from babase import app, Plugin
from bascenev1 import (
get_chat_messages as gcm,
broadcastmessage as push,
apptimer as z
)
# ba_meta require api 9
# ba_meta export babase.Plugin
class byBordd(p):
class byBordd(Plugin):
__init__ = lambda s: (setattr(s,'la',None),z(5,s.ear))[1]
def ear(s):
a = gcm()
if a and s.la != a[-1]:
if app.config.resolve('Chat Muted'):
push(a[-1], (1, 1, 1), True, s.con)
push(a[-1],(1,1,1),True)
s.la = a[-1]
z(0.1, s.ear)
def get(s):
with ga().context:
s.con = x("upButton")
s.la = None
s.ear()
z(1.0, byBordd().get)

View file

@ -38,7 +38,6 @@ class VeryPW(party.PartyWindow):
def _d(s): s._p(1)
def _p(s, i=0):
print(s._chat_texts)
s._w1 = gcm()
if s._f:
s._o = tw(query=s._text_field)