updating mods to api8

This commit is contained in:
Ayush Saini 2023-08-14 18:29:15 +05:30
parent 99cec14279
commit ecc4d84189
86 changed files with 78858 additions and 1056 deletions

View file

@ -148,10 +148,12 @@
},
"Fleet Zone Pings": {
"prod": {
"delhi": 16.50450974109344,
"hyderabad": 40.392026402187184,
"kolkata": 41.73070900011953,
"mumbai": 42.654999799975485
"bangkok": 178.89479999575997,
"delhi": 13.92013339677942,
"dubai": 154.7783000060008,
"hyderabad": 40.31838120082102,
"kolkata": 44.57410000031814,
"mumbai": 41.06290000345325
}
},
"Free-for-All Playlist Randomize": true,
@ -1048,7 +1050,7 @@
}
]
},
"launchCount": 2,
"launchCount": 4,
"lc14173": 1,
"lc14292": 1
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,54 @@
# Released under the MIT License. See LICENSE for details.
from playersData import pdata
import babase
import bauiv1 as bui
import bascenev1 as bs
import babase.internal
def clientid_to_accountid(clientid):
"""
Transform Clientid To Accountid
Parameters:
clientid : int
Returns:
None
"""
for i in babase.internal.get_game_roster():
if i['client_id'] == clientid:
return i['account_id']
return None
def check_permissions(accountid, command):
"""
Checks The Permission To Player To Executive Command
Parameters:
accountid : str
command : str
Returns:
Boolean
"""
roles = pdata.get_roles()
if is_server(accountid):
return True
for role in roles:
if accountid in roles[role]["ids"] and "ALL" in roles[role]["commands"]:
return True
elif accountid in roles[role]["ids"] and command in roles[role]["commands"]:
return True
return False
def is_server(accid):
for i in babase.internal.get_game_roster():
if i['account_id'] == accid and i['client_id'] == -1:
return True

View file

@ -0,0 +1,132 @@
# Released under the MIT License. See LICENSE for details.
from .commands import NormalCommands
from .commands import Management
from .commands import Fun
from .commands import Cheats
from .Handlers import clientid_to_accountid
from .Handlers import check_permissions
from chatHandle.chatFilter import ChatFilter
from bascenev1lib.actor import popuptext
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
import setting
from datetime import datetime
from playersData import pdata
from serverData import serverdata
settings = setting.get_settings_data()
def command_type(command):
"""
Checks The Command Type
Parameters:
command : str
Returns:
any
"""
if command in NormalCommands.Commands or command in NormalCommands.CommandAliases:
return "Normal"
if command in Management.Commands or command in Management.CommandAliases:
return "Manage"
if command in Fun.Commands or command in Fun.CommandAliases:
return "Fun"
if command in Cheats.Commands or command in Cheats.CommandAliases:
return "Cheats"
def Command(msg, clientid):
"""
Command Execution
Parameters:
msg : str
clientid : int
Returns:
any
"""
command = msg.lower().split(" ")[0].split("/")[1]
arguments = msg.lower().split(" ")[1:]
accountid = clientid_to_accountid(clientid)
if command_type(command) == "Normal":
NormalCommands.ExcelCommand(command, arguments, clientid, accountid)
elif command_type(command) == "Manage":
if check_permissions(accountid, command):
Management.ExcelCommand(command, arguments, clientid, accountid)
_bs.broadcastmessage("Executed", transient=True, clients=[clientid])
else:
_bs.broadcastmessage("access denied", transient=True,
clients=[clientid])
elif command_type(command) == "Fun":
if check_permissions(accountid, command):
Fun.ExcelCommand(command, arguments, clientid, accountid)
_bs.broadcastmessage("Executed", transient=True, clients=[clientid])
else:
_bs.broadcastmessage("access denied", transient=True,
clients=[clientid])
elif command_type(command) == "Cheats":
if check_permissions(accountid, command):
Cheats.ExcelCommand(command, arguments, clientid, accountid)
_bs.broadcastmessage("Executed", transient=True, clients=[clientid])
else:
_bs.broadcastmessage("access denied", transient=True,
clients=[clientid])
now = datetime.now()
if accountid in pdata.get_blacklist()["muted-ids"] and now < datetime.strptime(pdata.get_blacklist()["muted-ids"][accountid]["till"], "%Y-%m-%d %H:%M:%S"):
_bs.broadcastmessage("You are on mute", transient=True,
clients=[clientid])
return None
if serverdata.muted:
return None
if settings["ChatCommands"]["BrodcastCommand"]:
return msg
return None
def QuickAccess(msg, client_id):
if msg.startswith(","):
name = ""
teamid = 0
for i in bs.get_foreground_host_session().sessionplayers:
if i.inputdevice.client_id == client_id:
teamid = i.sessionteam.id
name = i.getname(True)
for i in bs.get_foreground_host_session().sessionplayers:
if hasattr(i, 'sessionteam') and i.sessionteam and teamid == i.sessionteam.id and i.inputdevice.client_id != client_id:
bs.broadcastmessage(name + ":" + msg[1:], clients=[i.inputdevice.client_id],
color=(0.3, 0.6, 0.3), transient=True)
return None
elif msg.startswith("."):
msg = msg[1:]
msgAr = msg.split(" ")
if len(msg) > 25 or int(len(msg) / 5) > len(msgAr):
_bs.broadcastmessage("msg/word length too long",
clients=[client_id], transient=True)
return None
msgAr.insert(int(len(msgAr) / 2), "\n")
for player in _babase.get_foreground_host_activity().players:
if player.sessionplayer.inputdevice.client_id == client_id and player.actor.exists() and hasattr(player.actor.node, "position"):
pos = player.actor.node.position
with _babase.Context(_babase.get_foreground_host_activity()):
popuptext.PopupText(
" ".join(msgAr), (pos[0], pos[1] + 1, pos[2])).autoretain()
return None
return None

View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

View file

@ -0,0 +1,312 @@
from .Handlers import handlemsg, handlemsg_all, clientid_to_myself
import babase
import bauiv1 as bui
import bascenev1 as bs, _ba
Commands = ['kill', 'heal', 'curse', 'sleep', 'superpunch', 'gloves', 'shield', 'freeze', 'unfreeze', 'godmode']
CommandAliases = ['die', 'heath', 'cur', 'sp', 'punch', 'protect', 'ice', 'thaw', 'gm']
def ExcelCommand(command, arguments, clientid, accountid):
"""
Checks The Command And Run Function
Parameters:
command : str
arguments : str
clientid : int
accountid : int
Returns:
None
"""
if command in ['kill', 'die']:
kill(arguments, clientid)
elif command in ['heal', 'heath']:
heal(arguments, clientid)
elif command in ['curse', 'cur']:
curse(arguments, clientid)
elif command == 'sleep':
sleep(arguments, clientid)
elif command in ['sp', 'superpunch']:
super_punch(arguments, clientid)
elif command in ['gloves', 'punch']:
gloves(arguments, clientid)
elif command in ['shield', 'protect']:
shield(arguments, clientid)
elif command in ['freeze', 'ice']:
freeze(arguments, clientid)
elif command in ['unfreeze', 'thaw']:
un_freeze(arguments, clientid)
elif command in ['gm', 'godmode']:
god_mode(arguments, clientid)
def kill(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
handlemsg(myself, bs.DieMessage())
elif arguments[0] == 'all':
handlemsg_all(bs.DieMessage())
else:
try:
req_player = int(arguments[0])
handlemsg(req_player, bs.DieMessage())
except:
return
def heal(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
handlemsg(myself, babase.PowerupMessage(poweruptype='health'))
elif arguments[0] == 'all':
handlemsg_all(babase.PowerupMessage(poweruptype='health'))
else:
try:
req_player = int(arguments[0])
handlemsg(req_player, babase.PowerupMessage(poweruptype='health'))
except:
return
def curse(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
handlemsg(myself, babase.PowerupMessage(poweruptype='curse'))
elif arguments[0] == 'all':
handlemsg_all(babase.PowerupMessage(poweruptype='curse'))
else:
try:
req_player = int(arguments[0])
handlemsg(req_player, babase.PowerupMessage(poweruptype='curse'))
except:
return
def sleep(arguments, clientid):
activity = _babase.get_foreground_host_activity()
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
activity.players[myself].actor.node.handlemessage('knockout', 8000)
elif arguments[0] == 'all':
for i in activity.players:
i.actor.node.handlemessage('knockout', 8000)
else:
try:
req_player = int(arguments[0])
activity.players[req_player].actor.node.handlemessage('knockout', 8000)
except:
return
def super_punch(arguments, clientid):
activity = _babase.get_foreground_host_activity()
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
if activity.players[myself].actor._punch_power_scale != 15:
activity.players[myself].actor._punch_power_scale = 15
activity.players[myself].actor._punch_cooldown = 0
else:
activity.players[myself].actor._punch_power_scale = 1.2
activity.players[myself].actor._punch_cooldown = 400
elif arguments[0] == 'all':
activity = _babase.get_foreground_host_activity()
for i in activity.players:
if i.actor._punch_power_scale != 15:
i.actor._punch_power_scale = 15
i.actor._punch_cooldown = 0
else:
i.actor._punch_power_scale = 1.2
i.actor._punch_cooldown = 400
else:
try:
activity = _babase.get_foreground_host_activity()
req_player = int(arguments[0])
if activity.players[req_player].actor._punch_power_scale != 15:
activity.players[req_player].actor._punch_power_scale = 15
activity.players[req_player].actor._punch_cooldown = 0
else:
activity.players[req_player].actor._punch_power_scale = 1.2
activity.players[req_player].actor._punch_cooldown = 400
except:
return
def gloves(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
handlemsg(myself, babase.PowerupMessage(poweruptype='punch'))
elif arguments[0] == 'all':
handlemsg_all(babase.PowerupMessage(poweruptype='punch'))
else:
try:
req_player = int(arguments[0])
handlemsg(req_player, babase.PowerupMessage(poweruptype='punch'))
except:
return
def shield(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
handlemsg(myself, babase.PowerupMessage(poweruptype='shield'))
elif arguments[0] == 'all':
handlemsg_all(babase.PowerupMessage(poweruptype='shield'))
else:
try:
req_player = int(arguments[0])
handlemsg(req_player, babase.PowerupMessage(poweruptype='shield'))
except:
return
def freeze(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
handlemsg(myself, babase.FreezeMessage())
elif arguments[0] == 'all':
handlemsg_all(babase.FreezeMessage())
else:
try:
req_player = int(arguments[0])
handlemsg(req_player, babase.FreezeMessage())
except:
return
def un_freeze(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
handlemsg(myself, babase.ThawMessage())
elif arguments[0] == 'all':
handlemsg_all(babase.ThawMessage())
else:
try:
req_player = int(arguments[0])
handlemsg(req_player, babase.ThawMessage())
except:
return
def god_mode(arguments, clientid):
if arguments == [] or arguments == ['']:
myself = clientid_to_myself(clientid)
activity = _babase.get_foreground_host_activity()
player = activity.players[myself].actor
if player._punch_power_scale != 7:
player._punch_power_scale = 7
player.node.hockey = True
player.node.invincible = True
else:
player._punch_power_scale = 1.2
player.node.hockey = False
player.node.invincible = False
elif arguments[0] == 'all':
activity = _babase.get_foreground_host_activity()
for i in activity.players:
if i.actor._punch_power_scale != 7:
i.actor._punch_power_scale = 7
i.actor.node.hockey = True
i.actor.node.invincible = True
else:
i.actor._punch_power_scale = 1.2
i.actor.node.hockey = False
i.actor.node.invincible = False
else:
activity = _babase.get_foreground_host_activity()
req_player = int(arguments[0])
player = activity.players[req_player].actor
if player._punch_power_scale != 7:
player._punch_power_scale = 7
player.node.hockey = True
player.node.invincible = True
else:
player._punch_power_scale = 1.2
player.node.hockey = False
player.node.invincible = False

View file

@ -0,0 +1,234 @@
from .Handlers import handlemsg, handlemsg_all
import babase
import bauiv1 as bui
import bascenev1 as bs
from tools import corelib
Commands = ['fly', 'invisible', 'headless', 'creepy', 'celebrate', 'spaz', 'speed', 'floater']
CommandAliases = ['inv', 'hl', 'creep', 'celeb', 'flo']
def ExcelCommand(command, arguments, clientid, accountid):
"""
Checks The Command And Run Function
Parameters:
command : str
arguments : str
clientid : int
accountid : int
Returns:
None
"""
if command=='speed':
speed(arguments)
elif command == 'fly':
fly(arguments)
elif command in ['inv', 'invisible']:
invi(arguments)
elif command in ['hl', 'headless']:
headless(arguments)
elif command in ['creepy', 'creep']:
creep(arguments)
elif command in ['celebrate', 'celeb']:
celeb(arguments)
elif command == 'spaz':
spaz(arguments)
elif command in ['floater','flo']:
floater(arguments,clientid)
def floater(arguments,clientid):
try:
from .. import floater
if arguments ==[]:
floater.assignFloInputs(clientid)
else:
floater.assignFloInputs(arguments[0])
except:
pass
def speed(arguments):
if arguments ==[] or arguments==['']:
return
else:
corelib.set_speed(float(arguments[0]))
def fly(arguments):
if arguments == [] or arguments == ['']:
return
elif arguments[0] == 'all':
activity = _babase.get_foreground_host_activity()
for players in activity.players:
if players.actor.node.fly != True:
players.actor.node.fly = True
else:
players.actor.node.fly = False
else:
try:
activity = _babase.get_foreground_host_activity()
player = int(arguments[0])
if activity.players[player].actor.node.fly != True:
activity.players[player].actor.node.fly = True
else:
activity.players[player].actor.node.fly = False
except:
return
def invi(arguments):
if arguments == [] or arguments == ['']:
return
elif arguments[0] == 'all':
activity = _babase.get_foreground_host_activity()
for i in activity.players:
if i.actor.exists() and i.actor.node.torso_mesh != None:
body = i.actor.node
body.head_mesh = None
body.torso_mesh = None
body.upper_arm_mesh = None
body.forearm_mesh = None
body.pelvis_mesh = None
body.hand_mesh = None
body.toes_mesh = None
body.upper_leg_mesh = None
body.lower_leg_mesh = None
body.style = 'cyborg'
else:
player = int(arguments[0])
activity = _babase.get_foreground_host_activity()
body = activity.players[player].actor.node
if body.torso_mesh != None:
body.head_mesh = None
body.torso_mesh = None
body.upper_arm_mesh = None
body.forearm_mesh = None
body.pelvis_mesh = None
body.hand_mesh = None
body.toes_mesh = None
body.upper_leg_mesh = None
body.lower_leg_mesh = None
body.style = 'cyborg'
def headless(arguments):
if arguments == [] or arguments == ['']:
return
elif arguments[0] == 'all':
activity = _babase.get_foreground_host_activity()
for players in activity.players:
node = players.actor.node
if node.head_mesh != None:
node.head_mesh = None
node.style='cyborg'
else:
try:
player = int(arguments[0])
activity = _babase.get_foreground_host_activity()
node = activity.players[player].actor.node
if node.head_mesh != None:
node.head_mesh = None
node.style='cyborg'
except:
return
def creep(arguments):
if arguments == [] or arguments == ['']:
return
elif arguments[0] == 'all':
activity = _babase.get_foreground_host_activity()
for players in activity.players:
node = players.actor.node
if node.head_mesh != None:
node.head_mesh = None
node.handlemessage(babase.PowerupMessage(poweruptype='punch'))
node.handlemessage(babase.PowerupMessage(poweruptype='shield'))
else:
try:
player = int(arguments[0])
activity = _babase.get_foreground_host_activity()
node = activity.players[player].actor.node
if node.head_mesh != None:
node.head_mesh = None
node.handlemessage(babase.PowerupMessage(poweruptype='punch'))
node.handlemessage(babase.PowerupMessage(poweruptype='shield'))
except:
return
def celeb(arguments):
if arguments == [] or arguments == ['']:
return
elif arguments[0] == 'all':
handlemsg_all(bs.CelebrateMessage())
else:
try:
player = int(arguments[0])
handlemsg(player, bs.CelebrateMessage())
except:
return
def spaz(arguments):
if arguments == [] or arguments == ['']:
return
return

View file

@ -0,0 +1,33 @@
""" Some useful handlers to reduce lot of code """
import _babase
import bascenev1 as bs
def send(msg, clientid):
"""Shortcut To Send Private Msg To Client"""
for m in msg.split("\n"):
bs.chatmessage(str(m), clients=[clientid])
bs.broadcastmessage(str(msg), transient=True, clients=[clientid])
def clientid_to_myself(clientid):
"""Return Player Index Of Self Player"""
for i , player in enumerate(_babase.get_foreground_host_activity().players):
if player.sessionplayer.inputdevice.client_id == clientid:
return i
def handlemsg(client, msg):
"""Handles Spaz Msg For Single Player"""
activity = _babase.get_foreground_host_activity()
activity.players[client].actor.node.handlemessage(msg)
def handlemsg_all(msg):
"""Handle Spaz message for all players in activity"""
activity = _babase.get_foreground_host_activity()
for i in activity.players:
i.actor.node.handlemessage(msg)

View file

@ -0,0 +1,605 @@
from .Handlers import handlemsg, handlemsg_all, send
from playersData import pdata
# from tools.whitelist import add_to_white_list, add_commit_to_logs
from serverData import serverdata
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
import time
import setting
import _thread
import random
from tools import playlist
from tools import logger
Commands = ['recents', 'info', 'createteam', 'showid', 'hideid', 'lm', 'gp', 'party', 'quit', 'kickvote', 'maxplayers', 'playlist', 'ban', 'kick', 'remove', 'end', 'quit', 'mute', 'unmute', 'slowmo', 'nv', 'dv', 'pause',
'cameramode', 'createrole', 'addrole', 'removerole', 'addcommand', 'addcmd', 'removecommand', 'getroles', 'removecmd', 'changetag', 'customtag', 'customeffect', 'removeeffect', 'removetag' 'add', 'spectators', 'lobbytime']
CommandAliases = ['max', 'rm', 'next', 'restart', 'mutechat', 'unmutechat', 'sm',
'slow', 'night', 'day', 'pausegame', 'camera_mode', 'rotate_camera', 'effect']
def ExcelCommand(command, arguments, clientid, accountid):
"""
Checks The Command And Run Function
Parameters:
command : str
arguments : str
clientid : int
accountid : int
Returns:
None
"""
if command in ['recents']:
get_recents(clientid)
if command in ['info']:
get_player_info(arguments, clientid)
if command in ['maxplayers', 'max']:
changepartysize(arguments)
if command in ['createteam']:
create_team(arguments)
elif command == 'playlist':
changeplaylist(arguments)
elif command == 'kick':
kick(arguments)
elif command == 'ban':
ban(arguments)
elif command in ['end', 'next']:
end(arguments)
elif command == 'kickvote':
kikvote(arguments, clientid)
elif command == 'hideid':
hide_player_spec()
elif command == "showid":
show_player_spec()
elif command == 'lm':
last_msgs(clientid)
elif command == 'gp':
get_profiles(arguments, clientid)
elif command == 'party':
party_toggle(arguments)
elif command in ['quit', 'restart']:
quit(arguments)
elif command in ['mute', 'mutechat']:
mute(arguments)
elif command in ['unmute', 'unmutechat']:
un_mute(arguments)
elif command in ['remove', 'rm']:
remove(arguments)
elif command in ['sm', 'slow', 'slowmo']:
slow_motion()
elif command in ['nv', 'night']:
nv(arguments)
elif command in ['dv', 'day']:
dv(arguments)
elif command in ['pause', 'pausegame']:
pause()
elif command in ['cameraMode', 'camera_mode', 'rotate_camera']:
rotate_camera()
elif command == 'createrole':
create_role(arguments)
elif command == 'addrole':
add_role_to_player(arguments)
elif command == 'removerole':
remove_role_from_player(arguments)
elif command == 'getroles':
get_roles_of_player(arguments, clientid)
elif command in ['addcommand', 'addcmd']:
add_command_to_role(arguments)
elif command in ['removecommand', 'removecmd']:
remove_command_to_role(arguments)
elif command == 'changetag':
change_role_tag(arguments)
elif command == 'customtag':
set_custom_tag(arguments)
elif command in ['customeffect', 'effect']:
set_custom_effect(arguments)
elif command in ['removetag']:
remove_custom_tag(arguments)
elif command in ['removeeffect']:
remove_custom_effect(arguments)
# elif command in ['add', 'whitelist']:
# whitelst_it(accountid, arguments)
elif command == 'spectators':
spectators(arguments)
elif command == 'lobbytime':
change_lobby_check_time(arguments)
def create_team(arguments):
if len(arguments) == 0:
bs.chatmessage("enter team name")
else:
from bascenev1._team import SessionTeam
bs.get_foreground_host_session().sessionteams.append(SessionTeam(team_id=len(bs.get_foreground_host_session().sessionteams) + 1, name=str(arguments[0]), color=(random.uniform(0, 1.2), random.uniform(
0, 1.2), random.uniform(0, 1.2))))
from bascenev1._lobby import Lobby
bs.get_foreground_host_session().lobby = Lobby()
def hide_player_spec():
_babase.hide_player_device_id(True)
def show_player_spec():
_babase.hide_player_device_id(False)
def get_player_info(arguments, client_id):
if len(arguments) == 0:
send("invalid client id", client_id)
for account in serverdata.recents:
if account['client_id'] == int(arguments[0]):
send(pdata.get_detailed_info(account["pbid"]), client_id)
def get_recents(client_id):
for players in serverdata.recents:
send(
f"{players['client_id']} {players['deviceId']} {players['pbid']}", client_id)
def changepartysize(arguments):
if len(arguments) == 0:
bs.chatmessage("enter number")
else:
bs.set_public_party_max_size(int(arguments[0]))
def changeplaylist(arguments):
if len(arguments) == 0:
bs.chatmessage("enter list code or name")
else:
if arguments[0] == 'coop':
serverdata.coopmode = True
else:
serverdata.coopmode = False
playlist.setPlaylist(arguments[0])
return
def kick(arguments):
cl_id = int(arguments[0])
for ros in bs.get_game_roster():
if ros["client_id"] == cl_id:
logger.log("kicked " + ros["display_string"])
bs.disconnect_client(int(arguments[0]))
return
def kikvote(arguments, clientid):
if arguments == [] or arguments == [''] or len(arguments) < 2:
return
elif arguments[0] == 'enable':
if arguments[1] == 'all':
_babase.set_enable_default_kick_voting(True)
else:
try:
cl_id = int(arguments[1])
for ros in bs.get_game_roster():
if ros["client_id"] == cl_id:
pdata.enable_kick_vote(ros["account_id"])
logger.log(
f'kick vote enabled for {ros["account_id"]} {ros["display_string"]}')
send(
"Upon server restart, Kick-vote will be enabled for this person", clientid)
return
except:
return
elif arguments[0] == 'disable':
if arguments[1] == 'all':
_babase.set_enable_default_kick_voting(False)
else:
try:
cl_id = int(arguments[1])
for ros in bs.get_game_roster():
if ros["client_id"] == cl_id:
_babase.disable_kickvote(ros["account_id"])
send("Kick-vote disabled for this person", clientid)
logger.log(
f'kick vote disabled for {ros["account_id"]} {ros["display_string"]}')
pdata.disable_kick_vote(
ros["account_id"], 2, "by chat command")
return
except:
return
else:
return
def last_msgs(clientid):
for i in bs.get_chat_messages():
send(i, clientid)
def get_profiles(arguments, clientid):
try:
playerID = int(arguments[0])
num = 1
for i in bs.get_foreground_host_session().sessionplayers[playerID].inputdevice.get_player_profiles():
try:
send(f"{num})- {i}", clientid)
num += 1
except:
pass
except:
pass
def party_toggle(arguments):
if arguments == ['public']:
bs.set_public_party_enabled(True)
bs.chatmessage("party is public now")
elif arguments == ['private']:
bs.set_public_party_enabled(False)
bs.chatmessage("party is private now")
else:
pass
def end(arguments):
if arguments == [] or arguments == ['']:
try:
with _babase.Context(_babase.get_foreground_host_activity()):
_babase.get_foreground_host_activity().end_game()
except:
pass
def ban(arguments):
try:
cl_id = int(arguments[0])
duration = int(arguments[1]) if len(arguments) >= 2 else 0.5
for ros in bs.get_game_roster():
if ros["client_id"] == cl_id:
pdata.ban_player(ros['account_id'], duration,
"by chat command")
logger.log(f'banned {ros["display_string"]} by chat command')
for account in serverdata.recents: # backup case if player left the server
if account['client_id'] == int(arguments[0]):
pdata.ban_player(
account["pbid"], duration, "by chat command")
logger.log(
f'banned {ros["display_string"]} by chat command, recents')
kick(arguments)
except:
pass
def quit(arguments):
if arguments == [] or arguments == ['']:
babase.quit()
def mute(arguments):
if len(arguments) == 0:
serverdata.muted = True
try:
cl_id = int(arguments[0])
duration = int(arguments[1]) if len(arguments) >= 2 else 0.5
for ros in bs.get_game_roster():
if ros["client_id"] == cl_id:
ac_id = ros['account_id']
logger.log(f'muted {ros["display_string"]}')
pdata.mute(ac_id, duration, "muted by chat command")
return
for account in serverdata.recents: # backup case if player left the server
if account['client_id'] == int(arguments[0]):
pdata.mute(account["pbid"], duration,
"muted by chat command, from recents")
except:
pass
return
def un_mute(arguments):
if len(arguments) == 0:
serverdata.muted = False
try:
cl_id = int(arguments[0])
for ros in bs.get_game_roster():
if ros["client_id"] == cl_id:
pdata.unmute(ros['account_id'])
logger.log(f'unmuted {ros["display_string"]} by chat command')
return
for account in serverdata.recents: # backup case if player left the server
if account['client_id'] == int(arguments[0]):
pdata.unmute(account["pbid"])
logger.log(
f'unmuted {ros["display_string"]} by chat command, recents')
except:
pass
def remove(arguments):
if arguments == [] or arguments == ['']:
return
elif arguments[0] == 'all':
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
i.remove_from_game()
else:
try:
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[0]):
i.remove_from_game()
except:
return
def slow_motion():
activity = _babase.get_foreground_host_activity()
if activity.globalsnode.slow_motion != True:
activity.globalsnode.slow_motion = True
else:
activity.globalsnode.slow_motion = False
def nv(arguments):
activity = _babase.get_foreground_host_activity()
if arguments == [] or arguments == ['']:
if activity.globalsnode.tint != (0.5, 0.7, 1.0):
activity.globalsnode.tint = (0.5, 0.7, 1.0)
else:
# will fix this soon
pass
elif arguments[0] == 'off':
if activity.globalsnode.tint != (0.5, 0.7, 1.0):
return
else:
pass
def dv(arguments):
activity = _babase.get_foreground_host_activity()
if arguments == [] or arguments == ['']:
if activity.globalsnode.tint != (1, 1, 1):
activity.globalsnode.tint = (1, 1, 1)
else:
# will fix this soon
pass
elif arguments[0] == 'off':
if activity.globalsnode.tint != (1, 1, 1):
return
else:
pass
def pause():
activity = _babase.get_foreground_host_activity()
if activity.globalsnode.paused != True:
activity.globalsnode.paused = True
else:
activity.globalsnode.paused = False
def rotate_camera():
activity = _babase.get_foreground_host_activity()
if activity.globalsnode.camera_mode != 'rotate':
activity.globalsnode.camera_mode = 'rotate'
else:
activity.globalsnode.camera_mode == 'normal'
def create_role(arguments):
try:
pdata.create_role(arguments[0])
except:
return
def add_role_to_player(arguments):
try:
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[1]):
roles = pdata.add_player_role(
arguments[0], i.get_v1_account_id())
except:
return
def remove_role_from_player(arguments):
try:
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[1]):
roles = pdata.remove_player_role(
arguments[0], i.get_v1_account_id())
except:
return
def get_roles_of_player(arguments, clientid):
try:
session = bs.get_foreground_host_session()
roles = []
reply = ""
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[0]):
roles = pdata.get_player_roles(i.get_v1_account_id())
print(roles)
for role in roles:
reply = reply+role+","
send(reply, clientid)
except:
return
def change_role_tag(arguments):
try:
pdata.change_role_tag(arguments[0], arguments[1])
except:
return
def set_custom_tag(arguments):
try:
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[1]):
roles = pdata.set_tag(arguments[0], i.get_v1_account_id())
except:
return
def remove_custom_tag(arguments):
try:
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[0]):
pdata.remove_tag(i.get_v1_account_id())
except:
return
def remove_custom_effect(arguments):
try:
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[0]):
pdata.remove_effect(i.get_v1_account_id())
except:
return
def set_custom_effect(arguments):
try:
session = bs.get_foreground_host_session()
for i in session.sessionplayers:
if i.inputdevice.client_id == int(arguments[1]):
pdata.set_effect(arguments[0], i.get_v1_account_id())
except:
return
all_commands = ["changetag", "createrole", "addrole", "removerole", "addcommand", "addcmd", "removecommand", "removecmd", "kick", "remove", "rm", "end", "next", "quit", "restart", "mute", "mutechat", "unmute", "unmutechat", "sm", "slow", "slowmo", "nv", "night", "dv", "day", "pause", "pausegame", "cameraMode",
"camera_mode", "rotate_camera", "kill", "die", "heal", "heath", "curse", "cur", "sleep", "sp", "superpunch", "gloves", "punch", "shield", "protect", "freeze", "ice", "unfreeze", "thaw", "gm", "godmode", "fly", "inv", "invisible", "hl", "headless", "creepy", "creep", "celebrate", "celeb", "spaz"]
def add_command_to_role(arguments):
try:
if len(arguments) == 2:
pdata.add_command_role(arguments[0], arguments[1])
else:
bs.chatmessage("invalid command arguments")
except:
return
def remove_command_to_role(arguments):
try:
if len(arguments) == 2:
pdata.remove_command_role(arguments[0], arguments[1])
except:
return
# def whitelst_it(accountid : str, arguments):
# settings = setting.get_settings_data()
# if arguments[0] == 'on':
# if settings["white_list"]["whitelist_on"]:
# bs.chatmessage("Already on")
# else:
# settings["white_list"]["whitelist_on"] = True
# setting.commit(settings)
# bs.chatmessage("whitelist on")
# from tools import whitelist
# whitelist.Whitelist()
# return
# elif arguments[0] == 'off':
# settings["white_list"]["whitelist_on"] = False
# setting.commit(settings)
# bs.chatmessage("whitelist off")
# return
# else:
# rost = bs.get_game_roster()
# for i in rost:
# if i['client_id'] == int(arguments[0]):
# add_to_white_list(i['account_id'], i['display_string'])
# bs.chatmessage(str(i['display_string'])+" whitelisted")
# add_commit_to_logs(accountid+" added "+i['account_id'])
def spectators(arguments):
if arguments[0] in ['on', 'off']:
settings = setting.get_settings_data()
if arguments[0] == 'on':
settings["white_list"]["spectators"] = True
setting.commit(settings)
bs.chatmessage("spectators on")
elif arguments[0] == 'off':
settings["white_list"]["spectators"] = False
setting.commit(settings)
bs.chatmessage("spectators off")
def change_lobby_check_time(arguments):
try:
argument = int(arguments[0])
except:
bs.chatmessage("must type number to change lobby check time")
return
settings = setting.get_settings_data()
settings["white_list"]["lobbychecktime"] = argument
setting.commit(settings)
bs.chatmessage(f"lobby check time is {argument} now")

View file

@ -0,0 +1,119 @@
from .Handlers import send
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
from stats import mystats
from babase._general import Call
import _thread
Commands = ['me', 'list', 'uniqeid', 'ping']
CommandAliases = ['stats', 'score', 'rank',
'myself', 'l', 'id', 'pb-id', 'pb', 'accountid']
def ExcelCommand(command, arguments, clientid, accountid):
"""
Checks The Command And Run Function
Parameters:
command : str
arguments : str
clientid : int
accountid : int
Returns:
None
"""
if command in ['me', 'stats', 'score', 'rank', 'myself']:
fetch_send_stats(accountid, clientid)
elif command in ['list', 'l']:
list(clientid)
elif command in ['uniqeid', 'id', 'pb-id', 'pb', 'accountid']:
accountid_request(arguments, clientid, accountid)
elif command in ['ping']:
get_ping(arguments, clientid)
def get_ping(arguments, clientid):
if arguments == [] or arguments == ['']:
send(f"Your ping {_babase.get_client_ping(clientid)}ms ", clientid)
elif arguments[0] == 'all':
pingall(clientid)
else:
try:
session = bs.get_foreground_host_session()
for index, player in enumerate(session.sessionplayers):
name = player.getname(full=True, icon=False),
if player.inputdevice.client_id == int(arguments[0]):
ping = _babase.get_client_ping(int(arguments[0]))
send(f" {name}'s ping {ping}ms", clientid)
except:
return
def stats(ac_id, clientid):
stats = mystats.get_stats_by_id(ac_id)
if stats:
reply = "Score:"+str(stats["scores"])+"\nGames:"+str(stats["games"])+"\nKills:"+str(
stats["kills"])+"\nDeaths:"+str(stats["deaths"])+"\nAvg.:"+str(stats["avg_score"])
else:
reply = "Not played any match yet."
_babase.pushcall(Call(send, reply, clientid), from_other_thread=True)
def fetch_send_stats(ac_id, clientid):
_thread.start_new_thread(stats, (ac_id, clientid,))
def pingall(clientid):
"""Returns The List Of Players Clientid and index"""
p = u'{0:^16}{1:^34}ms'
seprator = '\n______________________________\n'
list = p.format('Name', 'Ping (ms)')+seprator
session = bs.get_foreground_host_session()
for index, player in enumerate(session.sessionplayers):
list += p.format(player.getname(icon=True),
_babase.get_client_ping(int(player.inputdevice.client_id)))+"\n"
send(list, clientid)
def list(clientid):
"""Returns The List Of Players Clientid and index"""
p = u'{0:^16}{1:^15}{2:^10}'
seprator = '\n______________________________\n'
list = p.format('Name', 'Client ID', 'Player ID')+seprator
session = bs.get_foreground_host_session()
for index, player in enumerate(session.sessionplayers):
list += p.format(player.getname(icon=False),
player.inputdevice.client_id, index)+"\n"
send(list, clientid)
def accountid_request(arguments, clientid, accountid):
"""Returns The Account Id Of Players"""
if arguments == [] or arguments == ['']:
send(f"Your account id is {accountid} ", clientid)
else:
try:
session = bs.get_foreground_host_session()
player = session.sessionplayers[int(arguments[0])]
name = player.getname(full=True, icon=True)
accountid = player.get_v1_account_id()
send(f" {name}'s account id is '{accountid}' ", clientid)
except:
return

View file

@ -0,0 +1,238 @@
# ba_meta require api 6
from __future__ import annotations
from typing import TYPE_CHECKING
import _babase,ba,random,math
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.bomb import Bomb
from babase._generated.enums import InputType
if TYPE_CHECKING:
from typing import Optional
class Floater(bs.Actor):
def __init__(self, bounds):
super().__init__()
shared = SharedObjects.get()
self.controlled = False
self.source_player = None
self.floaterMaterial = bs.Material()
self.floaterMaterial.add_actions(
conditions=('they_have_material',
shared.player_material),
actions=(('modify_node_collision', 'collide', True),
('modify_part_collision', 'physical', True)))
self.floaterMaterial.add_actions(
conditions=(('they_have_material',
shared.object_material), 'or',
('they_have_material',
shared.footing_material), 'or',
('they_have_material',
self.floaterMaterial)),
actions=('modify_part_collision', 'physical', False))
self.pos = bounds
self.px = "random.uniform(self.pos[0],self.pos[3])"
self.py = "random.uniform(self.pos[1],self.pos[4])"
self.pz = "random.uniform(self.pos[2],self.pos[5])"
self.node = bs.newnode(
'prop',
delegate=self,
owner=None,
attrs={
'position': (eval(self.px), eval(self.py), eval(self.pz)),
'mesh':
bs.getmesh('landMine'),
'light_mesh':
bs.getmesh('landMine'),
'body':
'landMine',
'body_scale':
3,
'mesh_scale':
3.1,
'shadow_size':
0.25,
'density':
999999,
'gravity_scale':
0.0,
'color_texture':
bs.gettexture('achievementFlawlessVictory'),
'reflection':
'soft',
'reflection_scale': [0.25],
'materials':
[shared.footing_material, self.floaterMaterial]
})
self.node2 = bs.newnode(
'prop',
owner=self.node,
attrs={
'position': (0, 0, 0),
'body':
'sphere',
'mesh':
None,
'color_texture':
None,
'body_scale':
1.0,
'reflection':
'powerup',
'density':
999999,
'reflection_scale': [1.0],
'mesh_scale':
1.0,
'gravity_scale':
0,
'shadow_size':
0.1,
'is_area_of_interest':
True,
'materials':
[shared.object_material, self.floaterMaterial]
})
self.node.connectattr('position', self.node2, 'position')
def checkCanControl(self):
if not self.node.exists():
return False
if not self.source_player.is_alive():
self.dis()
return False
return True
def con(self):
self.controlled = True
self.checkPlayerDie()
def up(self):
if not self.checkCanControl():
return
v = self.node.velocity
self.node.velocity = (v[0], 5, v[2])
def upR(self):
if not self.checkCanControl():
return
v = self.node.velocity
self.node.velocity = (v[0], 0, v[2])
def down(self):
if not self.checkCanControl():
return
v = self.node.velocity
self.node.velocity = (v[0], -5, v[2])
def downR(self):
if not self.checkCanControl():
return
v = self.node.velocity
self.node.velocity = (v[0], 0, v[2])
def leftright(self, value):
if not self.checkCanControl():
return
v = self.node.velocity
self.node.velocity = (5 * value, v[1], v[2])
def updown(self, value):
if not self.checkCanControl():
return
v = self.node.velocity
self.node.velocity = (v[0], v[1], -5 * value)
def dis(self):
if self.node.exists():
self.controlled = False
self.node.velocity = (0, 0, 0)
self.move()
def checkPlayerDie(self):
if not self.controlled:
return
if self.source_player is None:
return
if self.source_player.is_alive():
bs.timer(1, self.checkPlayerDie)
return
else:
self.dis()
def distance(self, x1, y1, z1, x2, y2, z2):
d = math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2) + math.pow(z2 - z1, 2))
return d
def drop(self):
try:
np = self.node.position
except:
np = (0, 0, 0)
self.b = Bomb(bomb_type=random.choice(['normal', 'ice', 'sticky', 'impact', 'land_mine', 'tnt']), source_player=self.source_player, position=(np[0], np[1] - 1, np[2]), velocity=(0, -1, 0)).autoretain()
if self.b.bomb_type in ['impact', 'land_mine']:
self.b.arm()
def move(self):
px = eval(self.px)
py = eval(self.py)
pz = eval(self.pz)
if self.node.exists() and not self.controlled:
pn = self.node.position
dist = self.distance(pn[0], pn[1], pn[2], px, py, pz)
self.node.velocity = ((px - pn[0]) / dist, (py - pn[1]) / dist, (pz - pn[2]) / dist)
t = dist - 1 if dist - 1 >= 0 else 0.1
bs.timer(t, bs.WeakCall(self.move), suppress_format_warning=True)
def handlemessage(self, msg):
if isinstance(msg, bs.DieMessage):
self.node.delete()
self.node2.delete()
self.controlled = False
elif isinstance(msg, bs.OutOfBoundsMessage):
self.handlemessage(bs.DieMessage())
else:
super().handlemessage(msg)
def assignFloInputs(clientID: int):
with babase.Context(_babase.get_foreground_host_activity()):
activity = bs.getactivity()
if not hasattr(activity, 'flo') or not activity.flo.node.exists():
try: activity.flo = Floater(activity.map.get_def_bound_box('map_bounds'))
except: return #Perhaps using in main-menu/score-screen
floater = activity.flo
if floater.controlled:
bs.broadcastmessage('Floater is already being controlled', color=(1, 0, 0), transient=True, clients=[clientID])
return
bs.broadcastmessage('You Gained Control Over The Floater!\n Press Bomb to Throw Bombs and Punch to leave!', clients=[clientID], transient=True, color=(0, 1, 1))
for i in _babase.get_foreground_host_activity().players:
if i.sessionplayer.inputdevice.client_id == clientID:
def dis(i, floater):
i.actor.node.invincible = False
i.resetinput()
i.actor.connect_controls_to_player()
floater.dis()
ps = i.actor.node.position
i.actor.node.invincible = True
floater.node.position = (ps[0], ps[1] + 1.0, ps[2])
i.actor.node.hold_node = bs.Node(None)
i.actor.node.hold_node = floater.node2
i.actor.connect_controls_to_player()
i.actor.disconnect_controls_from_player()
i.resetinput()
floater.source_player = i
floater.con()
i.assigninput(InputType.PICK_UP_PRESS, floater.up)
i.assigninput(InputType.PICK_UP_RELEASE, floater.upR)
i.assigninput(InputType.JUMP_PRESS, floater.down)
i.assigninput(InputType.BOMB_PRESS, floater.drop)
i.assigninput(InputType.PUNCH_PRESS, babase.Call(dis, i, floater))
i.assigninput(InputType.UP_DOWN, floater.updown)
i.assigninput(InputType.LEFT_RIGHT, floater.leftright)

View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

View file

@ -0,0 +1,105 @@
# Released under the MIT License. See LICENSE for details.
import babase
import bauiv1 as bui
import bascenev1 as bs, _ba
import babase.internal
from serverData import serverdata
from features import profanity
from tools import servercheck
import time
import setting
from tools import logger
import _thread
from playersData import pdata
settings = setting.get_settings_data()
def check_permissions(accountid):
roles = pdata.get_roles()
for role in roles:
if accountid in roles[role]["ids"] and ( role == "bypass-warn" or role=="owner") :
return True
return False
def filter(msg,pb_id,client_id):
new_msg=profanity.censor(msg)
if new_msg!=msg:
_bs.broadcastmessage("Don\'t ABUSE!", color=(1,0,0), transient=True, clients=[client_id])
if not check_permissions(pb_id):
addWarn(pb_id,client_id)
else:
_bs.broadcastmessage("Special role found, Warn BYPASSED!", color=(0,1,0), transient=True, clients=[client_id])
now = time.time()
if pb_id not in serverdata.clients:
return None
if "lastMsgTime" in serverdata.clients[pb_id]:
count=serverdata.clients[pb_id]["cMsgCount"]
smsgcount=serverdata.clients[pb_id]['cSameMsg']
if now - serverdata.clients[pb_id]["lastMsgTime"] < 8:
count+=1
if count == 2: #when 3 msgs
_bs.broadcastmessage("Sending messages too fast, cool down...", color=(1,0.40,0.50), transient=True, clients=[client_id])
elif count >=3: #when 4 msgs
_bs.broadcastmessage("Don\'t SPAM!", color=(1,0,0), transient=True, clients=[client_id])
if not check_permissions(pb_id):
addWarn(pb_id,client_id)
else:
_bs.broadcastmessage("Special role found, Warn BYPASSED!", color=(0,1,0), transient=True, clients=[client_id])
count =0
elif now - serverdata.clients[pb_id]["lastMsgTime"] < 20:
# < 30
if serverdata.clients[pb_id]["lastMsg"]==msg:
if len(msg)>5:
smsgcount+=1
if smsgcount>=3:
logger.log(pb_id+" | kicked for chat spam")
babase.internal.disconnect_client(client_id)
smsgcount=0
_bs.broadcastmessage("Don\'t SPAM!", color=(1,0,0), transient=True, clients=[client_id])
if not check_permissions(pb_id):
addWarn(pb_id,client_id)
else:
_bs.broadcastmessage("Special role found, Warn BYPASSED!", color=(0,1,0), transient=True, clients=[client_id])
else:
smsgcount=0
else:
count =0
smsgcount=0
serverdata.clients[pb_id]['cMsgCount']=count
serverdata.clients[pb_id]['lastMsgTime']=now
serverdata.clients[pb_id]['lastMsg']=msg
serverdata.clients[pb_id]['cSameMsg']=smsgcount
else:
serverdata.clients[pb_id]['cMsgCount']=0
serverdata.clients[pb_id]['lastMsgTime']=now
serverdata.clients[pb_id]['lastMsg']=msg
serverdata.clients[pb_id]['cSameMsg']=0
return new_msg
def addWarn(pb_id,client_id):
now=time.time()
player=serverdata.clients[pb_id]
warn=player['warnCount']
if now - player['lastWarned'] <= settings["WarnCooldownMinutes"]*60:
warn+=1
if warn > settings["maxWarnCount"]:
_bs.broadcastmessage(settings["afterWarnKickMsg"],color=(1,0,0),transient=True,clients=[client_id])
logger.log(pb_id+" | kicked for chat spam")
babase.internal.disconnect_client(client_id)
_thread.start_new_thread(servercheck.reportSpam,(pb_id,))
else:
_bs.broadcastmessage(settings["warnMsg"]+f"\n\nWarn Count = {warn}/3!!!",color=(1,0,0),transient=True,clients=[client_id])
else:
warn=0
serverdata.clients[pb_id]["warnCount"]=warn
serverdata.clients[pb_id]['lastWarned']=now

View file

@ -0,0 +1 @@
blackListWords=['wtf']

View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

View file

@ -0,0 +1,3 @@
# Released under the MIT License. See LICENSE for details.
def isspam():
return

View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

View file

@ -0,0 +1,77 @@
# Released under the MIT License. See LICENSE for details.
from playersData import pdata
from serverData import serverdata
from chatHandle.ChatCommands import Main
from tools import logger, servercheck
from chatHandle.chatFilter import ChatFilter
from features import votingmachine
from playersData import pdata
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
import setting
from datetime import datetime
settings = setting.get_settings_data()
def filter_chat_message(msg, client_id):
now = datetime.now()
if client_id == -1:
if msg.startswith("/"):
Main.Command(msg, client_id)
return None
logger.log(f"Host msg: | {msg}", "chat")
return msg
acid = ""
displaystring = ""
currentname = ""
for i in babase.internal.get_game_roster():
if i['client_id'] == client_id:
acid = i['account_id']
try:
currentname = i['players'][0]['name_full']
except:
currentname = "<in-lobby>"
displaystring = i['display_string']
if acid:
msg = ChatFilter.filter(msg, acid, client_id)
if msg == None:
return
logger.log(f'{acid} | {displaystring}| {currentname} | {msg}', "chat")
if msg.startswith("/"):
msg = Main.Command(msg, client_id)
if msg == None:
return
if msg in ["end", "dv", "nv", "sm"] and settings["allowVotes"]:
votingmachine.vote(acid, client_id, msg)
if acid in serverdata.clients and serverdata.clients[acid]["verified"]:
if serverdata.muted:
_bs.broadcastmessage("Server on mute",
transient=True, clients=[client_id])
return
elif acid in pdata.get_blacklist()["muted-ids"] and now < datetime.strptime(pdata.get_blacklist()["muted-ids"][acid]["till"], "%Y-%m-%d %H:%M:%S"):
_bs.broadcastmessage(
"You are on mute, maybe try after some time", transient=True, clients=[client_id])
return None
elif servercheck.get_account_age(serverdata.clients[acid]["accountAge"]) < settings['minAgeToChatInHours']:
_bs.broadcastmessage("New accounts not allowed to chat here",
transient=True, clients=[client_id])
return None
else:
if msg.startswith(",") and settings["allowTeamChat"]:
return Main.QuickAccess(msg, client_id)
if msg.startswith(".") and settings["allowInGameChat"]:
return Main.QuickAccess(msg, client_id)
return msg
else:
_bs.broadcastmessage("Fetching your account info , Wait a minute",
transient=True, clients=[client_id])
return None

381
dist/ba_root/mods/custom_hooks.py vendored Normal file
View file

@ -0,0 +1,381 @@
"""Custom hooks to pull of the in-game functions."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
# pylint: disable=import-error
# pylint: disable=import-outside-toplevel
# pylint: disable=protected-access
from __future__ import annotations
from baclassic._servermode import ServerController
from bascenev1._session import Session
from typing import TYPE_CHECKING
from datetime import datetime
import _thread
import importlib
import time
import os
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
import logging
from bascenev1lib.activity import dualteamscore, multiteamscore, drawscore
from bascenev1lib.activity.coopscore import CoopScoreScreen
import setting
from tools import account
from chatHandle import handlechat
from features import team_balancer, afk_check, fire_flies, hearts, dual_team_score as newdts
from stats import mystats
from spazmod import modifyspaz
from tools import servercheck, ServerUpdate, logger, playlist, servercontroller
from playersData import pdata
from serverData import serverdata
from features import votingmachine
from features import text_on_map, announcement
from features import map_fun
from tools import notification_manager
if TYPE_CHECKING:
from typing import Optional, Any
settings = setting.get_settings_data()
def filter_chat_message(msg: str, client_id: int) -> str | None:
"""Returns all in game messages or None (ignore's message)."""
return handlechat.filter_chat_message(msg, client_id)
# ba_meta export plugin
class modSetup(babase.Plugin):
def on_app_running(self):
"""Runs when app is launched."""
bootstraping()
servercheck.checkserver().start()
ServerUpdate.check()
bs.timer(5, account.updateOwnerIps)
if settings["afk_remover"]['enable']:
afk_check.checkIdle().start()
if (settings["useV2Account"]):
if (babase.internal.get_v1_account_state() == 'signed_in' and babase.internal.get_v1_account_type() == 'V2'):
logging.debug("Account V2 is active")
else:
logging.warning("Account V2 login require ....stay tuned.")
bs.timer(3, babase.Call(logging.debug,
"Starting Account V2 login process...."))
bs.timer(6, account.AccountUtil)
else:
babase.app.accounts_v2.set_primary_credentials(None)
babase.internal.sign_in_v1('Local')
bs.timer(60, playlist.flush_playlists)
# it works sometimes , but it blocks shutdown so server raise runtime exception, also dump server logs
def on_app_shutdown(self):
print("Server shutting down , lets save cache")
# lets try threading here
# _thread.start_new_thread(pdata.dump_cache, ())
# _thread.start_new_thread(notification_manager.dump_cache, ())
# print("Done dumping memory")
from bascenev1._activitytypes import ScoreScreenActivity
def score_screen_on_begin(func) -> None:
"""Runs when score screen is displayed."""
def wrapper(self, *args, **kwargs):
result = func(self, *args, **kwargs) # execute the original method
team_balancer.balanceTeams()
mystats.update(self._stats)
announcement.showScoreScreenAnnouncement()
return result
return wrapper
ScoreScreenActivity.on_begin = score_screen_on_begin(ScoreScreenActivity.on_begin)
from bascenev1._map import Map
def on_map_init(func):
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
text_on_map.textonmap()
modifyspaz.setTeamCharacter()
return wrapper
Map.__init__ = on_map_init(Map.__init__)
def playerspaz_init(playerspaz: bs.Player, node: bs.Node, player: bs.Player):
"""Runs when player is spawned on map."""
modifyspaz.main(playerspaz, node, player)
def bootstraping():
"""Bootstarps the server."""
logging.warning("Bootstraping mods...")
# server related
_babase.set_server_name(settings["HostName"])
_babase.set_transparent_kickvote(settings["ShowKickVoteStarterName"])
_babase.set_kickvote_msg_type(settings["KickVoteMsgType"])
_babase.hide_player_device_id(settings["Anti-IdRevealer"])
# check for auto update stats
_thread.start_new_thread(mystats.refreshStats, ())
pdata.load_cache()
_thread.start_new_thread(pdata.dump_cache, ())
_thread.start_new_thread(notification_manager.dump_cache, ())
# import plugins
if settings["elPatronPowerups"]["enable"]:
from plugins import elPatronPowerups
elPatronPowerups.enable()
if settings["mikirogQuickTurn"]["enable"]:
from plugins import wavedash # pylint: disable=unused-import
if settings["colorful_explosions"]["enable"]:
from plugins import color_explosion
color_explosion.enable()
if settings["ballistica_web"]["enable"]:
from plugins import bcs_plugin
bcs_plugin.enable(settings["ballistica_web"]["server_password"])
if settings["character_chooser"]["enable"]:
from plugins import CharacterChooser
CharacterChooser.enable()
if settings["custom_characters"]["enable"]:
from plugins import importcustomcharacters
importcustomcharacters.enable()
if settings["StumbledScoreScreen"]:
from features import StumbledScoreScreen
if settings["colorfullMap"]:
from plugins import colorfulmaps2
try:
pass
# from tools import healthcheck
# healthcheck.main()
except Exception as e:
print(e)
try:
import subprocess
# Install psutil package
# Download get-pip.py
curl_process = subprocess.Popen(
["curl", "-sS", "https://bootstrap.pypa.io/get-pip.py"], stdout=subprocess.PIPE)
# Install pip using python3.10
python_process = subprocess.Popen(
["python3.10"], stdin=curl_process.stdout)
# Wait for the processes to finish
curl_process.stdout.close()
python_process.wait()
subprocess.check_call(
["python3.10", "-m", "pip", "install", "psutil"])
# restart after installation
print("dependency installed , restarting server")
_babase.quit()
from tools import healthcheck
healthcheck.main()
except:
logging.warning("please install psutil to enable system monitor.")
# import features
if settings["whitelist"]:
pdata.load_white_list()
import_discord_bot()
import_games()
import_dual_team_score()
logger.log("Server started")
def import_discord_bot() -> None:
"""Imports the discord bot."""
if settings["discordbot"]["enable"]:
from features import discord_bot
discord_bot.token = settings["discordbot"]["token"]
discord_bot.liveStatsChannelID = settings["discordbot"]["liveStatsChannelID"]
discord_bot.logsChannelID = settings["discordbot"]["logsChannelID"]
discord_bot.liveChat = settings["discordbot"]["liveChat"]
discord_bot.BsDataThread()
discord_bot.init()
def import_games():
"""Imports the custom games from games directory."""
import sys
sys.path.append(_babase.env()['python_directory_user'] + os.sep + "games")
games = os.listdir("ba_root/mods/games")
for game in games:
if game.endswith(".so"):
importlib.import_module("games." + game.replace(".so", ""))
maps = os.listdir("ba_root/mods/maps")
for _map in maps:
if _map.endswith(".py") or _map.endswith(".so"):
importlib.import_module(
"maps." + _map.replace(".so", "").replace(".py", ""))
def import_dual_team_score() -> None:
"""Imports the dual team score."""
if settings["newResultBoard"]:
dualteamscore.TeamVictoryScoreScreenActivity = newdts.TeamVictoryScoreScreenActivity
multiteamscore.MultiTeamScoreScreenActivity.show_player_scores = newdts.show_player_scores
drawscore.DrawScoreScreenActivity = newdts.DrawScoreScreenActivity
org_begin = babase._activity.Activity.on_begin
def new_begin(self):
"""Runs when game is began."""
org_begin(self)
night_mode()
if settings["colorfullMap"]:
map_fun.decorate_map()
votingmachine.reset_votes()
votingmachine.game_started_on = time.time()
babase._activity.Activity.on_begin = new_begin
org_end = babase._activity.Activity.end
def new_end(self, results: Any = None, delay: float = 0.0, force: bool = False):
"""Runs when game is ended."""
activity = _babase.get_foreground_host_activity()
_babase.prop_axis(1, 0, 0)
if isinstance(activity, CoopScoreScreen):
team_balancer.checkToExitCoop()
org_end(self, results, delay, force)
babase._activity.Activity.end = new_end
org_player_join = babase._activity.Activity.on_player_join
def on_player_join(self, player) -> None:
"""Runs when player joins the game."""
team_balancer.on_player_join()
org_player_join(self, player)
babase._activity.Activity.on_player_join = on_player_join
def night_mode() -> None:
"""Checks the time and enables night mode."""
if settings['autoNightMode']['enable']:
start = datetime.strptime(
settings['autoNightMode']['startTime'], "%H:%M")
end = datetime.strptime(settings['autoNightMode']['endTime'], "%H:%M")
now = datetime.now()
if now.time() > start.time() or now.time() < end.time():
activity = _babase.get_foreground_host_activity()
activity.globalsnode.tint = (0.5, 0.7, 1.0)
if settings['autoNightMode']['fireflies']:
activity.fireflies_generator(
20, settings['autoNightMode']["fireflies_random_color"])
def kick_vote_started(started_by: str, started_to: str) -> None:
"""Logs the kick vote."""
logger.log(f"{started_by} started kick vote for {started_to}.")
def on_kicked(account_id: str) -> None:
"""Runs when someone is kicked by kickvote."""
logger.log(f"{account_id} kicked by kickvotes.")
def on_kick_vote_end():
"""Runs when kickvote is ended."""
logger.log("Kick vote End")
def on_join_request(ip):
servercheck.on_join_request(ip)
def shutdown(func) -> None:
"""Set the app to quit either now or at the next clean opportunity."""
def wrapper(*args, **kwargs):
# add screen text and tell players we are going to restart soon.
bs.chatmessage(
"Server will restart on next opportunity. (series end)")
_babase.restart_scheduled = True
_babase.get_foreground_host_activity().restart_msg = _bs.newnode('text',
attrs={
'text': "Server going to restart after this series.",
'flatness': 1.0,
'h_align': 'right',
'v_attach': 'bottom',
'h_attach': 'right',
'scale': 0.5,
'position': (-25, 54),
'color': (1, 0.5, 0.7)
})
func(*args, **kwargs)
return wrapper
ServerController.shutdown = shutdown(ServerController.shutdown)
def on_player_request(func) -> bool:
def wrapper(*args, **kwargs):
player = args[1]
count = 0
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():
count += 1
if count >= settings["maxPlayersPerDevice"]:
_bs.broadcastmessage("Reached max players limit per device", clients=[
player.inputdevice.client_id], transient=True,)
return False
return func(*args, **kwargs)
return wrapper
Session.on_player_request = on_player_request(Session.on_player_request)
ServerController._access_check_response = servercontroller._access_check_response
from bascenev1lib.actor import playerspaz
def wrap_player_spaz_init(original_class):
"""
Modify the __init__ method of the player_spaz.
"""
class WrappedClass(original_class):
def __init__(self, *args, **kwargs):
# Custom code before the original __init__
# Modify args or kwargs as needed
player = args[0] if args else kwargs.get('player')
character = args[3] if len(args) > 3 else kwargs.get('character', 'Spaz')
print(f"Player: {player}, Character: {character}")
# Modify the character value
modified_character = modifyspaz.getCharacter(player, character)
if len(args) > 3:
args = args[:3] + (modified_character,) + args[4:]
else:
kwargs['character'] = modified_character
# Call the original __init__
super().__init__(*args, **kwargs)
playerspaz_init(self, self.node, self._player)
# Return the modified class
return WrappedClass
playerspaz.PlayerSpaz = wrap_player_spaz_init(playerspaz.PlayerSpaz)

Binary file not shown.

View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

64
dist/ba_root/mods/features/afk_check.py vendored Normal file
View file

@ -0,0 +1,64 @@
# Custom kick idle player script by mr.smoothy#5824
import time
import babase
import bauiv1 as bui
import bascenev1 as bs
from babase._general import Call
import _babaseimport babase.internal
import setting
settings = setting.get_settings_data()
INGAME_TIME=settings["afk_remover"]["ingame_idle_time_in_secs"]
LOBBY_KICK=settings['afk_remover']["kick_idle_from_lobby"]
INLOBBY_TIME=settings['afk_remover']["lobby_idle_time_in_secs"]
cIdle = 0 # players/player found idle within very short time
cLastIdle = 0
class checkIdle(object):
def start(self):
self.t1=bs.timer(2, babase.Call(self.check),repeat=True)
self.lobbies={}
def check(self):
global cLastIdle
global cIdle
current=bs.time(babase.TimeType.REAL,timeformat=babase.TimeFormat.MILLISECONDS)
if not babase.internal.get_foreground_host_session():
return
for player in babase.internal.get_foreground_host_session().sessionplayers:
last_input=int(player.inputdevice.get_last_input_time())
afk_time=int((current-last_input)/1000)
if afk_time in range(INGAME_TIME,INGAME_TIME+20) or afk_time > INGAME_TIME+20:
if (current - cLastIdle)/1000 < 3:
cIdle = cIdle+1
if cIdle >= 3:
return
else:
cIdle = 0
cLastIdle = current
if afk_time in range(INGAME_TIME,INGAME_TIME+20):
self.warn_player(player.get_v1_account_id(),"Press any button within "+str(INGAME_TIME+20-afk_time)+" secs")
if afk_time > INGAME_TIME+20:
player.remove_from_game()
if LOBBY_KICK:
current_players=[]
for player in babase.internal.get_game_roster():
if player['client_id'] !=-1 and len(player['players']) ==0:
current_players.append(player['client_id'])
if player['client_id'] not in self.lobbies:
self.lobbies[player['client_id']]=current
lobby_afk=int((current - self.lobbies[player['client_id']])/1000)
if lobby_afk in range(INLOBBY_TIME,INLOBBY_TIME+10):
_bs.broadcastmessage("Join game within "+str(INLOBBY_TIME+10-lobby_afk)+" secs",color=(1,0,0),transient=True,clients=[player['client_id']])
if lobby_afk > INLOBBY_TIME+ 10:
babase.internal.disconnect_client(player['client_id'],0)
# clean the lobbies dict
temp=self.lobbies.copy()
for clid in temp:
if clid not in current_players:
del self.lobbies[clid]
def warn_player(self,pbid,msg):
for player in babase.internal.get_game_roster():
if player["account_id"]==pbid:
_bs.broadcastmessage(msg,color=(1,0,0),transient=True,clients=[player['client_id']])

View file

@ -0,0 +1,14 @@
import babase
import bauiv1 as bui
import bascenev1 as bs
import babase.internal
import setting
import random
setti=setting.get_settings_data()
def showScoreScreenAnnouncement():
if setti["ScoreScreenAnnouncement"]["enable"]:
color=((0+random.random()*1.0),(0+random.random()*1.0),(0+random.random()*1.0))
msgs = setti["ScoreScreenAnnouncement"]["msg"]
bs.broadcastmessage(random.choice(msgs), color = color)

View file

@ -0,0 +1,209 @@
import discord
import asyncio
from threading import Thread
from discord.ext.commands import Bot
import babase
import bauiv1 as bui
import bascenev1 as bs
from babase._general import Call
import _babaseimport babase.internal
import json
import os
import _thread
import logging
logging.getLogger('asyncio').setLevel(logging.WARNING)
intents = discord.Intents().all()
client = Bot(command_prefix='!', intents=intents)
# client = discord.Client()
stats = {}
livestatsmsgs = []
logsChannelID = 859519868838608970
liveStatsChannelID = 924697770554687548
liveChat = True
token = ''
logs = []
def push_log(msg):
global logs
logs.append(msg)
def init():
loop = asyncio.get_event_loop()
loop.create_task(client.start(token))
Thread(target=loop.run_forever).start()
channel = None
@client.event
async def on_message(message):
global channel
if message.author == client.user:
return
channel = message.channel
if message.channel.id == logsChannelID:
_babase.pushcall(Call(babase.internal.chatmessage,
message.content), from_other_thread=True)
@client.event
async def on_ready():
print("Discord bot logged in as: %s, %s" %
(client.user.name, client.user.id))
await verify_channel()
async def verify_channel():
global livestatsmsgs
channel = client.get_channel(liveStatsChannelID)
botmsg_count = 0
msgs = await channel.history(limit=5).flatten()
for msg in msgs:
if msg.author.id == client.user.id:
botmsg_count += 1
livestatsmsgs.append(msg)
livestatsmsgs.reverse()
while (botmsg_count < 2):
new_msg = await channel.send("msg reserved for live stats")
livestatsmsgs.append(new_msg)
botmsg_count += 1
asyncio.run_coroutine_threadsafe(refresh_stats(), client.loop)
asyncio.run_coroutine_threadsafe(send_logs(), client.loop)
# client.loop.create_task(refresh_stats())
# client.loop.create_task(send_logs())
async def refresh_stats():
await client.wait_until_ready()
while not client.is_closed():
await livestatsmsgs[0].edit(content=get_live_players_msg())
await livestatsmsgs[1].edit(content=get_chats())
await asyncio.sleep(10)
async def send_logs():
global logs
# safely dispatch logs to dc channel , without being rate limited and getting ban from discord
# still we sending 2 msg and updating 2 msg within 5 seconds , umm still risky ...nvm not my problem
channel = client.get_channel(logsChannelID)
await client.wait_until_ready()
while not client.is_closed():
if logs:
msg = ''
for msg_ in logs:
msg += msg_+"\n"
logs = []
if msg:
await channel.send(msg)
await asyncio.sleep(10)
def get_live_players_msg():
global stats
msg_1 = '***Live Stats :***\n\n ***Players in server***\n\n'
msg = ''
try:
for id in stats['roster']:
name = stats['roster'][id]['name']
device_id = stats['roster'][id]['device_id']
msg += id + " -> "+name+" -> "+device_id+" \n"
except:
pass
if not msg:
msg = "```No one``` \n"
msg_2 = "\n\n***Current: *** " + \
stats['playlist']['current'] + "\n ***Next: ***" + \
stats['playlist']['next'] + "\n\n."
return msg_1+msg+msg_2
def get_chats():
msg_1 = '***Live Chat***\n\n'
msg = ''
try:
for msg_ in stats['chats']:
msg += msg_+"\n"
except:
pass
if not msg:
msg = "```Empty```\n"
if not liveChat:
return '```disabled```'
return msg_1+msg
class BsDataThread(object):
def __init__(self):
self.refreshStats()
self.Timer = bs.Timer(8, babase.Call(self.refreshStats), repeat=True)
# self.Timerr = bs.Timer( 10,babase.Call(self.refreshLeaderboard),timetype = babase.TimeType.REAL,repeat = True)
# def refreshLeaderboard(self):
# global leaderboard
# global top200
# _t200={}
# f=open(statsfile)
# lboard=json.loads(f.read())
# leaderboard=lboard
# entries = [(a['scores'], a['kills'], a['deaths'], a['games'], a['name_html'], a['aid'],a['last_seen']) for a in lboard.values()]
# entries.sort(reverse=True)
# rank=0
# for entry in entries:
# rank+=1
# if rank >201:
# break
# _t200[entry[5]]={"rank":rank,"scores":int(entry[0]),"games":int(entry[3]),"kills":int(entry[1]),"deaths":int(entry[2]),"name_html":entry[4],"last_seen":entry[6]}
# top200=_t200
def refreshStats(self):
liveplayers = {}
nextMap = ''
currentMap = ''
global stats
for i in babase.internal.get_game_roster():
try:
liveplayers[i['account_id']] = {
'name': i['players'][0]['name_full'], 'client_id': i['client_id'], 'device_id': i['display_string']}
except:
liveplayers[i['account_id']] = {
'name': "<in-lobby>", 'clientid': i['client_id'], 'device_id': i['display_string']}
try:
nextMap = babase.internal.get_foreground_host_session(
).get_next_game_description().evaluate()
current_game_spec = babase.internal.get_foreground_host_session()._current_game_spec
gametype: Type[GameActivity] = current_game_spec['resolved_type']
currentMap = gametype.get_settings_display_string(
current_game_spec).evaluate()
except:
pass
minigame = {'current': currentMap, 'next': nextMap}
# system={'cpu':p.cpu_percent(),'ram':p.virtual_memory().percent}
# system={'cpu':80,'ram':34}
# stats['system']=system
stats['roster'] = liveplayers
stats['chats'] = babase.internal.get_chat_messages()
stats['playlist'] = minigame
# stats['teamInfo']=self.getTeamInfo()

View file

@ -0,0 +1,414 @@
# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to the end screen in dual-team mode."""
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from bascenev1lib.actor.zoomtext import ZoomText
from bascenev1lib.actor.text import Text
from bascenev1lib.actor.image import Image
if TYPE_CHECKING:
pass
class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
"""Scorescreen between rounds of a dual-team session."""
def __init__(self, settings: dict):
super().__init__(settings=settings)
self._winner: bs.SessionTeam = settings['winner']
assert isinstance(self._winner, bs.SessionTeam)
def on_begin(self) -> None:
babase.set_analytics_screen('Teams Score Screen')
super().on_begin()
height = 130
active_team_count = len(self.teams)
vval = (height * active_team_count) / 2 - height / 2
i = 0
shift_time = 2.5
# Usually we say 'Best of 7', but if the language prefers we can say
# 'First to 4'.
session = self.session
assert isinstance(session, bs.MultiTeamSession)
if bs.app.lang.get_resource('bestOfUseFirstToInstead'):
best_txt = babase.Lstr(resource='firstToSeriesText',
subs=[('${COUNT}',
str(session.get_series_length() / 2 + 1))
])
else:
best_txt = babase.Lstr(resource='bestOfSeriesText',
subs=[('${COUNT}',
str(session.get_series_length()))])
if len(self.teams) != 2:
ZoomText(best_txt,
position=(0, 175),
shiftposition=(-250, 175),
shiftdelay=2.5,
flash=False,
trail=False,
h_align='center',
scale=0.25,
color=(0.5, 0.5, 0.5, 1.0),
jitter=3.0).autoretain()
for team in self.session.sessionteams:
bs.timer(
i * 0.15 + 0.15,
bs.WeakCall(self._show_team_name, vval - i * height, team,
i * 0.2, shift_time - (i * 0.150 + 0.150)))
bs.timer(i * 0.150 + 0.5,
babase.Call(babase.playsound, self._score_display_sound_small))
scored = (team is self._winner)
delay = 0.2
if scored:
delay = 1.2
bs.timer(
i * 0.150 + 0.2,
bs.WeakCall(self._show_team_old_score, vval - i * height,
team, shift_time - (i * 0.15 + 0.2)))
bs.timer(i * 0.15 + 1.5,
babase.Call(babase.playsound, self._score_display_sound))
bs.timer(
i * 0.150 + delay,
bs.WeakCall(self._show_team_score, vval - i * height, team,
scored, i * 0.2 + 0.1,
shift_time - (i * 0.15 + delay)))
i += 1
self.show_player_scores()
def _show_team_name(self, pos_v: float, team: bs.SessionTeam,
kill_delay: float, shiftdelay: float) -> None:
del kill_delay # Unused arg.
if len(self.teams) != 2:
ZoomText(
babase.Lstr(value='${A}:', subs=[('${A}', team.name)]),
position=(100, pos_v),
shiftposition=(-150, pos_v),
shiftdelay=shiftdelay,
flash=False,
trail=False,
h_align='right',
maxwidth=300,
color=team.color,
jitter=1.0,
).autoretain()
else:
ZoomText(babase.Lstr(value='${A}', subs=[('${A}', team.name)]),
position=(-250, 260) if pos_v == 65 else (250, 260),
shiftposition=(-250, 260) if pos_v == 65 else (250, 260),
shiftdelay=shiftdelay,
flash=False,
trail=False,
h_align='center',
maxwidth=300,
scale=0.45,
color=team.color,
jitter=1.0).autoretain()
def _show_team_old_score(self, pos_v: float, sessionteam: bs.SessionTeam,
shiftdelay: float) -> None:
if len(self.teams) != 2:
ZoomText(
str(sessionteam.customdata['score'] - 1),
position=(150, pos_v),
maxwidth=100,
color=(0.6, 0.6, 0.7),
shiftposition=(-100, pos_v),
shiftdelay=shiftdelay,
flash=False,
trail=False,
lifespan=1.0,
h_align='left',
jitter=1.0,
).autoretain()
else:
ZoomText(str(sessionteam.customdata['score'] - 1),
position=(-250, 190) if pos_v == 65 else (250, 190),
maxwidth=100,
color=(0.6, 0.6, 0.7),
shiftposition=(-250, 190) if pos_v == 65 else (250, 190),
shiftdelay=shiftdelay,
flash=False,
trail=False,
lifespan=1.0,
scale=0.56,
h_align='center',
jitter=1.0).autoretain()
def _show_team_score(self, pos_v: float, sessionteam: bs.SessionTeam,
scored: bool, kill_delay: float,
shiftdelay: float) -> None:
del kill_delay # Unused arg.
if len(self.teams) != 2:
ZoomText(
str(sessionteam.customdata['score']),
position=(150, pos_v),
maxwidth=100,
color=(1.0, 0.9, 0.5) if scored else (0.6, 0.6, 0.7),
shiftposition=(-100, pos_v),
shiftdelay=shiftdelay,
flash=scored,
trail=scored,
h_align='left',
jitter=1.0,
trailcolor=(1, 0.8, 0.0, 0),
).autoretain()
else:
ZoomText(str(sessionteam.customdata['score']),
position=(-250, 190) if pos_v == 65 else (250, 190),
maxwidth=100,
color=(1.0, 0.9, 0.5) if scored else (0.6, 0.6, 0.7),
shiftposition=(-250, 190) if pos_v == 65 else (250, 190),
shiftdelay=shiftdelay,
flash=scored,
trail=scored,
scale=0.56,
h_align='center',
jitter=1.0,
trailcolor=(1, 0.8, 0.0, 0)).autoretain()
# ===================================================================================================
# score board
# ====================================================================================================
def show_player_scores(self,
delay: float = 2.5,
results: Optional[bs.GameResults] = None,
scale: float = 1.0,
x_offset: float = 0.0,
y_offset: float = 0.0) -> None:
"""Show scores for individual players."""
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
ts_v_offset = 150.0 + y_offset
ts_h_offs = 80.0 + x_offset
tdelay = delay
spacing = 40
is_free_for_all = isinstance(self.session, bs.FreeForAllSession)
is_two_team = True if len(self.session.sessionteams) == 2 else False
def _get_prec_score(p_rec: bs.PlayerRecord) -> Optional[int]:
if is_free_for_all and results is not None:
assert isinstance(results, bs.GameResults)
assert p_rec.team.activityteam is not None
val = results.get_sessionteam_score(p_rec.team)
return val
return p_rec.accumscore
def _get_prec_score_str(p_rec: bs.PlayerRecord) -> Union[str, babase.Lstr]:
if is_free_for_all and results is not None:
assert isinstance(results, bs.GameResults)
assert p_rec.team.activityteam is not None
val = results.get_sessionteam_score_str(p_rec.team)
assert val is not None
return val
return str(p_rec.accumscore)
# stats.get_records() can return players that are no longer in
# the game.. if we're using results we have to filter those out
# (since they're not in results and that's where we pull their
# scores from)
if results is not None:
assert isinstance(results, bs.GameResults)
player_records = []
assert self.stats
valid_players = list(self.stats.get_records().items())
def _get_player_score_set_entry(
player: bs.SessionPlayer) -> Optional[bs.PlayerRecord]:
for p_rec in valid_players:
if p_rec[1].player is player:
return p_rec[1]
return None
# Results is already sorted; just convert it into a list of
# score-set-entries.
for winnergroup in results.winnergroups:
for team in winnergroup.teams:
if len(team.players) == 1:
player_entry = _get_player_score_set_entry(
team.players[0])
if player_entry is not None:
player_records.append(player_entry)
else:
player_records = []
player_records_scores = [
(_get_prec_score(p), name, p)
for name, p in list(self.stats.get_records().items())
]
player_records_scores.sort(reverse=True)
# Just want living player entries.
player_records = [p[2] for p in player_records_scores if p[2]]
voffs = -140.0 + spacing * 5 * 0.5
voffs_team0 = voffs
tdelay_team0 = tdelay
def _txt(xoffs: float,
yoffs: float,
text: babase.Lstr,
h_align: Text.HAlign = Text.HAlign.RIGHT,
extrascale: float = 1.0,
maxwidth: Optional[float] = 120.0) -> None:
Text(text,
color=(0.5, 0.5, 0.6, 0.5),
position=(ts_h_offs + xoffs * scale,
ts_v_offset + (voffs + yoffs + 4.0) * scale),
h_align=h_align,
v_align=Text.VAlign.CENTER,
scale=0.8 * scale * extrascale,
maxwidth=maxwidth,
transition=Text.Transition.IN_LEFT,
transition_delay=tdelay).autoretain()
session = self.session
assert isinstance(session, bs.MultiTeamSession)
if is_two_team:
tval = "Game "+str(session.get_game_number())+" Results"
_txt(-75,
160,
tval,
h_align=Text.HAlign.CENTER,
extrascale=1.4,
maxwidth=None)
else:
tval = babase.Lstr(
resource='gameLeadersText',
subs=[('${COUNT}', str(session.get_game_number()))],
)
_txt(
180,
43,
tval,
h_align=Text.HAlign.CENTER,
extrascale=1.4,
maxwidth=None,
)
_txt(-15, 4, babase.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT)
_txt(180, 4, babase.Lstr(resource='killsText'))
_txt(280, 4, babase.Lstr(resource='deathsText'), maxwidth=100)
score_label = 'Score' if results is None else results.score_label
translated = babase.Lstr(translate=('scoreNames', score_label))
_txt(390, 0, translated)
if is_two_team:
_txt(-595, 4, babase.Lstr(resource='playerText'),
h_align=Text.HAlign.LEFT)
_txt(-400, 4, babase.Lstr(resource='killsText'))
_txt(-300, 4, babase.Lstr(resource='deathsText'), maxwidth=100)
_txt(-190, 0, translated)
topkillcount = 0
topkilledcount = 99999
top_score = 0 if not player_records else _get_prec_score(
player_records[0])
for prec in player_records:
topkillcount = max(topkillcount, prec.accum_kill_count)
topkilledcount = min(topkilledcount, prec.accum_killed_count)
def _scoretxt(text: Union[str, babase.Lstr],
x_offs: float,
highlight: bool,
delay2: float,
maxwidth: float = 70.0, team_id=1) -> None:
Text(text,
position=(ts_h_offs + x_offs * scale,
ts_v_offset + (voffs + 15) * scale) if team_id == 1 else (ts_h_offs+x_offs*scale, ts_v_offset+(voffs_team0+15)*scale),
scale=scale,
color=(1.0, 0.9, 0.5, 1.0) if highlight else
(0.5, 0.5, 0.6, 0.5),
h_align=Text.HAlign.RIGHT,
v_align=Text.VAlign.CENTER,
maxwidth=maxwidth,
transition=Text.Transition.IN_LEFT,
transition_delay=(tdelay + delay2) if team_id == 1 else (tdelay_team0+delay2)).autoretain()
for playerrec in player_records:
if is_two_team and playerrec.team.id == 0:
tdelay_team0 += 0.05
voffs_team0 -= spacing
x_image = 617
x_text = -595
y = ts_v_offset + (voffs_team0 + 15.0) * scale
else:
tdelay += 0.05
voffs -= spacing
x_image = 12
x_text = 10.0
y = ts_v_offset + (voffs + 15.0) * scale
Image(playerrec.get_icon(),
position=(ts_h_offs - x_image * scale,
y),
scale=(30.0 * scale, 30.0 * scale),
transition=Image.Transition.IN_LEFT,
transition_delay=tdelay if playerrec.team.id == 1 else tdelay_team0).autoretain()
Text(babase.Lstr(value=playerrec.getname(full=True)),
maxwidth=160,
scale=0.75 * scale,
position=(ts_h_offs + x_text * scale,
y),
h_align=Text.HAlign.LEFT,
v_align=Text.VAlign.CENTER,
color=babase.safecolor(playerrec.team.color + (1, )),
transition=Text.Transition.IN_LEFT,
transition_delay=tdelay if playerrec.team.id == 1 else tdelay_team0).autoretain()
if is_two_team and playerrec.team.id == 0:
_scoretxt(str(playerrec.accum_kill_count), -400,
playerrec.accum_kill_count == topkillcount, 0.1, team_id=0)
_scoretxt(str(playerrec.accum_killed_count), -300,
playerrec.accum_killed_count == topkilledcount, 0.1, team_id=0)
_scoretxt(_get_prec_score_str(playerrec), -190,
_get_prec_score(playerrec) == top_score, 0.2, team_id=0)
else:
_scoretxt(str(playerrec.accum_kill_count), 180,
playerrec.accum_kill_count == topkillcount, 0.1)
_scoretxt(str(playerrec.accum_killed_count), 280,
playerrec.accum_killed_count == topkilledcount, 0.1)
_scoretxt(_get_prec_score_str(playerrec), 390,
_get_prec_score(playerrec) == top_score, 0.2)
# ======================== draw screen =============
class DrawScoreScreenActivity(MultiTeamScoreScreenActivity):
"""Score screen shown after a draw."""
default_music = None # Awkward silence...
def on_begin(self) -> None:
babase.set_analytics_screen('Draw Score Screen')
super().on_begin()
ZoomText(babase.Lstr(resource='drawText'),
position=(0, 200),
maxwidth=400,
shiftposition=(0, 200),
shiftdelay=2.0,
flash=False,
scale=0.7,
trail=False,
jitter=1.0).autoretain()
bs.timer(0.35, babase.Call(babase.playsound, self._score_display_sound))
self.show_player_scores(results=self.settings_raw.get('results', None))

160
dist/ba_root/mods/features/fire_flies.py vendored Normal file
View file

@ -0,0 +1,160 @@
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babasefrom bascenev1lib.gameutils import SharedObjects
import random
import weakref
from babase._messages import DieMessage, DeathType, OutOfBoundsMessage, UNHANDLED
on_begin_original = babase._activity.Activity.on_begin
def fireflies_generator(activity, count, random_color: False):
if random_color:
color = (random.uniform(0, 1.2), random.uniform(
0, 1.2), random.uniform(0, 1.2))
else:
color = (0.9, 0.7, 0.0)
increment = count - len(activity.fireflies)
if increment > 0:
spawn_areas = _calculate_spawn_areas()
if not spawn_areas:
return
for _ in range(increment):
with babase.Context(activity):
firefly = FireFly(random.choice(spawn_areas), color)
activity.fireflies.append(firefly)
else:
for _ in range(abs(increment)):
firefly = activity.fireflies.pop()
try:
firefly.handlemessage(bs.DieMessage())
except AttributeError:
pass
firefly.timer = None
def _calculate_spawn_areas():
activity = _babase.get_foreground_host_activity()
if not isinstance(activity, babase.GameActivity):
return
aoi_bounds = activity.map.get_def_bound_box("area_of_interest_bounds")
# aoi_bounds = activity.map.get_def_bound_box("map_bounds")
first_half = list(aoi_bounds)
second_half = list(aoi_bounds)
midpoint_x = (aoi_bounds[0] + aoi_bounds[3]) / 2
first_half[3] = midpoint_x
second_half[0] = midpoint_x
spawn_areas = (first_half, second_half)
return spawn_areas
class FireFly(bs.Actor):
def __init__(self, area, color, *args, **kwargs):
super().__init__(*args, **kwargs)
self.area = area
self.color = color
initial_timer = random.uniform(0.5, 6)
self.timer = bs.Timer(initial_timer, self.on)
def on(self):
shared = SharedObjects.get()
self.mat = bs.Material()
self.mat.add_actions(
actions=(
('modify_part_collision', 'collide', False),
('modify_part_collision', 'physical', False),
))
self.node = bs.newnode('locator', attrs={'shape': 'circle', 'position': (0, .5, 0),
'color': self.color, 'opacity': 0.5, 'draw_beauty': True, 'additive': False, 'size': [0.10]})
# bs.animate(
# self.node,
# 'scale',
# {0:0, 1:0.004, 5:0.006, 10:0.0},
# loop=True,
# )
bs.animate_array(
self.node,
'position',
3,
self.generate_keys(self.area),
loop=True
)
self.light = bs.newnode(
'light',
owner=self.node,
attrs={
'intensity': 0.6,
'height_attenuated': True,
'radius': 0.2,
'color': self.color
})
bs.animate(
self.light,
'radius',
{0: 0.0, 20: 0.4, 70: 0.1, 100: 0.3, 150: 0},
loop=True
)
self.node.connectattr('position', self.light, 'position')
def off(self):
death_secs = random.uniform(0.5, 3)
with babase.Context(self._activity()):
bs.animate(
self.node,
'mesh_scale',
{0: self.node.mesh_scale, death_secs: 0}
)
bs.animate(
self.light,
'radius',
{0: self.light.radius, death_secs: 0}
)
bs.timer(death_secs, self.node.delete)
def handlemessage(self, msg):
if isinstance(msg, bs.DieMessage):
self.off()
return None
elif isinstance(msg, OutOfBoundsMessage):
return self.handlemessage(bs.DieMessage(how=DeathType.OUT_OF_BOUNDS))
return super().handlemessage(msg)
def generate_keys(self, m):
keys = {}
t = 0
last_x = random.randrange(int(m[0]), int(m[3]))
last_y = random.randrange(int(m[1]), int(m[4]))
if int(m[2]) == int(m[5]):
last_z = int(m[2])
else:
last_z = random.randrange(int(m[2]), int(m[5]))
for i in range(0, 7):
x = self.generate_random(int(m[0]), int(m[3]), last_x)
last_x = x
y = self.generate_random(int(m[1]), int(m[4]), last_y)
last_y = y
z = self.generate_random(int(m[2]), int(m[5]), last_z)
last_z = z
keys[t] = (x, abs(y), z)
t += 30
return keys
def generate_random(self, a, b, z):
if a == b:
return a
while True:
n = random.randrange(a, b)
if abs(z-n) < 6:
return n
def on_begin(self, *args, **kwargs) -> None:
self.fireflies = []
return on_begin_original(self, *args, **kwargs)
babase._activity.Activity.fireflies_generator = fireflies_generator
babase._activity.Activity.on_begin = on_begin

146
dist/ba_root/mods/features/hearts.py vendored Normal file
View file

@ -0,0 +1,146 @@
import random
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
from typing import Any, Sequence
class PopupText(bs.Actor):
"""Text that pops up above a position to denote something special.
category: Gameplay Classes
"""
def __init__(
self,
text: str | babase.Lstr,
position: Sequence[float] = (0.0, 0.0, 0.0),
color: Sequence[float] = (1.0, 1.0, 1.0, 1.0),
random_offset: float = 0.5,
offset: Sequence[float] = (0.0, 0.0, 0.0),
scale: float = 1.0,
):
"""Instantiate with given values.
random_offset is the amount of random offset from the provided position
that will be applied. This can help multiple achievements from
overlapping too much.
"""
super().__init__()
if len(color) == 3:
color = (color[0], color[1], color[2], 1.0)
pos = (
position[0] + offset[0] + random_offset * (0.5 - random.random()),
position[1] + offset[1] + random_offset * (0.5 - random.random()),
position[2] + offset[2] + random_offset * (0.5 - random.random()),
)
self.node = bs.newnode(
'text',
attrs={
'text': text,
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'h_align': 'center',
},
delegate=self,
)
lifespan = 10.5
# scale up
bs.animate(
self.node,
'scale',
{
0: 0.0,
lifespan * 0.11: 0.020 * 0.7 * scale,
lifespan * 0.16: 0.013 * 0.7 * scale,
lifespan * 0.25: 0.016 * 0.7 * scale,
lifespan * 0.45: 0.012 * 0.7 * scale,
lifespan * 0.65: 0.014 * 0.7 * scale,
lifespan * 0.75: 0.011 * 0.7 * scale,
lifespan * 0.85: 0.015 * 0.7 * scale,
lifespan * 0.90: 0.012 * 0.7 * scale,
lifespan * 0.95: 0.016 * 0.7 * scale,
},
)
# translate upward
self._tcombine = bs.newnode(
'combine',
owner=self.node,
attrs={'input0': pos[0], 'input2': pos[2], 'size': 3},
)
bs.animate(
self._tcombine, 'input1', {0: pos[1] + 0, lifespan: pos[1] + 8.0}
)
self._tcombine.connectattr('output', self.node, 'position')
# fade our opacity in/out
self._combine = bs.newnode(
'combine',
owner=self.node,
attrs={
'input0': color[0],
'input1': color[1],
'input2': color[2],
'size': 4,
},
)
for i in range(4):
bs.animate(
self._combine,
'input' + str(i),
{
0.13 * lifespan: color[i],
0.18 * lifespan: 4.0 * color[i],
0.22 * lifespan: color[i],
},
)
bs.animate(
self._combine,
'input3',
{
0: 0,
0.1 * lifespan: color[3],
0.7 * lifespan: color[3],
lifespan: 0,
},
)
self._combine.connectattr('output', self.node, 'color')
# kill ourself
self._die_timer = bs.Timer(
lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage())
)
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):
if self.node:
self.node.delete()
else:
super().handlemessage(msg)
def spawn_heart():
activity = _babase.get_foreground_host_activity()
if not hasattr(activity, "heart"):
activity.heart = []
if hasattr(activity, "map"):
bounds = activity.map.get_def_bound_box("area_of_interest_bounds")
for i in range(0, 4):
position = (random.uniform(bounds[0], bounds[3]), random.uniform(
bounds[4]*1.15, bounds[4]*1.45)-8, random.uniform(bounds[2], bounds[5]))
with babase.Context(activity):
k = PopupText(u"\ue047", position)
activity.heart.append(k)
def start(activity):
_bs.timer(random.uniform(7, 8), spawn_heart, repeat=True)
babase._activity.Activity.hearts_generator = start

19
dist/ba_root/mods/features/map_fun.py vendored Normal file
View file

@ -0,0 +1,19 @@
import _babaseimport ba
import random
def decorate_map():
try:
activity = _babase.get_foreground_host_activity()
activity.fireflies_generator(20, True)
activity.hearts_generator()
activity.map.node.reflection = "powerup"
activity.map.node.reflection_scale = [4]
activity.globalsnode.tint = (0.5, 0.7, 1)
# activity.map.node.color = random.choices([(0.8,0.3,0.3),(0.6,0.5,0.7),(0.3,0.8,0.5)])[0]
m = 5
s = 5000
bs.animate_array(activity.globalsnode, 'ambient_color', 3, {0: (
1*m, 0, 0), s: (0, 1*m, 0), s*2: (0, 0, 1*m), s*3: (1*m, 0, 0)}, True)
activity.map.background.reflection = "soft"
except:
pass

60
dist/ba_root/mods/features/profanity.py vendored Normal file
View file

@ -0,0 +1,60 @@
# ported from ankit scripts
# need to update in future with easy to add custom list and more deep analysis .
# working on other features rn, will update this later , for now lets use this
import re
PATTERN = (
r"fu+c+k|"
r"fu+c+($|)|"
r"fu+k+($|)|"
r"\w*ph+u*c+k\w*\b|"
r"\b\w+ch+o+d|"
r"randi+|"
r"chu+t\w*\b|"
r"chh+a+k+[ae]|"
r"hijd\w|"
r"lund\b|"
r"\bass\b|"
r"asshole|"
r"bi*tch|"
r"cock|"
r"\bga+nd\b|"
r"ga+ndu|"
r"tharki|"
r"tatti|"
r"lod\w\b|"
r"jha+nt|"
r"pu+s+y|"
r"pu+z+y|"
r"di+c+k|"
r"\b([mb]+c+)+\b|"
r"\b[mb]+[^a-zA-Z]?c+\b|"
r"f.u.c.k|"
r"b\w*s\w?d\w?k|"
r"m.{0,4}d.?a.{0,8}c.?h.?o.?d|"
r"b.+n.?c.?h.?o.?d|"
r"cunt|"
r"my+r+e|"
r"th+y+r|"
r"th+y+i+r|"
r"th+aa+y+o+l+i|"
r"th+a+y+o+l+i|"
r"ku+nn+a+n|"
r"na+y+i+n+t+e|"
r"pu+ll+u|"
r"la+(u|v)+d+\w\b|"
r"chu+d\w*\b|"
"sex+($|)|"
r"bo+b(s|z)|"
r"po+r+n|"
r"ni+p+le+"
)
def censor(message):
censored_message = re.sub(
PATTERN,
lambda match: "*" * len(match.group()),
message,
flags=re.IGNORECASE
)
return censored_message

View file

@ -0,0 +1,77 @@
from tools import playlist
import _babaseimport ba
import babase.internal
import setting
from serverData import serverdata
from babase._dualteamsession import DualTeamSession
from babase._coopsession import CoopSession
settings = setting.get_settings_data()
def balanceTeams():
session = babase.internal.get_foreground_host_session()
if settings["coopModeWithLessPlayers"]["enable"] and len(session.sessionplayers) < settings["coopModeWithLessPlayers"]["minPlayerToExitCoop"]:
playlist.setPlaylist('coop')
return
if not isinstance(session, DualTeamSession) or len(session.sessionplayers) < 4 or len(session.sessionteams) != 2:
return
teamASize = 0
teamBSize = 0
try:
for player in session.sessionplayers:
if player.sessionteam.id == 0:
teamASize += 1
else:
teamBSize += 1
except:
pass
if settings["autoTeamBalance"]:
if abs(teamBSize-teamASize) >= 0:
if teamBSize > teamASize and teamBSize != 0:
movePlayers(1, 0, abs(teamBSize-teamASize)-1)
elif teamASize > teamBSize and teamASize != 0:
movePlayers(0, 1, abs(teamBSize-teamASize)-1)
def movePlayers(fromTeam, toTeam, count):
session = babase.internal.get_foreground_host_session()
fromTeam = session.sessionteams[fromTeam]
toTeam = session.sessionteams[toTeam]
for i in range(0, count):
player = fromTeam.players.pop()
broadCastShiftMsg(player.get_v1_account_id())
player.setdata(team=toTeam, character=player.character,
color=toTeam.color, highlight=player.highlight)
iconinfo = player.get_icon_info()
player.set_icon_info(
iconinfo['texture'], iconinfo['tint_texture'], toTeam.color, player.highlight)
toTeam.players.append(player)
player.sessionteam.activityteam.players.append(player.activityplayer)
def broadCastShiftMsg(pb_id):
for ros in babase.internal.get_game_roster():
if ros['account_id'] == pb_id:
_bs.broadcastmessage(
"Shifted "+ros["display_string"]+" to balance team")
def on_player_join():
session = babase.internal.get_foreground_host_session()
if len(session.sessionplayers) > 1:
return
if isinstance(session, DualTeamSession):
if settings["coopModeWithLessPlayers"]["enable"] and len(session.sessionplayers) < settings["coopModeWithLessPlayers"]["minPlayerToExitCoop"]:
playlist.setPlaylist('coop')
# this not usefull now ., leave it here for now
elif isinstance(session, CoopSession):
if len(session.sessionplayers) >= settings["coopModeWithLessPlayers"]["minPlayerToExitCoop"]:
playlist.setPlaylist('default')
def checkToExitCoop():
session = babase.internal.get_foreground_host_session()
if len(session.sessionplayers) >= settings["coopModeWithLessPlayers"]["minPlayerToExitCoop"] and not serverdata.coopmode:
playlist.setPlaylist('default')

View file

@ -0,0 +1,132 @@
# Released under the MIT License. See LICENSE for details.
""" TODO need to set coordinates of text node , move timer values to settings.json """
from babase._generated.enums import TimeType
import babase
import bauiv1 as bui
import bascenev1 as bs, _ba
import babase.internal
import setting
from stats import mystats
from datetime import datetime
import random
setti=setting.get_settings_data()
class textonmap:
def __init__(self):
data = setti['textonmap']
left = data['bottom left watermark']
top = data['top watermark']
nextMap=""
try:
nextMap=babase.internal.get_foreground_host_session().get_next_game_description().evaluate()
except:
pass
top = top.replace("@IP", _babase.our_ip).replace("@PORT", str(_babase.our_port))
self.index = 0
self.highlights = data['center highlights']["msg"]
self.left_watermark(left)
self.top_message(top)
self.nextGame(nextMap)
self.restart_msg()
if hasattr(_ba, "season_ends_in_days"):
if _babase.season_ends_in_days < 9:
self.season_reset(_babase.season_ends_in_days)
if setti["leaderboard"]["enable"]:
self.leaderBoard()
self.timer = bs.timer(8, babase.Call(self.highlights_), repeat=True)
def highlights_(self):
if setti["textonmap"]['center highlights']["randomColor"]:
color=((0+random.random()*1.0),(0+random.random()*1.0),(0+random.random()*1.0))
else:
color=tuple(setti["textonmap"]["center highlights"]["color"])
node = _bs.newnode('text',
attrs={
'text': self.highlights[self.index],
'flatness': 1.0,
'h_align': 'center',
'v_attach':'bottom',
'scale':1,
'position':(0,138),
'color':color
})
self.delt = bs.timer(7,node.delete)
self.index = int((self.index+1)%len(self.highlights))
def left_watermark(self, text):
node = _bs.newnode('text',
attrs={
'text': text,
'flatness': 1.0,
'h_align': 'left',
'v_attach':'bottom',
'h_attach':'left',
'scale':0.7,
'position':(25,67),
'color':(0.7,0.7,0.7)
})
def nextGame(self,text):
node = _bs.newnode('text',
attrs={
'text':"Next : "+text,
'flatness':1.0,
'h_align':'right',
'v_attach':'bottom',
'h_attach':'right',
'scale':0.7,
'position':(-25,16),
'color':(0.5,0.5,0.5)
})
def season_reset(self,text):
node = _bs.newnode('text',
attrs={
'text':"Season ends in: "+str(text)+" days",
'flatness':1.0,
'h_align':'right',
'v_attach':'bottom',
'h_attach':'right',
'scale':0.5,
'position':(-25,34),
'color':(0.6,0.5,0.7)
})
def restart_msg(self):
if hasattr(_ba,'restart_scheduled'):
_babase.get_foreground_host_activity().restart_msg = _bs.newnode('text',
attrs={
'text':"Server going to restart after this series.",
'flatness':1.0,
'h_align':'right',
'v_attach':'bottom',
'h_attach':'right',
'scale':0.5,
'position':(-25,54),
'color':(1,0.5,0.7)
})
def top_message(self, text):
node = _bs.newnode('text',
attrs={
'text': text,
'flatness': 1.0,
'h_align': 'center',
'v_attach':'top',
'scale':0.7,
'position':(0,-70),
'color':(1,1,1)
})
def leaderBoard(self):
if len(mystats.top3Name) >2:
if setti["leaderboard"]["barsBehindName"]:
self.ss1=bs.newnode('image',attrs={'scale':(300,30),'texture':bs.gettexture('bar'),'position':(0,-80),'attach':'topRight','opacity':0.5,'color':(0.7,0.1,0)})
self.ss1=bs.newnode('image',attrs={'scale':(300,30),'texture':bs.gettexture('bar'),'position':(0,-115),'attach':'topRight','opacity':0.5,'color':(0.6,0.6,0.6)})
self.ss1=bs.newnode('image',attrs={'scale':(300,30),'texture':bs.gettexture('bar'),'position':(0,-150),'attach':'topRight','opacity':0.5,'color':(0.1,0.3,0.1)})
self.ss1a=bs.newnode('text',attrs={'text':"#1 "+mystats.top3Name[0][:10]+"...",'flatness':1.0,'h_align':'left','h_attach':'right','v_attach':'top','v_align':'center','position':(-140,-80),'scale':0.7,'color':(0.7,0.4,0.3)})
self.ss1a=bs.newnode('text',attrs={'text':"#2 "+mystats.top3Name[1][:10]+"...",'flatness':1.0,'h_align':'left','h_attach':'right','v_attach':'top','v_align':'center','position':(-140,-115),'scale':0.7,'color':(0.8,0.8,0.8)})
self.ss1a=bs.newnode('text',attrs={'text':"#3 "+mystats.top3Name[2][:10]+"...",'flatness':1.0,'h_align':'left','h_attach':'right','v_attach':'top','v_align':'center','position':(-140,-150),'scale':0.7,'color':(0.2,0.6,0.2)})

View file

@ -0,0 +1,134 @@
# Electronic Voting Machine (EVM) by -mr.smoothy
import _babaseimport ba
import babase.internal
import time
game_started_on = 0
vote_machine = {"end": {"last_vote_start_time": 0, "vote_duration": 50,
"min_game_duration_to_start_vote": 30, "voters": []},
"sm": {"last_vote_start_time": 0, "vote_duration": 50,
"min_game_duration_to_start_vote": 1, "voters": []},
"nv": {"last_vote_start_time": 0, "vote_duration": 50,
"min_game_duration_to_start_vote": 1, "voters": []},
"dv": {"last_vote_start_time": 0, "vote_duration": 50,
"min_game_duration_to_start_vote": 1, "voters": []}}
def vote(pb_id, client_id, vote_type):
global vote_machine
voters = vote_machine[vote_type]["voters"]
last_vote_start_time = vote_machine[vote_type]["last_vote_start_time"]
vote_duration = vote_machine[vote_type]["vote_duration"]
min_game_duration_to_start_vote = vote_machine[vote_type]["min_game_duration_to_start_vote"]
now = time.time()
if now > last_vote_start_time + vote_duration:
voters = []
vote_machine[vote_type]["last_vote_start_time"] = now
if now < game_started_on + min_game_duration_to_start_vote:
_bs.broadcastmessage("Seems game just started, Try again after some time", transient=True,
clients=[client_id])
return
if len(voters) == 0:
babase.internal.chatmessage(f"{vote_type} vote started")
# clean up voters list
active_players = []
for player in babase.internal.get_game_roster():
active_players.append(player['account_id'])
for voter in voters:
if voter not in active_players:
voters.remove(voter)
if pb_id not in voters:
voters.append(pb_id)
_bs.broadcastmessage(f'Thanks for vote , encourage other players to type {vote_type} too.', transient=True,
clients=[client_id])
if vote_type == 'end':
update_vote_text(max_votes_required(
len(active_players)) - len(voters))
else:
activity = _babase.get_foreground_host_activity()
if activity is not None:
with _babase.Context(activity):
_bs.broadcastmessage(f"{max_votes_required(len(active_players)) - len(voters)} votes required for {vote_type}",
image={"texture": bs.gettexture("achievementSharingIsCaring"),
"tint_texture": bs.gettexture("achievementSharingIsCaring"),
"tint_color": (0.5, 0.5, 0.5), "tint2_color": (0.7, 0.5, 0.9)},
top=True)
vote_machine[vote_type]["voters"] = voters
if len(voters) >= max_votes_required(len(active_players)):
babase.internal.chatmessage(f"{vote_type} vote succeed")
vote_machine[vote_type]["voters"] = []
if vote_type == "end":
try:
with _babase.Context(_babase.get_foreground_host_activity()):
_babase.get_foreground_host_activity().end_game()
except:
pass
elif vote_type == "nv":
_babase.chatmessage("/nv")
elif vote_type == "dv":
_babase.chatmessage("/dv")
elif vote_type == "sm":
_babase.chatmessage("/sm")
def reset_votes():
global vote_machine
for value in vote_machine.values():
value["voters"] = []
def max_votes_required(players):
if players == 2:
return 1
elif players == 3:
return 2
elif players == 4:
return 2
elif players == 5:
return 3
elif players == 6:
return 3
elif players == 7:
return 4
elif players == 8:
return 4
elif players == 10:
return 5
else:
return players - 5
def update_vote_text(votes_needed):
activity = _babase.get_foreground_host_activity()
try:
activity.end_vote_text.node.text = "{} more votes to end this map\ntype 'end' to vote".format(
votes_needed)
except:
with _babase.Context(_babase.get_foreground_host_activity()):
node = bs.NodeActor(bs.newnode('text',
attrs={
'v_attach': 'top',
'h_attach': 'center',
'h_align': 'center',
'color': (1, 1, 0.5, 1),
'flatness': 0.5,
'shadow': 0.5,
'position': (-200, -30),
'scale': 0.7,
'text': '{} more votes to end this map \n type \'end\' to vote'.format(
votes_needed)
})).autoretain()
activity.end_vote_text = node
bs.timer(20, remove_vote_text)
def remove_vote_text():
activity = _babase.get_foreground_host_activity()
if hasattr(activity, "end_vote_text") and activity.end_vote_text.node.exists():
activity.end_vote_text.node.delete()

View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

View file

@ -0,0 +1,34 @@
{
"ban": {
"ids": {
"pb-234": {
"till": "2023-06-07 21:59:20",
"reason": "auto ban for spam"
}
},
"ips": {
"19.168.0.0.1": {
"till": "2023-06-07 21:59:20",
"reason": "auto ban for spam"
}
},
"deviceids": {
"sdfdsfwr3": {
"till": "2023-06-07 21:59:20",
"reason": "auto ban for spam"
}
}
},
"muted-ids": {
"pb-IF4iU0QaEw==": {
"till": "2023-06-19 19:44:47",
"reason": "manually from website"
}
},
"kick-vote-disabled": {
"pb-JiNJARBaXEFBVF9HFkNXXF1EF0ZaRlZE": {
"till": "2023-06-12 19:37:48",
"reason": "manually from website"
}
}
}

View file

@ -0,0 +1,17 @@
{
"customeffects": {
"pb-IF4TVWwZUQ==": [
"spark"
],
"pb-smoothy-effect": [
"spark"
],
"pb-testingroles": [
"highlightshine"
]
},
"customtag": {
"pb-id5y54y54": "smoothy",
"pb-smoothybro": "they smoothy"
}
}

View file

@ -0,0 +1,17 @@
{
"customeffects": {
"pb-IF4TVWwZUQ==": [
"spark"
],
"pb-smoothy-effect": [
"spark"
],
"pb-testingroles": [
"highlightshine"
]
},
"customtag": {
"pb-id5y54y54": "smoothy",
"pb-smoothybro": "they smoothy"
}
}

737
dist/ba_root/mods/playersData/pdata.py vendored Normal file
View file

@ -0,0 +1,737 @@
"""Module to manage players data."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import shutil
import copy
from typing import TYPE_CHECKING
import time
import os
import _thread
import babase
from serverData import serverdata
from tools.file_handle import OpenJson
# pylint: disable=import-error
import json
import datetime
from tools.ServerUpdate import checkSpammer
import setting
from datetime import datetime, timedelta
if TYPE_CHECKING:
pass
settings = setting.get_settings_data()
PLAYERS_DATA_PATH = os.path.join(
babase.env()["python_directory_user"], "playersData" + os.sep
)
class CacheData: # pylint: disable=too-few-public-methods
"""Stores the cache data."""
roles: dict = {}
data: dict = {}
custom: dict = {}
profiles: dict = {}
whitelist: list[str] = []
blacklist: dict = {}
def get_info(account_id: str) -> dict | None:
"""Returns the information about player.
Parameters
----------
account_id : str
account_id of the client
Returns
-------
dict | None
information of client
"""
profiles = get_profiles()
if account_id in profiles:
return profiles[account_id]
return None
def get_profiles() -> dict:
"""Returns the profiles of all players.
Returns
-------
dict
profiles of the players
"""
if CacheData.profiles == {}:
try:
if os.stat(PLAYERS_DATA_PATH+"profiles.json").st_size > 1000000:
newpath = f'{PLAYERS_DATA_PATH}profiles-{str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))}.json'
shutil.copyfile(PLAYERS_DATA_PATH + "profiles.json", newpath)
profiles = {"pb-sdf": {}}
print("resetting profiles")
else:
f = open(PLAYERS_DATA_PATH + "profiles.json", "r")
profiles = json.load(f)
f.close()
print("loading old proiles.json")
CacheData.profiles = profiles
except Exception as e:
f = open(PLAYERS_DATA_PATH + "profiles.json.backup", "r")
profiles = json.load(f)
print(e)
print("exception happened , falling back to profiles.json.backup")
CacheData.profiles = profiles
f.close()
return profiles
else:
return CacheData.profiles
def get_profiles_archive_index():
return [x for x in os.listdir(PLAYERS_DATA_PATH) if x.startswith("profiles")]
def get_old_profiles(filename):
try:
f = open(PLAYERS_DATA_PATH + filename, "r")
profiles = json.load(f)
return profiles
except:
return {}
def get_blacklist() -> dict:
if CacheData.blacklist == {}:
try:
f = open(PLAYERS_DATA_PATH + "blacklist.json", "r")
CacheData.blacklist = json.load(f)
except:
print('error opening blacklist json')
return {
"ban": {
"ids": {},
"ips": {},
"deviceids": {}
},
"muted-ids": {},
"kick-vote-disabled": {}
}
return CacheData.blacklist
def update_blacklist():
with open(PLAYERS_DATA_PATH + "blacklist.json", "w") as f:
json.dump(CacheData.blacklist, f, indent=4)
def commit_profiles(data={}) -> None:
"""Commits the given profiles in the database.
Parameters
----------
profiles of all players
"""
# with OpenJson(PLAYERS_DATA_PATH + "profiles.json") as profiles_file:
# profiles_file.dump(CacheData.profiles, indent=4)
def get_detailed_info(pbid):
main_account = get_info(pbid)
if main_account == None:
return "No info"
linked_accounts = ' '.join(main_account["display_string"])
ip = main_account["lastIP"]
deviceid = main_account["deviceUUID"]
otheraccounts = ""
dob = main_account["accountAge"]
profiles = get_profiles()
for key, value in profiles.items():
if ("lastIP" in value and value["lastIP"] == ip) or ("deviceUUID" in value and value["deviceUUID"] == deviceid):
otheraccounts += ' '.join(value["display_string"])
return f"Accounts:{linked_accounts} \n other accounts {otheraccounts} \n created on {dob}"
def add_profile(
account_id: str,
display_string: str,
current_name: str,
account_age: int,
) -> None:
"""Adds the profile in database.
Parameters
----------
account_id : str
account id of the client
display_string : str
display string of the client
current_name : str
name of the client
account_age : int
account_age of the account
"""
profiles = get_profiles()
profiles[account_id] = {
"display_string": display_string,
"profiles": [],
"name": current_name,
"accountAge": account_age,
"registerOn": time.time(),
"spamCount": 0,
"lastSpam": time.time(),
"totaltimeplayer": 0,
}
CacheData.profiles = profiles
serverdata.clients[account_id] = profiles[account_id]
serverdata.clients[account_id]["warnCount"] = 0
serverdata.clients[account_id]["lastWarned"] = time.time()
serverdata.clients[account_id]["verified"] = False
serverdata.clients[account_id]["rejoincount"] = 1
serverdata.clients[account_id]["lastJoin"] = time.time()
cid = 113
for ros in bs.get_game_roster():
if ros['account_id'] == account_id:
cid = ros['client_id']
ip = _babase.get_client_ip(cid)
serverdata.clients[account_id]["lastIP"] = ip
serverdata.recents.append(
{"client_id": cid, "deviceId": display_string, "pbid": account_id})
serverdata.recents = serverdata.recents[-20:]
device_id = _babase.get_client_public_device_uuid(cid)
if (device_id == None):
device_id = _babase.get_client_device_uuid(cid)
checkSpammer({'id': account_id, 'display': display_string,
'ip': ip, 'device': device_id})
if device_id in get_blacklist()["ban"]["deviceids"] or account_id in get_blacklist()["ban"]["ids"]:
bs.disconnect_client(cid)
serverdata.clients[account_id]["deviceUUID"] = device_id
def update_display_string(account_id: str, display_string: str) -> None:
"""Updates the display string of the account.
Parameters
----------
account_id : str
account id of the client
display_string : str
new display string to be updated
"""
profiles = get_profiles()
if account_id in profiles:
profiles[account_id]["display_string"] = display_string
CacheData.profiles = profiles
commit_profiles()
def update_profile(
account_id: str,
display_string: str = None,
allprofiles: list[str] = None,
name: str = None,
) -> None:
"""Updates the profile of client.
Parameters
----------
account_id : str
account id of the client
display_string : str, optional
display string of the account, by default None
allprofiles : list[str], optional
all profiles of the client, by default None
name : str, optional
name to be updated, by default None
"""
profiles = get_profiles()
if profiles is None:
return
if account_id in profiles and display_string is not None:
if display_string not in profiles[account_id]["display_string"]:
profiles[account_id]["display_string"].append(display_string)
if allprofiles is not None:
for profile in allprofiles:
if profile not in profiles[account_id]["profiles"]:
profiles[account_id]["profiles"].append(profile)
if name is not None:
profiles[account_id]["name"] = name
CacheData.profiles = profiles
commit_profiles()
def ban_player(account_id: str, duration_in_days: float, reason: str) -> None:
"""Bans the player.
Parameters
----------
account_id : str
account id of the player to be banned
"""
current_profiles = get_profiles()
ip = ""
device_id = ""
if account_id in current_profiles:
ip = current_profiles[account_id]["lastIP"]
device_id = current_profiles[account_id]["deviceUUID"]
ban_time = datetime.now() + timedelta(days=duration_in_days)
CacheData.blacklist["ban"]["ips"][ip] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": f'linked with account {account_id}'}
CacheData.blacklist["ban"]["ids"][account_id] = {
"till": ban_time.strftime("%Y-%m-%d %H:%M:%S"), "reason": reason}
CacheData.blacklist["ban"]["deviceids"][device_id] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": f'linked with account {account_id}'}
_thread.start_new_thread(update_blacklist, ())
def unban_player(account_id):
current_profiles = get_profiles()
ip = ""
device_id = ""
if account_id in current_profiles:
ip = current_profiles[account_id]["lastIP"]
device_id = current_profiles[account_id]["deviceUUID"]
CacheData.blacklist["ban"]["ips"].pop(ip, None)
CacheData.blacklist["ban"]["deviceids"].pop(device_id, None)
CacheData.blacklist["ban"]["ids"].pop(account_id, None)
_thread.start_new_thread(update_blacklist, ())
def disable_kick_vote(account_id, duration, reason):
ban_time = datetime.now() + timedelta(days=duration)
CacheData.blacklist["kick-vote-disabled"][account_id] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": reason}
_thread.start_new_thread(update_blacklist, ())
def enable_kick_vote(account_id):
CacheData.blacklist["kick-vote-disabled"].pop(account_id, None)
_thread.start_new_thread(update_blacklist, ())
def mute(account_id: str, duration_in_days: float, reason: str) -> None:
"""Mutes the player.
Parameters
----------
account_id : str
acccount id of the player to be muted
"""
ban_time = datetime.now() + timedelta(days=duration_in_days)
CacheData.blacklist["muted-ids"][account_id] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": reason}
_thread.start_new_thread(update_blacklist, ())
def unmute(account_id: str) -> None:
"""Unmutes the player.
Parameters
----------
account_id : str
acccount id of the player to be unmuted
"""
CacheData.blacklist["muted-ids"].pop(account_id, None)
_thread.start_new_thread(update_blacklist, ())
def update_spam(account_id: str, spam_count: int, last_spam: float) -> None:
"""Updates the spam time and count.
Parameters
----------
account_id : str
account id of the client
spam_count : int
spam count to be added
last_spam : float
last spam time
"""
profiles = get_profiles()
if account_id in profiles:
profiles[account_id]["spamCount"] = spam_count
profiles[account_id]["lastSpam"] = last_spam
CacheData.profiles = profiles
commit_profiles(profiles)
def commit_roles(data: dict) -> None:
"""Commits the roles in database.
Parameters
----------
data : dict
data to be commited
"""
if not data:
return
# with OpenJson(PLAYERS_DATA_PATH + "roles.json") as roles_file:
# roles_file.format(data)
def get_roles() -> dict:
"""Returns the roles.
Returns
-------
dict
roles
"""
if CacheData.roles == {}:
try:
f = open(PLAYERS_DATA_PATH + "roles.json", "r")
roles = json.load(f)
f.close()
CacheData.roles = roles
except Exception as e:
print(e)
f = open(PLAYERS_DATA_PATH + "roles.json.backup", "r")
roles = json.load(f)
f.close()
CacheData.roles = roles
return CacheData.roles
def create_role(role: str) -> None:
"""Ceates the role.
Parameters
----------
role : str
role to be created
"""
roles = get_roles()
if role in roles:
return
roles[role] = {
"tag": role,
"tagcolor": [1, 1, 1],
"commands": [],
"ids": [],
}
CacheData.roles = roles
commit_roles(roles)
def add_player_role(role: str, account_id: str) -> None:
"""Adds the player to the role.
Parameters
----------
role : str
role to be added
account_id : str
account id of the client
"""
roles = get_roles()
if role in roles:
if account_id not in roles[role]["ids"]:
roles[role]["ids"].append(account_id)
CacheData.roles = roles
commit_roles(roles)
else:
print("no role such")
def remove_player_role(role: str, account_id: str) -> str:
"""Removes the role from player.
Parameters
----------
role : str
role to br removed
account_id : str
account id of the client
Returns
-------
str
status of the removing role
"""
roles = get_roles()
if role in roles:
roles[role]["ids"].remove(account_id)
CacheData.roles = roles
commit_roles(roles)
return "removed from " + role
return "role not exists"
def add_command_role(role: str, command: str) -> str:
"""Adds the command to the role.
Parameters
----------
role : str
role to add the command
command : str
command to be added
Returns
-------
str
status of the adding command
"""
roles = get_roles()
if role in roles:
if command not in roles[role]["commands"]:
roles[role]["commands"].append(command)
CacheData.roles = roles
commit_roles(roles)
return "command added to " + role
return "command not exists"
def remove_command_role(role: str, command: str) -> str:
"""Removes the command from the role.
Parameters
----------
role : str
role to remove command from
command : str
command to be removed
Returns
-------
str
status of the removing command
"""
roles = get_roles()
if role in roles:
if command in roles[role]["commands"]:
roles[role]["commands"].remove(command)
CacheData.roles = roles
commit_roles(roles)
return "command added to " + role
return "command not exists"
def change_role_tag(role: str, tag: str) -> str:
"""Changes the tag of the role.
Parameters
----------
role : str
role to chnage the tag
tag : str
tag to be added
Returns
-------
str
status of the adding tag
"""
roles = get_roles()
if role in roles:
roles[role]["tag"] = tag
CacheData.roles = roles
commit_roles(roles)
return "tag changed"
return "role not exists"
def get_player_roles(account_id: str) -> list[str]:
"""Returns the avalibe roles of the account.
Parameters
----------
account_id : str
account id of the client
Returns
-------
list[str]
list of the roles
"""
roles = get_roles()
have_roles = []
for role in roles:
if account_id in roles[role]["ids"]:
have_roles.append(role)
return have_roles
def get_custom() -> dict:
"""Returns the custom effects.
Returns
-------
dict
custom effects
"""
if CacheData.custom == {}:
try:
f = open(PLAYERS_DATA_PATH + "custom.json", "r")
custom = json.load(f)
f.close()
CacheData.custom = custom
except:
f = open(PLAYERS_DATA_PATH + "custom.json.backup", "r")
custom = json.load(f)
f.close()
CacheData.custom = custom
for account_id in custom["customeffects"]:
custom["customeffects"][account_id] = [custom["customeffects"][account_id]] if type(
custom["customeffects"][account_id]) is str else custom["customeffects"][account_id]
return CacheData.custom
def set_effect(effect: str, account_id: str) -> None:
"""Sets the costum effect for the player.
Parameters
----------
effect : str
effect to be added to the player
accout_id : str
account id of the client
"""
custom = get_custom()
if account_id in custom["customeffects"]:
effects = [custom["customeffects"][account_id]] if type(
custom["customeffects"][account_id]) is str else custom["customeffects"][account_id]
effects.append(effect)
custom["customeffects"][account_id] = effects
else:
custom["customeffects"][account_id] = [effect]
CacheData.custom = custom
commit_c()
def set_tag(tag: str, account_id: str) -> None:
"""Sets the custom tag to the player.
Parameters
----------
tag : str
tag to be added to the player
account_id : str
account id of the client
"""
custom = get_custom()
custom["customtag"][account_id] = tag
CacheData.custom = custom
commit_c()
def update_roles(roles):
CacheData.roles = roles
def get_custom_perks():
return CacheData.custom
def update_custom_perks(custom):
CacheData.custom = custom
def remove_effect(account_id: str) -> None:
"""Removes the effect from player.
Parameters
----------
account_id : str
account id of the client
"""
custom = get_custom()
custom["customeffects"].pop(account_id)
CacheData.custom = custom
def remove_tag(account_id: str) -> None:
"""Removes the tag from the player
Parameters
----------
account_id : str
account id of the client
"""
custom = get_custom()
custom["customtag"].pop(account_id)
CacheData.custom = custom
def commit_c():
"""Commits the custom data into the custom.json."""
# with OpenJson(PLAYERS_DATA_PATH + "custom.json") as custom_file:
# custom_file.dump(CacheData.custom, indent=4)
def update_toppers(topper_list: list[str]) -> None:
"""Updates the topper list into top5 role.
Parameters
----------
topper_list : list[str]
list of the topper players
"""
roles = get_roles()
if "top5" not in roles:
create_role("top5")
CacheData.roles["top5"]["ids"] = topper_list
commit_roles(roles)
def load_white_list() -> None:
"""Loads the whitelist."""
with OpenJson(PLAYERS_DATA_PATH + "whitelist.json") as whitelist_file:
data = whitelist_file.load()
for account_id in data:
CacheData.whitelist.append(account_id)
def load_cache():
""" to be called on server boot"""
get_profiles()
get_custom()
get_roles()
def dump_cache():
if CacheData.profiles != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "profiles.json",
PLAYERS_DATA_PATH + "profiles.json.backup")
profiles = copy.deepcopy(CacheData.profiles)
with open(PLAYERS_DATA_PATH + "profiles.json", "w") as f:
json.dump(profiles, f, indent=4)
if CacheData.roles != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "roles.json",
PLAYERS_DATA_PATH + "roles.json.backup")
roles = copy.deepcopy(CacheData.roles)
with open(PLAYERS_DATA_PATH + "roles.json", "w") as f:
json.dump(roles, f, indent=4)
if CacheData.custom != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "custom.json",
PLAYERS_DATA_PATH + "custom.json.backup")
custom = copy.deepcopy(CacheData.custom)
with open(PLAYERS_DATA_PATH + "custom.json", "w") as f:
json.dump(custom, f, indent=4)
time.sleep(60)
dump_cache()

22973
dist/ba_root/mods/playersData/profiles.json vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,93 @@
{
"owner": {
"tag": "\\cowner\\c",
"tagcolor": [
1,
0.6,
0.4
],
"commands": [
"ALL"
],
"ids": [
"pb-IF48VWkBFQ",
"pb-JiNJARBaXEFBVF9HFkNXXF1EF0ZaRlZE",
"pb-IF4TVWwZUQ==",
"pb-IF4SVW9dEg==",
"pb-IF5XUm9eAg=="
]
},
"admin": {
"tag": "\ue043admin\ue043",
"tagcolor": [
1,
1,
1
],
"commands": [
"createrole"
],
"ids": [
"pb-IF4TVWwZUQ=="
]
},
"vip": {
"tag": "vip",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": []
},
"smoothy": {
"tag": "smoothy",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": []
},
"pros": {
"tag": "pros",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": []
},
"top5": {
"tag": "top5",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": [
"pb-IF4VAk4a",
"pb-IF4RU2ECAg=="
]
},
"bypass-warn": {
"tag": "",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": [
"pb-IF5XUm9eAg==",
"pb-IF43VUwlAg==",
"pb-IF4iVUc5Cg==",
"pb-IF4vNnMJ",
"pb-IF4TVWwZUQ=="
]
}
}

View file

@ -0,0 +1,93 @@
{
"owner": {
"tag": "\\cowner\\c",
"tagcolor": [
1,
0.6,
0.4
],
"commands": [
"ALL"
],
"ids": [
"pb-IF48VWkBFQ",
"pb-JiNJARBaXEFBVF9HFkNXXF1EF0ZaRlZE",
"pb-IF4TVWwZUQ==",
"pb-IF4SVW9dEg==",
"pb-IF5XUm9eAg=="
]
},
"admin": {
"tag": "\ue043admin\ue043",
"tagcolor": [
1,
1,
1
],
"commands": [
"createrole"
],
"ids": [
"pb-IF4TVWwZUQ=="
]
},
"vip": {
"tag": "vip",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": []
},
"smoothy": {
"tag": "smoothy",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": []
},
"pros": {
"tag": "pros",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": []
},
"top5": {
"tag": "top5",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": [
"pb-IF4VAk4a",
"pb-IF4RU2ECAg=="
]
},
"bypass-warn": {
"tag": "",
"tagcolor": [
1,
1,
1
],
"commands": [],
"ids": [
"pb-IF5XUm9eAg==",
"pb-IF43VUwlAg==",
"pb-IF4iVUc5Cg==",
"pb-IF4vNnMJ",
"pb-IF4TVWwZUQ=="
]
}
}

View file

@ -0,0 +1,31 @@
{
"pb-IF41U2scIg==": {
"subscribers": [
"jrz6Rg"
]
},
"pb-IF4IVFkKNA==": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue063ATTITUDEB2"
},
"pb-IF4CKhYn": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue032Rico Un\u2122\u00a9\u00ae"
},
"pb-IF4jF1NY": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue01eHoemaster"
},
"pb-IF4wVVk8Jg==": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue063Homulilly"
}
}

View file

@ -0,0 +1,31 @@
{
"pb-IF41U2scIg==": {
"subscribers": [
"jrz6Rg"
]
},
"pb-IF4IVFkKNA==": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue063ATTITUDEB2"
},
"pb-IF4CKhYn": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue032Rico Un\u2122\u00a9\u00ae"
},
"pb-IF4jF1NY": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue01eHoemaster"
},
"pb-IF4wVVk8Jg==": {
"subscribers": [
"jrz6Rg"
],
"name": "\ue063Homulilly"
}
}

View file

@ -0,0 +1,10 @@
{
"jrz6Rg": {
"endpoint": "https://wns2-pn1p.notify.windows.com/w/?token=BQYAAAACsvYS2nXaGg2g2jLcjLjp4bwznS9iOrr3BCN%2bvTCn%2fHqOyzKJSI3IqeLRM0klolKhnswEn0YOYDgvEnAW0bJRJsSW8nmg0y8RZoY5%2bGnVGqv8VXoGafhDTJOb5%2bKfgRRz01qtOW2qQogWRj3dRsVw%2fEw6dliZOl3SC3eBfgOg5dIkiIFSvl%2bVDnbCzW%2bESVJe9aYuffk%2b1XvvTtcxpoUzQMvcoa7fXhrcJtb%2foZWLGj3sWOrGA3D1gnMuuoSnRqH8qsHTqP2BkFiXm2E860qxocYKDVWNZkkbWGw1PwNsZe3U9qO1fyDkmWpdljZf7hFZ9eS9bauneNBqKw7H6IGl",
"expirationTime": null,
"keys": {
"p256dh": "BOG4r91J0rFq8YveeCMv4E9zAIOpqyAadsqAQ0R0yWSKVCUE06gMGB52ofsCNEhtBH3_lYs9Uf0ecT-iVGFeziA",
"auth": "yNEWaUfEYLWZlyBNdZIDrg"
}
}
}

View file

@ -0,0 +1,10 @@
{
"jrz6Rg": {
"endpoint": "https://wns2-pn1p.notify.windows.com/w/?token=BQYAAAACsvYS2nXaGg2g2jLcjLjp4bwznS9iOrr3BCN%2bvTCn%2fHqOyzKJSI3IqeLRM0klolKhnswEn0YOYDgvEnAW0bJRJsSW8nmg0y8RZoY5%2bGnVGqv8VXoGafhDTJOb5%2bKfgRRz01qtOW2qQogWRj3dRsVw%2fEw6dliZOl3SC3eBfgOg5dIkiIFSvl%2bVDnbCzW%2bESVJe9aYuffk%2b1XvvTtcxpoUzQMvcoa7fXhrcJtb%2foZWLGj3sWOrGA3D1gnMuuoSnRqH8qsHTqP2BkFiXm2E860qxocYKDVWNZkkbWGw1PwNsZe3U9qO1fyDkmWpdljZf7hFZ9eS9bauneNBqKw7H6IGl",
"expirationTime": null,
"keys": {
"p256dh": "BOG4r91J0rFq8YveeCMv4E9zAIOpqyAadsqAQ0R0yWSKVCUE06gMGB52ofsCNEhtBH3_lYs9Uf0ecT-iVGFeziA",
"auth": "yNEWaUfEYLWZlyBNdZIDrg"
}
}
}

View file

@ -0,0 +1,8 @@
{
"pb-IF4VAk4a": [
"\ue030Server127089"
],
"pb-IF5VVxI5Fg==": [
"\ue020id just to remeber pbid here"
]
}

View file

@ -0,0 +1,357 @@
# ba_meta require api 8
'''
Character Chooser by Mr.Smoothy
This plugin will let you choose your character from lobby.
Install this plugin on your Phone/PC or on Server
If installed on server :- this will also let players choose server specific custom characters . so no more sharing of character file with all players,
just install this plugin on server ...and players can pick character from lobby .
Use:-
> select your profile (focus on color and name)
> press ready (punch)
> now use UP/DOWN buttons to scroll character list
> Press ready again (punch) to join the game
> or press Bomb button to go back to profile choosing menu
> END
Watch : https://www.youtube.com/watch?v=hNmv2l-NahE
Join : https://discord.gg/ucyaesh
Contact : discord mr.smoothy#5824
Share this plugin with your server owner /admins to use it online
:)
'''
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs,_ba
import babase.internal
from bascenev1lib.actor.playerspaz import PlayerSpaz
from babase._error import print_exception, print_error, NotFoundError
from babase._gameutils import animate, animate_array
from babase._language import Lstr
from babase._generated.enums import SpecialChar, InputType
from babase._profile import get_player_profile_colors
if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Tuple, Union, Sequence, Optional
import weakref
import os,json
from ba import _lobby
from bascenev1lib.actor.spazappearance import *
from babase._lobby import ChangeMessage
from babase._lobby import PlayerReadyMessage
def __init__(self, vpos: float, sessionplayer: _bs.SessionPlayer,
lobby: 'Lobby') -> None:
self._deek_sound = _bs.getsound('deek')
self._click_sound = _bs.getsound('click01')
self._punchsound = _bs.getsound('punch01')
self._swish_sound = _bs.getsound('punchSwish')
self._errorsound = _bs.getsound('error')
self._mask_texture = _bs.gettexture('characterIconMask')
self._vpos = vpos
self._lobby = weakref.ref(lobby)
self._sessionplayer = sessionplayer
self._inited = False
self._dead = False
self._text_node: Optional[bs.Node] = None
self._profilename = ''
self._profilenames: List[str] = []
self._ready: bool = False
self._character_names: List[str] = []
self._last_change: Sequence[Union[float, int]] = (0, 0)
self._profiles: Dict[str, Dict[str, Any]] = {}
app = _babase.app
self.bakwas_chars=["Lee","Todd McBurton","Zola","Butch","Witch","warrior","Middle-Man","Alien","OldLady","Gladiator","Wrestler","Gretel","Robot"]
# Load available player profiles either from the local config or
# from the remote device.
self.reload_profiles()
for name in _babase.app.spaz_appearances:
if name not in self._character_names and name not in self.bakwas_chars:
self._character_names.append(name)
# Note: this is just our local index out of available teams; *not*
# the team-id!
self._selected_team_index: int = self.lobby.next_add_team
# Store a persistent random character index and colors; we'll use this
# for the '_random' profile. Let's use their input_device id to seed
# it. This will give a persistent character for them between games
# and will distribute characters nicely if everyone is random.
self._random_color, self._random_highlight = (
get_player_profile_colors(None))
# To calc our random character we pick a random one out of our
# unlocked list and then locate that character's index in the full
# list.
char_index_offset = app.lobby_random_char_index_offset
self._random_character_index = (
(sessionplayer.inputdevice.id + char_index_offset) %
len(self._character_names))
# Attempt to set an initial profile based on what was used previously
# for this input-device, etc.
self._profileindex = self._select_initial_profile()
self._profilename = self._profilenames[self._profileindex]
self._text_node = _bs.newnode('text',
delegate=self,
attrs={
'position': (-100, self._vpos),
'maxwidth': 190,
'shadow': 0.5,
'vr_depth': -20,
'h_align': 'left',
'v_align': 'center',
'v_attach': 'top'
})
animate(self._text_node, 'scale', {0: 0, 0.1: 1.0})
self.icon = _bs.newnode('image',
owner=self._text_node,
attrs={
'position': (-130, self._vpos + 20),
'mask_texture': self._mask_texture,
'vr_depth': -10,
'attach': 'topCenter'
})
animate_array(self.icon, 'scale', 2, {0: (0, 0), 0.1: (45, 45)})
# Set our initial name to '<choosing player>' in case anyone asks.
self._sessionplayer.setname(
Lstr(resource='choosingPlayerText').evaluate(), real=False)
# Init these to our rando but they should get switched to the
# selected profile (if any) right after.
self._character_index = self._random_character_index
self._color = self._random_color
self._highlight = self._random_highlight
self.characterchooser=False
self.update_from_profile()
self.update_position()
self._inited = True
self._set_ready(False)
def _set_ready(self, ready: bool) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.profile import browser as pbrowser
from babase._general import Call
profilename = self._profilenames[self._profileindex]
# Handle '_edit' as a special case.
if profilename == '_edit' and ready:
with _babase.Context('ui'):
pbrowser.ProfileBrowserWindow(in_main_menu=False)
# Give their input-device UI ownership too
# (prevent someone else from snatching it in crowded games)
babase.internal.set_ui_input_device(self._sessionplayer.inputdevice)
return
if ready==False:
self._sessionplayer.assigninput(
InputType.LEFT_PRESS,
Call(self.handlemessage, ChangeMessage('team', -1)))
self._sessionplayer.assigninput(
InputType.RIGHT_PRESS,
Call(self.handlemessage, ChangeMessage('team', 1)))
self._sessionplayer.assigninput(
InputType.BOMB_PRESS,
Call(self.handlemessage, ChangeMessage('character', 1)))
self._sessionplayer.assigninput(
InputType.UP_PRESS,
Call(self.handlemessage, ChangeMessage('profileindex', -1)))
self._sessionplayer.assigninput(
InputType.DOWN_PRESS,
Call(self.handlemessage, ChangeMessage('profileindex', 1)))
self._sessionplayer.assigninput(
(InputType.JUMP_PRESS, InputType.PICK_UP_PRESS,
InputType.PUNCH_PRESS),
Call(self.handlemessage, ChangeMessage('ready', 1)))
self._ready = False
self._update_text()
self._sessionplayer.setname('untitled', real=False)
elif ready == True:
self.characterchooser=True
self._sessionplayer.assigninput(
(InputType.LEFT_PRESS, InputType.RIGHT_PRESS,
InputType.UP_PRESS, InputType.DOWN_PRESS,
InputType.JUMP_PRESS, InputType.BOMB_PRESS,
InputType.PICK_UP_PRESS), self._do_nothing)
self._sessionplayer.assigninput(
(InputType.UP_PRESS),Call(self.handlemessage,ChangeMessage('characterchooser',-1)))
self._sessionplayer.assigninput(
(InputType.DOWN_PRESS),Call(self.handlemessage,ChangeMessage('characterchooser',1)))
self._sessionplayer.assigninput(
(InputType.BOMB_PRESS),Call(self.handlemessage,ChangeMessage('ready',0)))
self._sessionplayer.assigninput(
(InputType.JUMP_PRESS,InputType.PICK_UP_PRESS, InputType.PUNCH_PRESS),
Call(self.handlemessage, ChangeMessage('ready', 2)))
# Store the last profile picked by this input for reuse.
input_device = self._sessionplayer.inputdevice
name = input_device.name
unique_id = input_device.unique_identifier
device_profiles = _babase.app.config.setdefault(
'Default Player Profiles', {})
# Make an exception if we have no custom profiles and are set
# to random; in that case we'll want to start picking up custom
# profiles if/when one is made so keep our setting cleared.
special = ('_random', '_edit', '__account__')
have_custom_profiles = any(p not in special
for p in self._profiles)
profilekey = name + ' ' + unique_id
if profilename == '_random' and not have_custom_profiles:
if profilekey in device_profiles:
del device_profiles[profilekey]
else:
device_profiles[profilekey] = profilename
_babase.app.config.commit()
# Set this player's short and full name.
self._sessionplayer.setname(self._getname(),
self._getname(full=True),
real=True)
self._ready = True
self._update_text()
else:
# Inform the session that this player is ready.
_babase.getsession().handlemessage(PlayerReadyMessage(self))
def handlemessage(self, msg: Any) -> Any:
"""Standard generic message handler."""
if isinstance(msg, ChangeMessage):
self._handle_repeat_message_attack()
# If we've been removed from the lobby, ignore this stuff.
if self._dead:
print_error('chooser got ChangeMessage after dying')
return
if not self._text_node:
print_error('got ChangeMessage after nodes died')
return
if msg.what=='characterchooser':
_self._click_sound.play()
# update our index in our local list of characters
self._character_index = ((self._character_index + msg.value) %
len(self._character_names))
self._update_text()
self._update_icon()
if msg.what == 'team':
sessionteams = self.lobby.sessionteams
if len(sessionteams) > 1:
_self._swish_sound.play()
self._selected_team_index = (
(self._selected_team_index + msg.value) %
len(sessionteams))
self._update_text()
self.update_position()
self._update_icon()
elif msg.what == 'profileindex':
if len(self._profilenames) == 1:
# This should be pretty hard to hit now with
# automatic local accounts.
__bs.getsound('error').play()
else:
# Pick the next player profile and assign our name
# and character based on that.
_self._deek_sound.play()
self._profileindex = ((self._profileindex + msg.value) %
len(self._profilenames))
self.update_from_profile()
elif msg.what == 'character':
_self._click_sound.play()
self.characterchooser=True
# update our index in our local list of characters
self._character_index = ((self._character_index + msg.value) %
len(self._character_names))
self._update_text()
self._update_icon()
elif msg.what == 'ready':
self._handle_ready_msg(msg.value)
def _update_text(self) -> None:
assert self._text_node is not None
if self._ready:
# Once we're ready, we've saved the name, so lets ask the system
# for it so we get appended numbers and stuff.
text = Lstr(value=self._sessionplayer.getname(full=True))
if self.characterchooser:
text = Lstr(value='${A}\n${B}',
subs=[('${A}', text),
('${B}', Lstr(value=""+self._character_names[self._character_index]))])
self._text_node.scale=0.8
else:
text = Lstr(value='${A} (${B})',
subs=[('${A}', text),
('${B}', Lstr(resource='readyText'))])
else:
text = Lstr(value=self._getname(full=True))
self._text_node.scale=1.0
can_switch_teams = len(self.lobby.sessionteams) > 1
# Flash as we're coming in.
fin_color = _babase.safecolor(self.get_color()) + (1, )
if not self._inited:
animate_array(self._text_node, 'color', 4, {
0.15: fin_color,
0.25: (2, 2, 2, 1),
0.35: fin_color
})
else:
# Blend if we're in teams mode; switch instantly otherwise.
if can_switch_teams:
animate_array(self._text_node, 'color', 4, {
0: self._text_node.color,
0.1: fin_color
})
else:
self._text_node.color = fin_color
self._text_node.text = text
def enable() -> None:
_lobby.Chooser.__init__=__init__
_lobby.Chooser._set_ready=_set_ready
_lobby.Chooser._update_text=_update_text
_lobby.Chooser.handlemessage=handlemessage

25
dist/ba_root/mods/plugins/__init__.py vendored Normal file
View file

@ -0,0 +1,25 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
if TYPE_CHECKING:
pass
# ba_meta export plugin
class Init(babase.Plugin): # pylint: disable=too-few-public-methods
"""Initializes all of the plugins in the directory."""

550
dist/ba_root/mods/plugins/auto_stunt.py vendored Normal file
View file

@ -0,0 +1,550 @@
# ba_meta require api 8
# AutoStunt mod by - Mr.Smoothy x Rikko
# https://discord.gg/ucyaesh
# https://bombsquad.ga
# Dont modify redistribute this plugin , if want to use features of this plugin in your mod write logic in seprate file
# and import this as module.
# If want to contribute in this original module, raise PR on github https://github.com/bombsquad-community/plugin-manager
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
import bascenev1lib
from bascenev1lib.actor.text import Text
from bascenev1lib.actor.image import Image
from bascenev1lib.actor import spaz
from bascenev1lib.actor import playerspaz
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.actor.spazfactory import SpazFactory
from bascenev1lib.game.elimination import EliminationGame
import math
import json
import os
from typing import Optional
CONTROLS_CENTER = (0, 0)
CONTROLS_SCALE = 1
BASE_STUNTS_DIRECTORY = os.path.join(_babase.env()["python_directory_user"], "CustomStunts")
PLAYERS_STUNT_INFO = {}
STUNT_CACHE = {}
original_on_begin = bs._activity.Activity.on_begin
original_chatmessage = bs.chatmessage
class ControlsUI:
def on_jump_press(activity):
activity._jump_image.node.color = list(
channel * 2 for channel in activity._jump_image.node.color[:3]) + [1]
def on_jump_release(activity):
activity._jump_image.node.color = list(
channel * 0.5 for channel in activity._jump_image.node.color[:3]) + [1]
def on_pickup_press(activity):
activity._pickup_image.node.color = list(
channel * 2 for channel in activity._pickup_image.node.color[:3]) + [1]
def on_pickup_release(activity):
activity._pickup_image.node.color = list(
channel * 0.5 for channel in activity._pickup_image.node.color[:3]) + [1]
def on_punch_press(activity):
activity._punch_image.node.color = list(
channel * 2 for channel in activity._punch_image.node.color[:3]) + [1]
def on_punch_release(activity):
activity._punch_image.node.color = list(
channel * 0.5 for channel in activity._punch_image.node.color[:3]) + [1]
def on_bomb_press(activity):
activity._bomb_image.node.color = list(
channel * 2 for channel in activity._bomb_image.node.color[:3]) + [1]
def on_bomb_release(activity):
activity._bomb_image.node.color = list(
channel * 0.5 for channel in activity._bomb_image.node.color[:3]) + [1]
def on_move_ud(activity, value):
activity.set_stick_image_position(activity, x=activity.stick_image_position_x, y=value)
def on_move_lr(activity, value):
activity.set_stick_image_position(activity, x=value, y=activity.stick_image_position_y)
def display(activity):
activity._jump_image.node.color = list(activity._jump_image.node.color[:3]) + [1]
activity._pickup_image.node.color = list(activity._pickup_image.node.color[:3]) + [1]
activity._punch_image.node.color = list(activity._punch_image.node.color[:3]) + [1]
activity._bomb_image.node.color = list(activity._bomb_image.node.color[:3]) + [1]
activity._stick_base_image.opacity = 1.0
activity._stick_nub_image.opacity = 1.0
def hide(activity):
activity._jump_image.node.color = list(activity._jump_image.node.color[:3]) + [0]
activity._pickup_image.node.color = list(activity._pickup_image.node.color[:3]) + [0]
activity._punch_image.node.color = list(activity._punch_image.node.color[:3]) + [0]
activity._bomb_image.node.color = list(activity._bomb_image.node.color[:3]) + [0]
activity._stick_base_image.opacity = 0.0
activity._stick_nub_image.opacity = 0.0
CONTROLS_UI_MAP = {
"JUMP_PRESS": ControlsUI.on_jump_press,
"JUMP_RELEASE": ControlsUI.on_jump_release,
"PICKUP_PRESS": ControlsUI.on_pickup_press,
"PICKUP_RELEASE": ControlsUI.on_pickup_release,
"PUNCH_PRESS": ControlsUI.on_punch_press,
"PUNCH_RELEASE": ControlsUI.on_punch_release,
"BOMB_PRESS": ControlsUI.on_bomb_press,
"BOMB_RELEASE": ControlsUI.on_bomb_release,
"UP_DOWN": ControlsUI.on_move_ud,
"LEFT_RIGHT": ControlsUI.on_move_lr
}
class NewSpaz(bascenev1lib.actor.spaz.Spaz):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.move_map = {
"UP_DOWN": self.on_move_up_down,
"LEFT_RIGHT": self.on_move_left_right,
"HOLD_POSITION": self.on_hold_position_press,
"HOLD_RELEASE": self.on_hold_position_release,
"JUMP_PRESS": self.on_jump_press,
"JUMP_RELEASE": self.on_jump_release,
"PICKUP_PRESS": self.on_pickup_press,
"PICKUP_RELEASE": self.on_pickup_release,
"PUNCH_PRESS": self.on_punch_press,
"PUNCH_RELEASE": self.on_punch_release,
"BOMB_PRESS": self.on_bomb_press,
"BOMB_RELEASE": self.on_bomb_release,
"RUN": self.on_run,
}
class NewPlayerSpaz(bascenev1lib.actor.playerspaz.PlayerSpaz):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.move_map = {
"UP_DOWN": self.on_move_up_down,
"LEFT_RIGHT": self.on_move_left_right,
"HOLD_POSITION": self.on_hold_position_press,
"HOLD_RELEASE": self.on_hold_position_release,
"JUMP_PRESS": self.on_jump_press,
"JUMP_RELEASE": self.on_jump_release,
"PICKUP_PRESS": self.on_pickup_press,
"PICKUP_RELEASE": self.on_pickup_release,
"PUNCH_PRESS": self.on_punch_press,
"PUNCH_RELEASE": self.on_punch_release,
"BOMB_PRESS": self.on_bomb_press,
"BOMB_RELEASE": self.on_bomb_release,
"RUN": self.on_run,
}
self.mirror_spaz = []
self.source_player.in_replay = False
self.source_player.mirror_mode = False
def _handle_action(self, action, value: Optional[float] = None) -> None:
if self.source_player.sessionplayer in PLAYERS_STUNT_INFO:
PLAYERS_STUNT_INFO[self.source_player.sessionplayer].append({
"time": bs.time() - self.source_player.recording_start_time,
"move": {
"action": action,
"value": value,
}
})
elif self.source_player.in_replay:
ui_activation = CONTROLS_UI_MAP.get(action)
if ui_activation:
if action in ["UP_DOWN", "LEFT_RIGHT"]:
ui_activation(self.source_player.actor._activity(), value)
else:
ui_activation(self.source_player.actor._activity())
elif self.source_player.mirror_mode:
for mspaz in self.mirror_spaz:
if mspaz and mspaz.node.exists():
if action in ["UP_DOWN", "LEFT_RIGHT", "RUN"]:
mspaz.move_map[action](value)
else:
mspaz.move_map[action]()
def on_move_up_down(self, value: float, *args, **kwargs) -> None:
self._handle_action("UP_DOWN", value)
super().on_move_up_down(value, *args, **kwargs)
def on_move_left_right(self, value: float, *args, **kwargs) -> None:
self._handle_action("LEFT_RIGHT", value)
super().on_move_left_right(value, *args, **kwargs)
def on_hold_position_press(self, *args, **kwargs) -> None:
self._handle_action("HOLD_POSITION")
super().on_hold_position_press(*args, **kwargs)
def on_hold_position_release(self, *args, **kwargs) -> None:
self._handle_action("HOLD_RELEASE")
super().on_hold_position_release(*args, **kwargs)
def on_jump_press(self, *args, **kwargs) -> None:
self._handle_action("JUMP_PRESS")
super().on_jump_press(*args, **kwargs)
def on_jump_release(self, *args, **kwargs) -> None:
self._handle_action("JUMP_RELEASE")
super().on_jump_release(*args, **kwargs)
def on_pickup_press(self, *args, **kwargs) -> None:
self._handle_action("PICKUP_PRESS")
super().on_pickup_press(*args, **kwargs)
def on_pickup_release(self, *args, **kwargs) -> None:
self._handle_action("PICKUP_RELEASE")
super().on_pickup_release(*args, **kwargs)
def on_punch_press(self, *args, **kwargs) -> None:
self._handle_action("PUNCH_PRESS")
super().on_punch_press(*args, **kwargs)
def on_punch_release(self, *args, **kwargs) -> None:
self._handle_action("PUNCH_RELEASE")
super().on_punch_release(*args, **kwargs)
def on_bomb_press(self, *args, **kwargs) -> None:
self._handle_action("BOMB_PRESS")
super().on_bomb_press(*args, **kwargs)
def on_bomb_release(self, *args, **kwargs) -> None:
self._handle_action("BOMB_RELEASE")
super().on_bomb_release(*args, **kwargs)
def on_run(self, value: float, *args, **kwargs) -> None:
self._handle_action("RUN", value)
super().on_run(value, *args, **kwargs)
def handle_player_replay_end(player):
player.in_replay = False
ControlsUI.hide(player.actor._activity())
def get_player_from_client_id(client_id, activity=None):
activity = activity or _babase.get_foreground_host_activity()
for player in activity.players:
if player.sessionplayer.inputdevice.client_id == client_id:
return player
raise bs.SessionPlayerNotFound()
def mirror(clieid):
player = get_player_from_client_id(clieid)
spawn_mirror_spaz(player)
def capture(player):
with babase.Context(player.actor._activity()):
player.recording_start_time = bs.time()
PLAYERS_STUNT_INFO[player.sessionplayer] = []
def save(player, stunt_name):
stunt_path = f"{os.path.join(BASE_STUNTS_DIRECTORY, stunt_name)}.json"
os.makedirs(BASE_STUNTS_DIRECTORY, exist_ok=True)
with open(stunt_path, "w") as fout:
json.dump(PLAYERS_STUNT_INFO[player.sessionplayer], fout, indent=2)
del PLAYERS_STUNT_INFO[player.sessionplayer]
def replay(player, stunt_name):
stunt_path = f"{os.path.join(BASE_STUNTS_DIRECTORY, stunt_name)}.json"
if stunt_name in STUNT_CACHE:
stunt = STUNT_CACHE[stunt_name]
else:
try:
with open(stunt_path, "r") as fin:
stunt = json.load(fin)
STUNT_CACHE[stunt_name] = stunt
except:
bs.broadcastmessage(f"{stunt_name} doesn't exists")
return
player.in_replay = True
with babase.Context(player.actor._activity()):
ControlsUI.display(player.actor._activity())
for move in stunt:
value = move["move"]["value"]
if value is None:
bs.timer(
move["time"],
babase.Call(player.actor.move_map[move["move"]["action"]])
)
else:
bs.timer(
move["time"],
babase.Call(player.actor.move_map[move["move"]["action"]], move["move"]["value"])
)
last_move_time = move["time"]
time_to_hide_controls = last_move_time + 1
bs.timer(time_to_hide_controls, babase.Call(handle_player_replay_end, player))
def spawn_mirror_spaz(player):
player.mirror_mode = True
with babase.Context(player.actor._activity()):
bot = spaz.Spaz(player.color, player.highlight, character=player.character).autoretain()
bot.handlemessage(babase.StandMessage(
(player.actor.node.position[0], player.actor.node.position[1], player.actor.node.position[2]+1), 93))
bot.node.name = player.actor.node.name
bot.node.name_color = player.actor.node.name_color
player.actor.mirror_spaz.append(bot)
def ghost(player, stunt_name):
stunt_path = f"{os.path.join(BASE_STUNTS_DIRECTORY, stunt_name)}.json"
if stunt_name in STUNT_CACHE:
stunt = STUNT_CACHE[stunt_name]
else:
try:
with open(stunt_path, "r") as fin:
stunt = json.load(fin)
STUNT_CACHE[stunt_name] = stunt
except:
bs.broadcastmessage(f"{stunt_name} doesn't exists")
return
player.in_replay = True
with babase.Context(player.actor._activity()):
bot = spaz.Spaz((1, 0, 0), character="Spaz").autoretain()
bot.handlemessage(babase.StandMessage(player.actor.node.position, 93))
give_ghost_power(bot)
ControlsUI.display(player.actor._activity())
for move in stunt:
value = move["move"]["value"]
if value is None:
bs.timer(
move["time"],
babase.Call(bot.move_map[move["move"]["action"]])
)
ui_activation = CONTROLS_UI_MAP.get(move["move"]["action"])
if ui_activation:
bs.timer(
move["time"],
babase.Call(ui_activation, player.actor._activity())
)
else:
bs.timer(
move["time"],
babase.Call(bot.move_map[move["move"]["action"]], move["move"]["value"])
)
ui_activation = CONTROLS_UI_MAP.get(move["move"]["action"])
if ui_activation:
bs.timer(
move["time"],
babase.Call(ui_activation, player.actor._activity(), move["move"]["value"])
)
last_move_time = move["time"]
time_to_hide_controls = last_move_time + 1
bs.timer(time_to_hide_controls, babase.Call(handle_player_replay_end, player))
bs.timer(time_to_hide_controls, babase.Call(bot.node.delete))
def give_ghost_power(spaz):
spaz.node.invincible = True
shared = SharedObjects.get()
factory = SpazFactory.get()
ghost = bs.Material()
# smoothy hecks
ghost.add_actions(
conditions=(('they_have_material', factory.spaz_material), 'or',
('they_have_material', shared.player_material), 'or',
('they_have_material', shared.attack_material), 'or',
('they_have_material', shared.pickup_material), 'or',
('they_have_material', PowerupBoxFactory.get().powerup_accept_material)),
actions=(
('modify_part_collision', 'collide', False),
('modify_part_collision', 'physical', False)
))
mats = list(spaz.node.materials)
roller = list(spaz.node.roller_materials)
ext = list(spaz.node.extras_material)
pick = list(spaz.node.pickup_materials)
punch = list(spaz.node.punch_materials)
mats.append(ghost)
roller.append(ghost)
ext.append(ghost)
pick.append(ghost)
punch.append(ghost)
spaz.node.materials = tuple(mats)
spaz.node.roller_materials = tuple(roller)
spaz.node.extras_material = tuple(ext)
spaz.node.pickup_materials = tuple(pick)
spaz.node.punch_materials = tuple(pick)
def new_chatmessage(msg):
if not msg.startswith("*"):
return original_chatmessage(msg)
stripped_msg = msg[1:]
msg_splits = stripped_msg.split(maxsplit=3)
command = msg_splits[0]
client_id = -1
player = get_player_from_client_id(client_id)
if command == "start":
capture(player)
bs.chatmessage("Recording started for {}.".format(
player.getname(),
))
return original_chatmessage(msg)
stunt_name = " ".join(msg_splits[1:])
if command == "save":
if len(msg_splits) < 2:
bs.broadcastmessage("Enter name of stunt eg : *save bombjump")
return original_chatmessage(msg)
save(player, stunt_name)
bs.chatmessage('Recording "{}" by {} saved.'.format(
stunt_name,
player.getname(),
))
elif command == "stunt":
if len(msg_splits) < 2:
bs.broadcastmessage("Enter name of stunt eg : *stunt bombjump")
return original_chatmessage(msg)
replay(player, stunt_name)
bs.chatmessage('Replaying "{}" on {}.'.format(
stunt_name,
player.getname(),
))
elif command == "learn":
if len(msg_splits) < 2:
bs.broadcastmessage("Enter name of stunt eg : *learn bombjump")
return original_chatmessage(msg)
ghost(player, stunt_name)
bs.chatmessage('Replaying "{}" on {}.'.format(
stunt_name,
player.getname(),
))
elif command == "mirror":
spawn_mirror_spaz(player)
return original_chatmessage(msg)
def set_stick_image_position(self, x: float, y: float) -> None:
# Clamp this to a circle.
len_squared = x * x + y * y
if len_squared > 1.0:
length = math.sqrt(len_squared)
mult = 1.0 / length
x *= mult
y *= mult
self.stick_image_position_x = x
self.stick_image_position_y = y
offs = 50.0
assert self._scale is not None
p = [
self._stick_nub_position[0] + x * offs * 0.6,
self._stick_nub_position[1] + y * offs * 0.6
]
c = list(self._stick_nub_image_color)
if abs(x) > 0.1 or abs(y) > 0.1:
c[0] *= 2.0
c[1] *= 4.0
c[2] *= 2.0
assert self._stick_nub_image is not None
self._stick_nub_image.position = p
self._stick_nub_image.color = c
c = list(self._stick_base_image_color)
if abs(x) > 0.1 or abs(y) > 0.1:
c[0] *= 1.5
c[1] *= 1.5
c[2] *= 1.5
assert self._stick_base_image is not None
self._stick_base_image.color = c
def on_begin(self, *args, **kwargs) -> None:
self._jump_image = Image(
bs.gettexture('buttonJump'),
position=(385, 160),
scale=(50, 50),
color=[0.1, 0.45, 0.1, 0]
)
self._pickup_image = Image(
bs.gettexture('buttonPickUp'),
position=(385, 240),
scale=(50, 50),
color=[0, 0.35, 0, 0]
)
self._punch_image = Image(
bs.gettexture('buttonPunch'),
position=(345, 200),
scale=(50, 50),
color=[0.45, 0.45, 0, 0]
)
self._bomb_image = Image(
bs.gettexture('buttonBomb'),
position=(425, 200),
scale=(50, 50),
color=[0.45, 0.1, 0.1, 0]
)
self.stick_image_position_x = self.stick_image_position_y = 0.0
self._stick_base_position = p = (-328, 200)
self._stick_base_image_color = c2 = (0.25, 0.25, 0.25, 1.0)
self._stick_base_image = bs.newnode(
'image',
attrs={
'texture': bs.gettexture('nub'),
'absolute_scale': True,
'vr_depth': -40,
'position': p,
'scale': (220.0*0.6, 220.0*0.6),
'color': c2
})
self._stick_nub_position = p = (-328, 200)
self._stick_nub_image_color = c3 = (0.4, 0.4, 0.4, 1.0)
self._stick_nub_image = bs.newnode('image',
attrs={
'texture': bs.gettexture('nub'),
'absolute_scale': True,
'position': p,
'scale': (110*0.6, 110*0.66),
'color': c3
})
self._stick_base_image.opacity = 0.0
self._stick_nub_image.opacity = 0.0
self.set_stick_image_position = set_stick_image_position
return original_on_begin(self, *args, **kwargs)
# ba_meta export plugin
class byHeySmoothy(babase.Plugin):
def on_app_running(self):
bui.set_party_icon_always_visible(True)
bs._activity.Activity.on_begin = on_begin
# _babase.chatmessage = new_chatmessage
bascenev1lib.actor.playerspaz.PlayerSpaz = NewPlayerSpaz
bascenev1lib.actor.spaz.Spaz = NewSpaz
# lets define a sample elimination game that can use super power of this plugin
from plugins import auto_stunt
# ba_meta export bascenev1.GameActivity
class BroEliminaition(EliminationGame):
name = 'BroElimination'
description = 'Elimination Game with dual character control'
def spawn_player(self, player) -> bs.Actor:
super().spawn_player(player)
auto_stunt.spawn_mirror_spaz(player)

287
dist/ba_root/mods/plugins/bcs_plugin.py vendored Normal file
View file

@ -0,0 +1,287 @@
# -*- coding: utf-8 -*-
# coding: utf-8
# ba_meta require api 8
# from gunicorn.app.base import BaseApplication
# from gunicorn.workers import ggevent as gevent_worker
import logging
from threading import Thread
from flask import Flask, request, jsonify
from functools import wraps
import os
import _babaseimport _thread
# import uvicorn
from . import bombsquad_service
os.environ['FLASK_APP'] = 'bombsquadflaskapi.py'
os.environ['FLASK_ENV'] = 'development'
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
app = Flask(__name__)
app.config["DEBUG"] = False
SECRET_KEY = 'default'
@app.after_request
def add_cors_headers(response):
# Allow requests from any origin
response.headers['Access-Control-Allow-Origin'] = '*'
# Allow specific headers
response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization,Secret-Key'
# Allow specific HTTP methods
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
return response
def check_admin(func):
@wraps(func)
def wrapper(*args, **kwargs):
if "Secret-Key" not in request.headers or request.headers["Secret-Key"] != SECRET_KEY:
return jsonify({"message": "Invalid secret key provided."}), 401
return func(*args, **kwargs)
return wrapper
@app.route('/', methods=['GET'])
def home():
return '''Nothing here :)'''
@app.route('/api/live-stats', methods=['GET'])
def get_live_stats():
return jsonify(bombsquad_service.get_stats()), 200
@app.route('/api/top-200', methods=['GET'])
def get_top200():
return jsonify(bombsquad_service.get_top_200()), 200
@app.route('/api/subscribe', methods=['POST'])
def subscribe_player():
try:
data = request.get_json()
bombsquad_service.subscribe_player(
data["subscription"], data["player_id"], data["name"])
response = {
'message': f'Subscribed {data["name"]} successfully , will send confirmation notification to test'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
# ============ Admin only =========
@app.route('/api/login', methods=['POST'])
@check_admin
def login():
return jsonify({"message": "Successful"}), 200
@app.route('/api/current-leaderboard', methods=['GET'])
@check_admin
def get_complete_leaderboard():
return jsonify(bombsquad_service.get_complete_leaderboard()), 200
@app.route('/api/server-settings', methods=['GET'])
@check_admin
def get_server_settings():
return jsonify(bombsquad_service.get_server_settings()), 200
@app.route('/api/roles', methods=['GET'])
@check_admin
def get_roles():
return jsonify(bombsquad_service.get_roles()), 200
@app.route('/api/roles', methods=['POST'])
@check_admin
def update_roles():
try:
data = request.get_json()
bombsquad_service.update_roles(data)
response = {
'message': 'Roles updated successfully'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
@app.route('/api/perks', methods=['GET'])
@check_admin
def get_perks():
return jsonify(bombsquad_service.get_perks()), 200
@app.route('/api/perks', methods=['POST'])
@check_admin
def update_perks():
try:
data = request.get_json()
bombsquad_service.update_perks(data)
response = {
'message': 'Custom perks updated successfully'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
@app.route('/api/server-settings', methods=['POST'])
@check_admin
def update_server_settings():
try:
data = request.get_json()
bombsquad_service.update_server_settings(data)
response = {
'message': 'Settings updated successfully, server may need restart'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
@app.route('/api/db-list', methods=['GET'])
@check_admin
def fetch_dB_list():
key = request.args.get('type')
if key is None:
return "type required", 400
if key == "logs":
return jsonify(bombsquad_service.get_logs_db_list()), 200
elif key == "players":
return jsonify(bombsquad_service.get_profiles_db_list()), 200
else:
return jsonify({"message": "Invalid db type"}), 400
@app.route('/api/search-logs', methods=['GET'])
@check_admin
def search_logs():
key = request.args.get('key')
db = request.args.get('db')
if key is None or db is None:
return jsonify({"message": "key and db required"}), 400
return jsonify(bombsquad_service.get_matching_logs(key, db)), 200
@app.route('/api/search-player', methods=['GET'])
@check_admin
def search_players():
key = request.args.get('key')
db = request.args.get('db')
if key is None or db is None:
return jsonify({"message": "key and db required"}), 400
return jsonify(bombsquad_service.search_player_profile(key, db)), 200
@app.route('/api/get-player-info', methods=['GET'])
@check_admin
def get_player():
account_id = request.args.get('account-id')
if account_id is None:
return jsonify({"message": "account-id required"}), 400
return jsonify(bombsquad_service.get_player_details(account_id)), 200
@app.route('/api/update-player', methods=['POST'])
@check_admin
def update_player():
account_id = request.args.get('account-id')
action = request.args.get('action')
duration = int(request.args.get('duration'))
if account_id is None or action is None:
return "account-id and action required", 400
if action == "ban":
bombsquad_service.ban_player(account_id, duration)
elif action == "unban":
bombsquad_service.unban_player(account_id)
elif action == "mute":
bombsquad_service.mute_player(account_id, duration)
elif action == "unmute":
bombsquad_service.unmute_player(account_id)
elif action == "disable-kick-vote":
bombsquad_service.disable_kick_vote(account_id, duration)
elif action == "enable-kick-vote":
bombsquad_service.enable_kick_vote(account_id)
else:
return jsonify({"message": "Invalid Action"}), 400
return jsonify({"message": f"{action} done"}), 201
@app.route('/api/config', methods=['GET'])
@check_admin
def get_config():
return jsonify(bombsquad_service.get_server_config()), 200
@app.route('/api/action', methods=['POST'])
@check_admin
def do_action():
action = request.args.get('action')
value = request.args.get('value')
bombsquad_service.do_action(action, value)
return jsonify({"message": f'{action} done'}), 200
@app.route('/api/config', methods=['POST'])
@check_admin
def update_server_config():
try:
data = request.get_json()
bombsquad_service.update_server_config(data)
response = {
'message': 'config updated successfully, server will restart'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
# from flask_asgi import FlaskASGI
# asgi_app = FlaskASGI(app)
# class FlaskApplication(BaseApplication):
# def __init__(self, app, options=None):
# self.options = options or {}
# self.application = app
# super(FlaskApplication, self).__init__()
# def load_config(self):
# config = {key: value for key, value in self.options.items(
# ) if key in self.cfg.settings and value is not None}
# for key, value in config.items():
# self.cfg.set(key.lower(), value)
# def load(self):
# return self.application
# def start_uvicorn():
# uvicorn.run("main:app", host='0.0.0.0', port=5000,
# reload=False, log_level="debug", workers=3, use_colors=True, no_signal=True)
# flask_run = _thread.start_new_thread(app.run, ("0.0.0.0", 5000, False))
def run_server():
from waitress import serve
serve(app, host="0.0.0.0", port=_babase.get_game_port())
def enable(password):
global SECRET_KEY
SECRET_KEY = password
t = Thread(target=run_server)
t.start()
# uvicorn_thread = threading.Thread(target=start_uvicorn)
# uvicorn_thread.start()
# options = {
# 'bind': '0.0.0.0:8000',
# 'workers': 4,
# 'worker_class': 'gevent'
# }
# flask_app = FlaskApplication(app, options)
# gevent_worker.GeventWorker(app.wsgi_app).init_process()
# flask_app.run()

View file

@ -0,0 +1,270 @@
import ecdsa
import base64
import json
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babaseimport babase.internal
from stats import mystats
from typing import Optional, Any, Dict, List, Type, Sequence
from babase._gameactivity import GameActivity
from playersData import pdata
from serverData import serverdata
from tools import servercheck, logger, notification_manager
import setting
from datetime import datetime
import _thread
import os
import yaml
stats = {}
leaderboard = {}
top200 = {}
vapidkeys = {}
serverinfo = {}
class BsDataThread(object):
def __init__(self):
global stats
stats["name"] = _babase.app.server._config.party_name
stats["discord"] = "https://discord.gg/ucyaesh"
stats["vapidKey"] = notification_manager.get_vapid_keys()["public_key"]
self.refresh_stats_cache_timer = bs.Timer(8, babase.Call(self.refreshStats),
babase.TimeType.REAL, repeat=True)
self.refresh_leaderboard_cache_timer = bs.Timer(10, babase.Call(
self.refreshLeaderboard), babase.TimeType.REAL, repeat=True)
def startThread(self):
_thread.start_new_thread(self.refreshLeaderboard, ())
def refreshLeaderboard(self):
global leaderboard
global top200
lboard = mystats.get_cached_stats()
leaderboard = lboard
sorted_data = sorted(lboard.values(), key=lambda x: x["rank"])
top_200_players = sorted_data[:200]
top200 = {player["aid"]: player for player in top_200_players}
def refreshStats(self):
global stats
global serverinfo
liveplayers = {}
nextMap = ''
currentMap = ''
for i in babase.internal.get_game_roster():
try:
liveplayers[i['account_id']] = {
'name': i['players'][0]['name_full'], 'client_id': i['client_id'], 'device_id': i['display_string']}
except:
liveplayers[i['account_id']] = {
'name': "<in-lobby>", 'client_id': i['client_id'], 'device_id': i['display_string']}
try:
nextMap = babase.internal.get_foreground_host_session(
).get_next_game_description().evaluate()
current_game_spec = babase.internal.get_foreground_host_session()._current_game_spec
gametype: Type[GameActivity] = current_game_spec['resolved_type']
currentMap = gametype.get_settings_display_string(
current_game_spec).evaluate()
except:
pass
current_games = {'current': currentMap, 'next': nextMap}
# system={'cpu':"p.cpu_percent()",'ram':p.virtual_memory().percent}
system = {'cpu': "null", 'ram': 'null'}
stats['system'] = system
stats['roster'] = liveplayers
stats['chats'] = babase.internal.get_chat_messages()
stats['playlist'] = current_games
stats['teamInfo'] = self.getTeamInfo()
stats["sessionType"] = type(
babase.internal.get_foreground_host_session()).__name__
# print(self.getTeamInfo());
def getTeamInfo(self):
data = {}
session = babase.internal.get_foreground_host_session()
if session:
teams = session.sessionteams
for team in teams:
data[str(team.id)] = {'name': team.name if isinstance(team.name, str) else team.name.evaluate(),
'color': list(team.color),
'score': team.customdata['score'],
'players': []
}
for player in team.players:
teamplayer = {'name': player.getname(),
'device_id': player.inputdevice.get_v1_account_name(True),
'inGame': player.in_game,
'character': player.character,
'account_id': player.get_v1_account_id()
}
data[str(team.id)]['players'].append(teamplayer)
return data
BsDataThread()
def get_stats():
return stats
def get_complete_leaderboard():
return leaderboard
def get_top_200():
return top200
def get_server_settings():
return setting.get_settings_data()
def update_server_settings(settings):
logger.log(f'updating server settings, request from web')
setting.commit(settings)
def get_roles():
return pdata.get_roles()
def get_perks():
# TODO wire with spaz_effects to fetch list of effects.
return {"perks": pdata.get_custom_perks(), "availableEffects": ["spark", "glow", "fairydust", "sparkground", "sweat", "sweatground", "distortion", "shine", "highlightshine", "scorch", "ice", "iceground",
"slime", "metal", "splinter", "rainbow"]}
def update_perks(custom):
logger.log(f'updating custom perks, request from web')
pdata.update_custom_perks(custom)
def update_roles(roles):
logger.log("updated roles from web")
return pdata.update_roles(roles)
def get_profiles_db_list():
return pdata.get_profiles_archive_index()
def get_logs_db_list():
return serverdata.get_stats_index()
def get_matching_logs(key: str, filename: str):
logs = serverdata.read_logs(filename)
matching_lines = [line.strip() for line in logs.split('\n') if key in line]
return matching_lines
def search_player_profile(search_key: str, db: str):
selectedDB = {}
if db == "profiles.json":
selectedDB = pdata.get_profiles()
elif db in pdata.get_profiles_archive_index():
selectedDB = pdata.get_old_profiles(db)
matching_objects = {}
count = 0
for key in selectedDB.keys():
if (search_key == key or
any(search_key.lower() in s.lower() for s in selectedDB[key].get("display_string", [])) or
search_key.lower() in selectedDB[key].get("name", "").lower()):
matching_objects[key] = selectedDB[key]
count += 1
if count > 50:
break
return matching_objects
def get_player_details(account_id: str):
current_time = datetime.now()
current_profiles = pdata.get_profiles()
ip = ""
device_id = ""
if account_id in current_profiles:
ip = current_profiles[account_id]["lastIP"]
device_id = current_profiles[account_id]["deviceUUID"]
extra_info = pdata.get_detailed_info(account_id)
isBanned = False
isMuted = False
isKickVoteDisabled = False
haveBanReason = servercheck.check_ban(ip, device_id, account_id, False)
if haveBanReason:
isBanned = True
extra_info += " , Banned for > " + haveBanReason
if account_id in pdata.get_blacklist()["muted-ids"] and current_time < datetime.strptime(pdata.get_blacklist()["muted-ids"][account_id]["till"], "%Y-%m-%d %H:%M:%S"):
isMuted = True
extra_info += f', Muted for > { pdata.get_blacklist()["muted-ids"][account_id]["reason"] } , till > {pdata.get_blacklist()["muted-ids"][account_id]["till"]} ,'
if account_id in pdata.get_blacklist()["kick-vote-disabled"] and current_time < datetime.strptime(pdata.get_blacklist()["kick-vote-disabled"][account_id]["till"], "%Y-%m-%d %H:%M:%S"):
isKickVoteDisabled = True
extra_info += f', Kick vote disabled for > { pdata.get_blacklist()["kick-vote-disabled"][account_id]["reason"] } , till > {pdata.get_blacklist()["kick-vote-disabled"][account_id]["till"]} '
return {"extra": extra_info, "isBan": isBanned, "isMuted": isMuted, "isKickVoteDisabled": isKickVoteDisabled}
def unban_player(account_id):
logger.log(f'unbanning {account_id} , request from web')
pdata.unban_player(account_id)
def unmute_player(account_id):
logger.log(f'unmuting {account_id} , request from web')
pdata.unmute(account_id)
def enable_kick_vote(account_id):
logger.log(f'enabling kick vote for {account_id} , request from web')
pdata.enable_kick_vote(account_id)
# TODO take duration input
def ban_player(account_id, duration):
logger.log(f'banning {account_id} , request from web')
pdata.ban_player(account_id, duration, "manually from website")
def mute_player(account_id, duration):
logger.log(f'muting {account_id} , request from web')
pdata.mute(account_id, duration, "manually from website")
def disable_kick_vote(account_id, duration):
logger.log(f'disable {account_id} , request from web')
pdata.disable_kick_vote(account_id, duration, "manually from website")
def get_server_config():
return _babase.app.server._config.__dict__
def update_server_config(config):
current_dir = os.getcwd()
file_path = os.path.join(current_dir, '..', 'config.yaml')
with open(file_path, "w") as f:
f.write(yaml.dump(config))
def do_action(action, value):
if action == "message":
_babase.pushcall(babase.Call(_babase.chatmessage, value), from_other_thread=True)
elif action == "quit":
_babase.pushcall(babase.Call(_babase.quit), from_other_thread=True)
def subscribe_player(sub, account_id, name):
notification_manager.subscribe(sub, account_id, name)

View file

@ -0,0 +1,353 @@
"""Define a simple example plugin."""
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
import random
from bascenev1lib.actor import bomb
from bascenev1lib.actor.bomb import BombFactory
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence, Optional, Callable
def new_blast_init(
self,
position: Sequence[float] = (0.0, 1.0, 0.0),
velocity: Sequence[float] = (0.0, 0.0, 0.0),
blast_radius: float = 2.0,
blast_type: str = "normal",
source_player: bs.Player = None,
hit_type: str = "explosion",
hit_subtype: str = "normal",
):
"""Instantiate with given values."""
# bah; get off my lawn!
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
bs.Actor.__init__(self) # super method can't be used here
shared = SharedObjects.get()
factory = BombFactory.get()
self.blast_type = blast_type
self._source_player = source_player
self.hit_type = hit_type
self.hit_subtype = hit_subtype
self.radius = blast_radius
# Set our position a bit lower so we throw more things upward.
rmats = (factory.blast_material, shared.attack_material)
self.node = bs.newnode(
"region",
delegate=self,
attrs={
"position": (position[0], position[1] - 0.1, position[2]),
"scale": (self.radius, self.radius, self.radius),
"type": "sphere",
"materials": rmats,
},
)
bs.timer(0.05, self.node.delete)
# Throw in an explosion and flash.
evel = (velocity[0], max(-1.0, velocity[1]), velocity[2])
explosion = bs.newnode(
"explosion",
attrs={
"position": position,
"velocity": evel,
"radius": self.radius,
"big": (self.blast_type == "tnt"),
},
)
if self.blast_type == "ice":
explosion.color = (0, 0.05, 0.4)
bs.timer(1.0, explosion.delete)
if self.blast_type != "ice":
bs.emitfx(
position=position,
velocity=velocity,
count=int(1.0 + random.random() * 4),
emit_type="tendrils",
tendril_type="thin_smoke",
)
bs.emitfx(
position=position,
velocity=velocity,
count=int(4.0 + random.random() * 4),
emit_type="tendrils",
tendril_type="ice" if self.blast_type == "ice" else "smoke",
)
bs.emitfx(
position=position,
emit_type="distortion",
spread=1.0 if self.blast_type == "tnt" else 2.0,
)
# And emit some shrapnel.
if self.blast_type == "ice":
def emit() -> None:
bs.emitfx(
position=position,
velocity=velocity,
count=30,
spread=2.0,
scale=0.4,
chunk_type="ice",
emit_type="stickers",
)
# It looks better if we delay a bit.
bs.timer(0.05, emit)
elif self.blast_type == "sticky":
def emit() -> None:
bs.emitfx(
position=position,
velocity=velocity,
count=int(4.0 + random.random() * 8),
spread=0.7,
chunk_type="slime",
)
bs.emitfx(
position=position,
velocity=velocity,
count=int(4.0 + random.random() * 8),
scale=0.5,
spread=0.7,
chunk_type="slime",
)
bs.emitfx(
position=position,
velocity=velocity,
count=15,
scale=0.6,
chunk_type="slime",
emit_type="stickers",
)
bs.emitfx(
position=position,
velocity=velocity,
count=20,
scale=0.7,
chunk_type="spark",
emit_type="stickers",
)
bs.emitfx(
position=position,
velocity=velocity,
count=int(6.0 + random.random() * 12),
scale=0.8,
spread=1.5,
chunk_type="spark",
)
# It looks better if we delay a bit.
bs.timer(0.05, emit)
elif self.blast_type == "impact":
def emit() -> None:
bs.emitfx(
position=position,
velocity=velocity,
count=int(4.0 + random.random() * 8),
scale=0.8,
chunk_type="metal",
)
bs.emitfx(
position=position,
velocity=velocity,
count=int(4.0 + random.random() * 8),
scale=0.4,
chunk_type="metal",
)
bs.emitfx(
position=position,
velocity=velocity,
count=20,
scale=0.7,
chunk_type="spark",
emit_type="stickers",
)
bs.emitfx(
position=position,
velocity=velocity,
count=int(8.0 + random.random() * 15),
scale=0.8,
spread=1.5,
chunk_type="spark",
)
# It looks better if we delay a bit.
bs.timer(0.05, emit)
else: # Regular or land mine bomb shrapnel.
def emit() -> None:
if self.blast_type != "tnt":
bs.emitfx(
position=position,
velocity=velocity,
count=int(4.0 + random.random() * 8),
chunk_type="rock",
)
bs.emitfx(
position=position,
velocity=velocity,
count=int(4.0 + random.random() * 8),
scale=0.5,
chunk_type="rock",
)
bs.emitfx(
position=position,
velocity=velocity,
count=30,
scale=1.0 if self.blast_type == "tnt" else 0.7,
chunk_type="spark",
emit_type="stickers",
)
bs.emitfx(
position=position,
velocity=velocity,
count=int(18.0 + random.random() * 20),
scale=1.0 if self.blast_type == "tnt" else 0.8,
spread=1.5,
chunk_type="spark",
)
# TNT throws splintery chunks.
if self.blast_type == "tnt":
def emit_splinters() -> None:
bs.emitfx(
position=position,
velocity=velocity,
count=int(20.0 + random.random() * 25),
scale=0.8,
spread=1.0,
chunk_type="splinter",
)
bs.timer(0.01, emit_splinters)
# Every now and then do a sparky one.
if self.blast_type == "tnt" or random.random() < 0.1:
def emit_extra_sparks() -> None:
bs.emitfx(
position=position,
velocity=velocity,
count=int(10.0 + random.random() * 20),
scale=0.8,
spread=1.5,
chunk_type="spark",
)
bs.timer(0.02, emit_extra_sparks)
# It looks better if we delay a bit.
bs.timer(0.05, emit)
lcolor = (0.6, 0.6, 1.0) if self.blast_type == "ice" else (1, 0.3, 0.1)
light = bs.newnode(
"light",
attrs={"position": position,
"volume_intensity_scale": 10.0, "color": lcolor},
)
scl = random.uniform(0.6, 0.9)
scorch_radius = light_radius = self.radius
if self.blast_type == "tnt":
light_radius *= 1.4
scorch_radius *= 1.15
scl *= 3.0
iscale = 1.6
bs.animate(
light,
"intensity",
{
0: 2.0 * iscale,
scl * 0.02: 0.1 * iscale,
scl * 0.025: 0.2 * iscale,
scl * 0.05: 17.0 * iscale,
scl * 0.06: 5.0 * iscale,
scl * 0.08: 4.0 * iscale,
scl * 0.2: 0.6 * iscale,
scl * 2.0: 0.00 * iscale,
scl * 3.0: 0.0,
},
)
bs.animate(
light,
"radius",
{
0: light_radius * 0.2,
scl * 0.05: light_radius * 0.55,
scl * 0.1: light_radius * 0.3,
scl * 0.3: light_radius * 0.15,
scl * 1.0: light_radius * 0.05,
},
)
bs.timer(scl * 3.0, light.delete)
# Make a scorch that fades over time.
scorch = bs.newnode(
"scorch",
attrs={
"position": position,
"size": scorch_radius * 0.5,
"big": (self.blast_type == "tnt"),
},
)
if self.blast_type == "ice":
scorch.color = (1, 1, 1.5)
else:
scorch.color = (random.random(), random.random(), random.random())
bs.animate(scorch, "presence", {3.000: 1, 13.000: 0})
bs.timer(13.0, scorch.delete)
if self.blast_type == "ice":
factory.hiss_sound.play(position=light.position)
lpos = light.position
factory.random_explode_sound().play(position=lpos)
factory.debris_fall_sound.play(position=lpos)
babase.camerashake(intensity=5.0 if self.blast_type == "tnt" else 1.0)
# TNT is more epic.
if self.blast_type == "tnt":
factory.random_explode_sound().play(position=lpos)
def _extra_boom() -> None:
factory.random_explode_sound().play(position=lpos)
bs.timer(0.25, _extra_boom)
def _extra_debris_sound() -> None:
factory.debris_fall_sound.play(position=lpos)
factory.wood_debris_fall_sound.play(position=lpos)
bs.timer(0.4, _extra_debris_sound)
def enable() -> None:
bomb.Blast.__init__ = new_blast_init

View file

@ -0,0 +1,125 @@
# This plugin developed fro Bombsquad Server, and I don't know how to make UI
# Just edit Config before starting server
# by: Lirik
# Further edited/Fixed by:Freak
import babase
import bauiv1 as bui
import bascenev1 as bs
import random
from random import choice
CONFIGS = {
"Radius": 2.0,
"Blinking": False,
"AdaptivePos": True,
"IgnoreOnMaps": [],
"Colors": {
"Intensity": 0.5,
"Animate": True,
"Random": True,
"LeftSide": (1, 0, 1),
"RightSide": (0, 0, 1),
}
}
def get_random_color():
"""Fetches random color every time for our nodes"""
choices = [0, 1, 2, 3]
return (choice(choices), choice(choices), choice(choices))
def get_colors():
"""Fucntion for getting colors for our light node based on configs"""
if CONFIGS["Colors"]["Random"]:
return get_random_color(), get_random_color()
return CONFIGS["Colors"]["LeftSide"], CONFIGS["Colors"]["RightSide"]
# Add more perfect positions for all maps
def get_adaptive_pos(name: str) -> tuple:
"""Fuction for getting pecfect positions for the current map
Args:
name (str): Name of the map
Returns:
[tuple]: tuple containing left and right position respectively
"""
adaptive = {"Crag Castle": ((-6, 7, -7), (6, 7, -7))}
if name in adaptive and CONFIGS["AdaptivePos"]:
return adaptive[name]
return (-10, 7, -3), (10, 7, -3)
def Map___init__(func):
"""Redefined method for babase.Map"""
def wrapper(self, vr_overlay_offset=None):
func(self, vr_overlay_offset)
name = self.getname()
if name in CONFIGS["IgnoreOnMaps"]:
return
left_color, right_color = get_colors()
left_pos, right_pos = get_adaptive_pos(name)
self.left_light = bs.newnode(
"light",
attrs={
"position": left_pos,
"radius": CONFIGS["Radius"],
"intensity": CONFIGS["Colors"]["Intensity"],
"color": left_color,
"volume_intensity_scale": 10,
},
)
self.right_light = bs.newnode(
"light",
attrs={
"position": right_pos,
"radius": CONFIGS["Radius"],
"intensity": CONFIGS["Colors"]["Intensity"],
"color": right_color,
"volume_intensity_scale": 10,
},
)
bs.animate(
self.left_light,
"radius",
{0: 0, 1.5: 0.5, 3: CONFIGS["Radius"]},
loop=True if CONFIGS["Blinking"] else False,
)
bs.animate(
self.right_light,
"radius",
{0: 0, 1.5: 0.5, 3: CONFIGS["Radius"]},
loop=True if CONFIGS["Blinking"] else False,
)
if CONFIGS["Colors"]["Animate"]:
bs.animate_array(
self.left_light,
"color",
3,
{
0: get_random_color(),
1: get_random_color(),
2: get_random_color(),
3: get_random_color(),
4: get_random_color(),
5: get_random_color(),
},
loop=True,
)
return wrapper
babase.Map.__init__ = Map___init__(babase.Map.__init__)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,60 @@
"""Module to update `setting.json`."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
from typing import TYPE_CHECKING
import os
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babaseimport json
from bascenev1lib.actor.spazappearance import Appearance
from tools.file_handle import OpenJson
if TYPE_CHECKING:
pass
def register_character(name: str, char: dict) -> None:
"""Registers the character in the game."""
t = Appearance(name.split(".")[0])
t.color_texture = char['color_texture']
t.color_mask_texture = char['color_mask']
t.default_color = (0.6, 0.6, 0.6)
t.default_highlight = (0, 1, 0)
t.icon_texture = char['icon_texture']
t.icon_mask_texture = char['icon_mask_texture']
t.head_mesh = char['head']
t.torso_mesh = char['torso']
t.pelvis_mesh = char['pelvis']
t.upper_arm_mesh = char['upper_arm']
t.forearm_mesh = char['forearm']
t.hand_mesh = char['hand']
t.upper_leg_mesh = char['upper_leg']
t.lower_leg_mesh = char['lower_leg']
t.toes_mesh = char['toes_mesh']
t.jump_sounds = char['jump_sounds']
t.attack_sounds = char['attack_sounds']
t.impact_sounds = char['impact_sounds']
t.death_sounds = char['death_sounds']
t.pickup_sounds = char['pickup_sounds']
t.fall_sounds = char['fall_sounds']
t.style = char['style']
def enable() -> None:
path=os.path.join(_babase.env()["python_directory_user"],"custom_characters" + os.sep)
if not os.path.isdir(path):
os.makedirs(path)
files=os.listdir(path)
for file in files:
if file.endswith(".json"):
with open(path + file) as json_file:
character = json.load(json_file)
register_character(file,character)

134
dist/ba_root/mods/plugins/wavedash.py vendored Normal file
View file

@ -0,0 +1,134 @@
"""Wavedash by TheMikirog
This is an early version of the plugin. Feedback appreciated!
"""
# ba_meta require api 8
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
import math
import bascenev1lib
from bascenev1lib.actor.spaz import Spaz
if TYPE_CHECKING:
pass
class MikiWavedashTest:
class FootConnectMessage:
"""Spaz started touching the ground"""
class FootDisconnectMessage:
"""Spaz stopped touching the ground"""
def wavedash(self) -> None:
if not self.node:
return
isMoving = abs(self.node.move_up_down) >= 0.5 or abs(
self.node.move_left_right) >= 0.5
if self._dead or not self.grounded or not isMoving:
return
if self.node.knockout > 0.0 or self.frozen or self.node.hold_node:
return
t_ms = bs.time(timeformat=babase.TimeFormat.MILLISECONDS)
assert isinstance(t_ms, int)
if t_ms - self.last_wavedash_time_ms >= self._wavedash_cooldown:
move = [self.node.move_left_right, -self.node.move_up_down]
vel = [self.node.velocity[0], self.node.velocity[2]]
move_length = math.hypot(move[0], move[1])
vel_length = math.hypot(vel[0], vel[1])
if vel_length < 1.25:
return
move_norm = [m/move_length for m in move]
vel_norm = [v/vel_length for v in vel]
dot = sum(x*y for x, y in zip(move_norm, vel_norm))
turn_power = min(round(math.acos(dot) / math.pi, 2)*1.3, 1)
if turn_power < 0.2:
return
boost_power = math.sqrt(
math.pow(vel[0], 2) + math.pow(vel[1], 2)) * 1.2
boost_power = min(pow(boost_power, 4), 160)
# print(boost_power * turn_power)
self.last_wavedash_time_ms = t_ms
# FX
bs.emitfx(position=self.node.position,
velocity=(vel[0]*0.5, -1, vel[1]*0.5),
chunk_type='sweat',
count=8,
scale=boost_power / 160 * turn_power,
spread=0.25)
# Boost itself
pos = self.node.position
for i in range(6):
self.node.handlemessage('impulse', pos[0], -0.1+pos[1]+i*0.1, pos[2],
0, 0, 0,
boost_power * turn_power,
boost_power * turn_power, 0, 0,
move[0], 0, move[1])
def new_spaz_init(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
# args[0] = self
args[0]._wavedash_cooldown = 30
args[0].last_wavedash_time_ms = -9999
args[0].grounded = 0
return wrapper
bascenev1lib.actor.spaz.Spaz.__init__ = new_spaz_init(
bascenev1lib.actor.spaz.Spaz.__init__)
def new_factory(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
args[0].roller_material.add_actions(
conditions=('they_have_material',
bascenev1lib.gameutils.SharedObjects.get().footing_material),
actions=(('message', 'our_node', 'at_connect', MikiWavedashTest.FootConnectMessage),
('message', 'our_node', 'at_disconnect', MikiWavedashTest.FootDisconnectMessage)))
return wrapper
bascenev1lib.actor.spazfactory.SpazFactory.__init__ = new_factory(
bascenev1lib.actor.spazfactory.SpazFactory.__init__)
def new_handlemessage(func):
def wrapper(*args, **kwargs):
if args[1] == MikiWavedashTest.FootConnectMessage:
args[0].grounded += 1
elif args[1] == MikiWavedashTest.FootDisconnectMessage:
if args[0].grounded > 0:
args[0].grounded -= 1
func(*args, **kwargs)
return wrapper
bascenev1lib.actor.spaz.Spaz.handlemessage = new_handlemessage(
bascenev1lib.actor.spaz.Spaz.handlemessage)
def new_on_run(func):
def wrapper(*args, **kwargs):
if args[0]._last_run_value < args[1] and args[1] > 0.8:
MikiWavedashTest.wavedash(args[0])
func(*args, **kwargs)
return wrapper
bascenev1lib.actor.spaz.Spaz.on_run = new_on_run(bascenev1lib.actor.spaz.Spaz.on_run)

144
dist/ba_root/mods/port.py vendored Normal file
View file

@ -0,0 +1,144 @@
# Usage: port_7_to_8.py <plugin-name> <client/server type of mod>
import re
import sys
import os
def port(file_path):
with open(file_path, "rb") as fin:
print("Porting "+ os.path.basename(file_path))
content = fin.read().decode("utf-8")
if "# ba_meta require api 8" in content:
return
content = content.replace("# ba_meta require api 7", "# ba_meta require api 8")
content = content.replace("# ba_meta export game", "# ba_meta export bascenev1.GameActivity")
content = content.replace("user_agent_string", "legacy_user_agent_string")
content = content.replace("_ba.", "_babase.")
content = content.replace("_ba.", "_babase.")
content = content.replace("ba.", "babase.")
content = content.replace("import _ba\n", "import _babase")
content = re.sub(r'\bimport _ba\b', "import _babase", content)
content = re.sub(r'\bimport ba(\b|\.(\w+))', "import babase\nimport bauiv1 as bui\nimport bascenev1 as bs", content)
content = content.replace("babase.app.ui", "bui.app.ui_v1")
content = content.replace("babase.app.accounts_v1", "bui.app.classic.accounts")
###################################################################################
# Comment out one of these as per your requirements, depending whether to
# stay local or if it'll also be needed to transmitted to the clients.
## For local:
if False:
content = content.replace("_babase.screenmessage", "bui.screenmessage")
content = content.replace("babase.screenmessage", "bui.screenmessage")
content = content.replace("babase.getsound", "bui.getsound")
content = content.replace("babase.gettexture", "bui.gettexture")
content = content.replace("babase.getmesh", "bui.getmesh")
content = content.replace("babase.getcollisionmesh", "bui.getcollisionmesh")
else:
## For transmission:
content = content.replace("babase.screenmessage", "bs.broadcastmessage")
content = content.replace("babase.getsound", "bs.getsound")
content = content.replace("babase.getmesh", "bs.getmesh")
content = content.replace("babase.getcollisionmesh", "bs.getcollisionmesh")
###################################################################################
content = content.replace("babase.open_url", "bui.open_url")
content = content.replace("babase.IntSetting", "bs.IntSetting")
content = content.replace("babase.IntChoiceSetting", "bs.IntChoiceSetting")
content = content.replace("babase.FloatChoiceSetting", "bs.FloatChoiceSetting")
content = content.replace("babase.BoolSetting", "bs.BoolSetting")
content = content.replace("babase.Actor", "bs.Actor")
content = content.replace("babase.Player", "bs.Player")
content = content.replace("babase.PlayerDiedMessage", "bs.PlayerDiedMessage")
content = content.replace("babase.time", "bs.time")
content = content.replace("babase.Timer", "bs.Timer")
content = content.replace("babase.newnode", "bs.newnode")
content = content.replace("babase.Node", "bs.Node")
content = content.replace("babase.emitfx", "bs.emitfx")
content = content.replace("babase.animate", "bs.animate")
content = content.replace("babase.FreeForAllSession", "bs.FreeForAllSession")
content = content.replace("babase.DualTeamSession", "bs.DualTeamSession")
content = content.replace("babase.MultiTeamSession", "bs.MultiTeamSession")
content = content.replace("babase.TeamGameActivity", "bs.TeamGameActivity")
content = content.replace("babase.Team", "bs.Team")
content = content.replace("babase.Session", "bs.Session")
content = content.replace("babase.Material", "bs.Material")
content = content.replace("babase.WeakCall", "bs.WeakCall")
content = content.replace("babase.DieMessage", "bs.DieMessage")
content = content.replace("babase.OutOfBoundsMessage", "bs.OutOfBoundsMessage")
content = content.replace("babase.DroppedMessage", "bs.DroppedMessage")
content = content.replace("babase.HitMessage", "bs.HitMessage")
content = content.replace("babase.NotFoundError", "bs.NotFoundError")
content = content.replace("babase.getcollision", "bs.getcollision")
content = content.replace("babase.app.lang", "bs.app.lang")
content = content.replace("babase.MusicType", "bs.MusicType")
content = content.replace("babase.gettexture", "bs.gettexture")
content = content.replace("babase.getactivity", "bs.getactivity")
content = content.replace("babase.getactivity", "bs.getactivity")
content = content.replace("babase.CelebrateMessage", "bs.CelebrateMessage")
content = content.replace("babase.ScoreConfig", "bs.ScoreConfig")
content = content.replace("babase.ScoreType", "bs.ScoreType")
content = content.replace("babase.GameResults", "bs.GameResults")
content = content.replace("babase.getmaps", "bs.app.classic.getmaps")
content = content.replace("babase.cameraflash", "bs.cameraflash")
content = content.replace("babase.getmodel", "bs.getmesh")
content = content.replace("model", "mesh")
content = content.replace("babase.Window", "bui.Window")
content = content.replace("babase.Widget", "bui.Widget")
content = content.replace("babase.widget", "bui.widget")
content = content.replace("babase.containerwidget", "bui.containerwidget")
content = content.replace("babase.scrollwidget", "bui.scrollwidget")
content = content.replace("babase.buttonwidget", "bui.buttonwidget")
content = content.replace("babase.textwidget", "bui.textwidget")
content = content.replace("babase.checkboxwidget", "bui.checkboxwidget")
content = content.replace("babase.imagewidget", "bui.imagewidget")
content = content.replace("_bui", "bui")
# Converting `ba.playsound(abc)` to `abc.play()` is tricky.
# Do it manually in case regex substitution fails.
content = re.sub(
r'babase\.playsound\(\s*([^,\n]+),\s*([^,\n]+),\s*position=([^,\n]+)\)',
r'\1.play(\2, position=\3)',
content,
flags=re.MULTILINE
)
content = re.sub("babase\.playsound\((.+?), (.+?), (.+?)\)", "\\1.play(\\2, \\3)", content)
content = re.sub(
r'babase\.playsound\(([^,\n]+),\s*position=([^,\n]+)\)',
r'\1.play(position=\2)',
content
)
content = re.sub("babase\.playsound\((.*)\)", "\\1.play()", content)
content = content.replace("babase.internal.add_transaction", "bui.app.plus.add_v1_account_transaction")
content = content.replace("babase.internal.run_transaction", "bui.app.plus.run_v1_account_transaction")
content = content.replace("_babase.add_transaction", "bui.app.plus.add_v1_account_transaction")
content = content.replace("_babase.run_transactions", "bui.app.plus.run_v1_account_transactions")
content = content.replace("_babase.InputType", "babase.InputType")
content = content.replace("bastd.ui", "bauiv1lib")
content = content.replace("bastd", "bascenev1lib")
content = content.replace("timetype=","")
content = content.replace("babase.columnwidget", "bui.columnwidget")
content = content.replace("_babase.get_chat_messages", "bs.get_chat_messages")
content = content.replace("_babase.get_foreground_host_session","bs.get_foreground_host_session")
content = re.sub(r'bs\.Timer\(([^)]*)\bTimeType\.REAL\b([^)]*)\)', r'babase.AppTimer(\1\2)', content)
print("Done porting to API 8 "+os.path.basename(file_path))
with open(file_path, "w") as f:
f.write(content)
def list_python_files(directory='.'):
python_files = []
for dirpath, dirnames, filenames in os.walk(directory):
for filename in filenames:
if filename.endswith('.py'):
python_files.append(os.path.join(dirpath, filename))
return python_files
def start():
current_directory = os.getcwd()
py_files = list_python_files(current_directory)
for file in py_files:
port(file)
start()

3872
dist/ba_root/mods/serverData/Chat Logs.log vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

View file

3896
dist/ba_root/mods/serverData/joining.log vendored Normal file

File diff suppressed because it is too large Load diff

8604
dist/ba_root/mods/serverData/logs.log vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
# Released under the MIT License. See LICENSE for details.
import fcntl
import _babase
import os
clients = {}
cachedclients = []
muted = False
coopmode = False
ips = {}
recents = []
SERVER_DATA_PATH = os.path.join(
_babase.env()["python_directory_user"], "serverData" + os.sep
)
def get_stats_index():
return [x for x in os.listdir(SERVER_DATA_PATH) if "log" in x]
def read_logs(filename):
file_path = SERVER_DATA_PATH+filename
if not os.path.exists(file_path):
return ""
file = open(file_path, "r")
fcntl.flock(file.fileno(), fcntl.LOCK_SH)
contents = ""
try:
contents = file.read()
finally:
fcntl.flock(file.fileno(), fcntl.LOCK_UN)
file.close()
return contents

File diff suppressed because it is too large Load diff

180
dist/ba_root/mods/setting.json vendored Normal file
View file

@ -0,0 +1,180 @@
{
"Anti-IdRevealer": false,
"ChatCommands": {
"BrodcastCommand": true
},
"HostDeviceName": "v1.4",
"HostName": "BCS",
"KickVoteMsgType": "chat",
"ScoreScreenAnnouncement": {
"enable": true,
"msg": [
"click stats button to join discord",
"watch hey smoothy youtube channel",
"downlaod new mods from discord"
]
},
"ShowKickVoteStarterName": true,
"StumbledScoreScreen": true,
"WarnCooldownMinutes": 30,
"afk_remover": {
"enable": false,
"ingame_idle_time_in_secs": 60,
"kick_idle_from_lobby": true,
"lobby_idle_time_in_secs": 10
},
"afterWarnKickMsg": "Enough warnings, Goodbye have a nice day :)",
"allowInGameChat": true,
"allowTeamChat": true,
"allowVotes": true,
"autoNightMode": {
"enable": true,
"endTime": "06:00",
"fireflies": true,
"fireflies_random_color": false,
"startTime": "18:30"
},
"autoTeamBalance": true,
"ballistica_web": {
"enable": false,
"server_password":"my_secerT_password_very_hard"
},
"character_chooser": {
"enable": true
},
"colorful_explosions": {
"enable": true
},
"colorfullMap": true,
"contributeData": true,
"coopModeWithLessPlayers": {
"enable": false,
"minPlayerToExitCoop": 3
},
"custom_characters": {
"enable": true
},
"discordWebHook": {
"enable": false,
"webhookURL": "https://discord.com/api/webhooks/82649239/e7s0zyBJIuczXL7_CGSO5WM"
},
"discordbot": {
"enable": false,
"liveChat": true,
"liveStatsChannelID": 925440043672285200,
"logsChannelID": 925440079843958800,
"token": "<secret-token-here>"
},
"elPatronPowerups": {
"Quantity": {
"Curse": 1,
"Fire Bombs": 3,
"Fly Bombs": 3,
"Goodbye": 2,
"Healing Damage": 1,
"Health": 1,
"Ice Bombs": 3,
"Ice Man": 1,
"Impact Bombs": 3,
"Impairment Bombs": 2,
"Mine Bombs": 2,
"Punch": 3,
"Shield": 2,
"Speed": 2,
"Sticky Bombs": 3,
"Tank Shield": 1,
"Triple": 3
},
"enable": true,
"settings": {
"Healing Damage PTG": 72,
"Powers Gravity": true,
"Powerup Name": true,
"Powerup Scale": 1,
"Powerup Style": "Auto",
"Powerup Time": false,
"Powerup With Shield": true,
"Powerups": {
"Curse": 1,
"Fire Bombs": 3,
"Fly Bombs": 3,
"Goodbye": 2,
"Healing Damage": 1,
"Health": 1,
"Ice Bombs": 3,
"Ice Man": 1,
"Impact Bombs": 3,
"Impairment Bombs": 2,
"Mine Bombs": 2,
"Punch": 3,
"Shield": 2,
"Speed": 2,
"Sticky Bombs": 3,
"Tank Shield": 1,
"Triple": 3
},
"Tank Shield PTG": 96
}
},
"enableHitTexts": true,
"enableTagAnimation": true,
"enableTop5effects": true,
"enableeffects": true,
"enablehptag": true,
"enablerank": true,
"enablestats": true,
"enabletags": true,
"firstTimeJoinMsg": "Welcome to the server,we r saving all your account details and chats",
"leaderboard": {
"barsBehindName": true,
"enable": true
},
"maxAccountPerIP": 3,
"maxPlayersPerDevice": 2,
"maxWarnCount": 2,
"mikirogQuickTurn": {
"enable": false
},
"minAgeToChatInHours": 78,
"minAgeToJoinInHours": 24,
"newResultBoard": true,
"playermod": {
"default_bomb": "normal",
"default_bomb_count": 1,
"default_boxing_gloves": true,
"default_shield": false
},
"playlists": {
"elim": 412172,
"epic": 412173,
"ffa": 412175,
"ffasmash": 412179,
"smash": 412151,
"soccer": 412160,
"team": 12345
},
"regularWelcomeMsg": "Welcome Back",
"sameCharacterForTeam": false,
"statsResetAfterDays": 31,
"textonmap": {
"bottom left watermark": "Owner : <owner-name> \nEditor : <bablu>\nScripts : BCS1.7.13",
"center highlights": {
"color": [
1,
0,
0
],
"msg": [
"type end to start end vote",
"start msg with prefix .(dot) to send in game popup msg",
"start msg with prefix ,(comma) to send msg to teammates",
"BombSquad Community Server - BCS"
],
"randomColor": true
},
"top watermark": "Welcome to server \nIP @IP PORT @PORT"
},
"useV2Account": false,
"warnMsg": "WARNING !!!",
"whitelist": false
}

48
dist/ba_root/mods/setting.py vendored Normal file
View file

@ -0,0 +1,48 @@
"""Module to update `setting.json`."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
from typing import TYPE_CHECKING
from functools import lru_cache
import json
import _babase
if TYPE_CHECKING:
pass
SETTINGS_PATH = _babase.env().get("python_directory_user", "") + "/setting.json"
@lru_cache(maxsize=None)
def get_settings_data() -> dict:
"""Returns the dictionary of settings related to the server.
Returns
-------
dict
settings related to server
"""
with open(SETTINGS_PATH, mode="r", encoding="utf-8") as data:
return json.load(data)
def refresh_cache() -> None:
get_settings_data.cache_clear()
# lets cache it again
get_settings_data()
def commit(data: dict) -> None:
"""Commits the data in setting file.
Parameters
----------
data : dict
data to be commited
"""
with open(SETTINGS_PATH, mode="w", encoding="utf-8") as setting_file:
json.dump(data, setting_file, indent=4)
# settings updated ok now update the cache
refresh_cache()

7
dist/ba_root/mods/spazmod/__init__.py vendored Normal file
View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

33
dist/ba_root/mods/spazmod/hitmessage.py vendored Normal file
View file

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Released under the MIT License. See LICENSE for details.
import babase
import bauiv1 as bui
import bascenev1 as bs, _ba, setting
import babase.internal
from stats.mystats import damage_data
from bascenev1lib.actor.popuptext import PopupText
our_settings = setting.get_settings_data()
def handle_hit(mag, pos):
if not mag: return
#Send Screen Texts in enabled
if our_settings['enableHitTexts']:
try:
if mag >= 110: PopupText("#PRO !",color=(1,0.2,0.2),scale=1.6,position=pos).autoretain()
elif mag >= 93 and mag < 110: PopupText("GOOD ONE!",color=(1,0.3,0.1),scale=1.6,position=pos).autoretain()
elif mag >= 63 and mag < 93: PopupText("OH! YEAH",color=(1,0.5,0.2),scale=1.6,position=pos).autoretain()
elif mag >= 45 and mag < 63: PopupText("WTF!",color=(0.7,0.4,0.2),scale=1.6,position=pos).autoretain()
elif mag > 30 and mag < 45: PopupText("!!!",color=(1,1,1),scale=1.6,position=pos).autoretain()
except: pass
return
class hit_message(bs.HitMessage):
def __init__(self, *args, **kwargs):
hit_type = kwargs["hit_type"]
if hit_type == "punch":
handle_hit(kwargs['magnitude'], kwargs['pos'])
super().__init__(*args, **kwargs)
bs.HitMessage = hit_message

80
dist/ba_root/mods/spazmod/modifyspaz.py vendored Normal file
View file

@ -0,0 +1,80 @@
from spazmod import tag
import setting
from random import randint
from spazmod import hitmessage
import _babaseimport ba
import babase.internal
_setting = setting.get_settings_data()
if _setting['enableeffects']:
from spazmod import spaz_effects
spaz_effects.apply()
def update_name():
import babase.internal
from stats import mystats
stat = mystats.get_all_stats()
ros = babase.internal.get_game_roster()
for i in ros:
if i['account_id']:
name = i['display_string']
aid = i['account_id']
if aid in stat:
stat[aid]['name'] = name
mystats.dump_stats(stat)
# all activites related to modify spaz by any how will be here
def main(spaz, node, player):
if _setting['enablehptag']:
tag.addhp(node, spaz)
if _setting['enabletags']:
tag.addtag(node, player)
if _setting['enablerank']:
tag.addrank(node, player)
if _setting["playermod"]['default_boxing_gloves']:
spaz.equip_boxing_gloves()
if _setting['playermod']['default_shield']:
spaz.equip_shields()
spaz.bomb_type_default = _setting['playermod']['default_bomb']
spaz.bomb_count = _setting['playermod']['default_bomb_count']
# update_name() will add threading here later . it was adding delay on game start
def getCharacter(player, character):
if _setting["sameCharacterForTeam"]:
if "character" in player.team.sessionteam.customdata:
return player.team.sessionteam.customdata["character"]
return character
def getRandomCharacter(otherthen):
characters = list(babase.app.spaz_appearances.keys())
invalid_characters = ["Snake Shadow", "Lee", "Zola", "Butch", "Witch",
"Middle-Man", "Alien", "OldLady", "Wrestler", "Gretel", "Robot"]
while True:
val = randint(0, len(characters)-1)
ch = characters[val]
if ch not in invalid_characters and ch not in otherthen:
return ch
def setTeamCharacter():
if not _setting["sameCharacterForTeam"]:
return
used = []
teams = babase.internal.get_foreground_host_session().sessionteams
if len(teams) < 10:
for team in teams:
character = getRandomCharacter(used)
used.append(character)
team.name = character
team.customdata["character"] = character

View file

@ -0,0 +1,365 @@
from bascenev1lib.actor.playerspaz import *
import babase
import bauiv1 as bui
import bascenev1 as bs
import bascenev1lib
import functools
import random
from typing import List, Sequence, Optional, Dict, Any
import setting
from playersData import pdata
from stats import mystats
_settings = setting.get_settings_data()
RANK_EFFECT_MAP = {
1: ["rainbow", "shine"],
2: ["sweat"],
3: ["metal"],
4: ["iceground"],
}
def effect(repeat_interval=0):
def _activator(method):
@functools.wraps(method)
def _inner_activator(self, *args, **kwargs):
def _caller():
try:
method(self, *args, **kwargs)
except:
if self is None or not self.is_alive() or not self.node.exists():
self._activations = []
else:
raise
effect_activation = bs.Timer(repeat_interval, babase.Call(_caller), repeat=repeat_interval > 0)
self._activations.append(effect_activation)
return _inner_activator
return _activator
def node(check_interval=0):
def _activator(method):
@functools.wraps(method)
def _inner_activator(self):
node = method(self)
def _caller():
if self is None or not self.is_alive() or not self.node.exists():
node.delete()
self._activations = []
node_activation = bs.Timer(check_interval, babase.Call(_caller), repeat=check_interval > 0)
try:
self._activations.append(node_activation)
except AttributeError:
pass
return _inner_activator
return _activator
class NewPlayerSpaz(PlayerSpaz):
def __init__(self,
player: bs.Player,
color: Sequence[float],
highlight: Sequence[float],
character: str,
powerups_expire: bool = True,
*args,
**kwargs):
super().__init__(player=player,
color=color,
highlight=highlight,
character=character,
powerups_expire=powerups_expire,
*args,
**kwargs)
self._activations = []
self.effects = []
babase._asyncio._asyncio_event_loop.create_task(self.set_effects())
async def set_effects(self):
try:
account_id = self._player._sessionplayer.get_v1_account_id()
except:
return
custom_effects = pdata.get_custom()['customeffects']
if account_id in custom_effects:
self.effects = [custom_effects[account_id]] if type(custom_effects[account_id]) is str else custom_effects[account_id]
else:
# check if we have any effect for his rank.
if _settings['enablestats']:
stats = mystats.get_cached_stats()
if account_id in stats and _settings['enableTop5effects']:
rank = stats[account_id]["rank"]
self.effects = RANK_EFFECT_MAP[rank] if rank in RANK_EFFECT_MAP else []
if len(self.effects) == 0:
return
self._effect_mappings = {
"spark": self._add_spark,
"sparkground": self._add_sparkground,
"sweat": self._add_sweat,
"sweatground": self._add_sweatground,
"distortion": self._add_distortion,
"glow": self._add_glow,
"shine": self._add_shine,
"highlightshine": self._add_highlightshine,
"scorch": self._add_scorch,
"ice": self._add_ice,
"iceground": self._add_iceground,
"slime": self._add_slime,
"metal": self._add_metal,
"splinter": self._add_splinter,
"rainbow": self._add_rainbow,
"fairydust": self._add_fairydust,
"noeffect": lambda: None,
}
for effect in self.effects:
trigger = self._effect_mappings[effect] if effect in self._effect_mappings else lambda: None
activity = self._activity()
if activity:
with babase.Context(self._activity()):
trigger()
@effect(repeat_interval=0.1)
def _add_spark(self):
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(1, 10),
scale=0.5,
spread=0.2,
chunk_type="spark",
)
@effect(repeat_interval=0.1)
def _add_sparkground(self):
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(1, 5),
scale=0.2,
spread=0.1,
chunk_type="spark",
emit_type="stickers",
)
@effect(repeat_interval=0.04)
def _add_sweat(self):
velocity = 4.0
calculate_position = lambda torso_position: torso_position - 0.25 + random.uniform(0, 0.5)
calculate_velocity = lambda node_velocity, multiplier: random.uniform(-velocity, velocity) + node_velocity * multiplier
position = tuple(calculate_position(coordinate)
for coordinate in self.node.torso_position)
velocity = (
calculate_velocity(self.node.velocity[0], 2),
calculate_velocity(self.node.velocity[1], 4),
calculate_velocity(self.node.velocity[2], 2),
)
bs.emitfx(
position=position,
velocity=velocity,
count=10,
scale=random.uniform(0.3, 1.4),
spread=0.1,
chunk_type="sweat",
)
@effect(repeat_interval=0.04)
def _add_sweatground(self):
velocity = 1.2
calculate_position = lambda torso_position: torso_position - 0.25 + random.uniform(0, 0.5)
calculate_velocity = lambda node_velocity, multiplier: random.uniform(-velocity, velocity) + node_velocity * multiplier
position = tuple(calculate_position(coordinate)
for coordinate in self.node.torso_position)
velocity = (
calculate_velocity(self.node.velocity[0], 2),
calculate_velocity(self.node.velocity[1], 4),
calculate_velocity(self.node.velocity[2], 2),
)
bs.emitfx(
position=position,
velocity=velocity,
count=10,
scale=random.uniform(0.1, 1.2),
spread=0.1,
chunk_type="sweat",
emit_type="stickers",
)
@effect(repeat_interval=1.0)
def _add_distortion(self):
bs.emitfx(
position=self.node.position,
spread=1.0,
emit_type="distortion"
)
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(1, 5),
emit_type="tendrils",
tendril_type="smoke",
)
@effect(repeat_interval=3.0)
def _add_shine(self):
shine_factor = 1.2
dim_factor = 0.90
default_color = self.node.color
shiny_color = tuple(channel * shine_factor for channel in default_color)
dimmy_color = tuple(channel * dim_factor for channel in default_color)
animation = {
0: default_color,
1: dimmy_color,
2: shiny_color,
3: default_color,
}
bs.animate_array(self.node, "color", 3, animation)
@effect(repeat_interval=9.0)
def _add_highlightshine(self):
shine_factor = 1.2
dim_factor = 0.90
default_highlight = self.node.highlight
shiny_highlight = tuple(channel * shine_factor for channel in default_highlight)
dimmy_highlight = tuple(channel * dim_factor for channel in default_highlight)
animation = {
0: default_highlight,
3: dimmy_highlight,
6: shiny_highlight,
9: default_highlight,
}
bs.animate_array(self.node, "highlight", 3, animation)
@effect(repeat_interval=2.0)
def _add_rainbow(self):
highlight = tuple(random.random() for _ in range(3))
highlight = babase.safecolor(highlight)
animation = {
0: self.node.highlight,
2: highlight,
}
bs.animate_array(self.node, "highlight", 3, animation)
@node(check_interval=0.5)
def _add_glow(self):
glowing_light = bs.newnode(
"light",
attrs={
"color": (1.0, 0.4, 0.5),
"height_attenuated": False,
"radius": 0.4}
)
self.node.connectattr("position", glowing_light, "position")
bs.animate(
glowing_light,
"intensity",
{0: 0.0, 1: 0.2, 2: 0.0},
loop=True)
return glowing_light
@node(check_interval=0.5)
def _add_scorch(self):
scorcher = bs.newnode(
"scorch",
attrs={
"position": self.node.position,
"size": 1.00,
"big": True}
)
self.node.connectattr("position", scorcher, "position")
animation = {
0: (1,0,0),
1: (0,1,0),
2: (1,0,1),
3: (0,1,1),
4: (1,0,0),
}
bs.animate_array(scorcher, "color", 3, animation, loop=True)
return scorcher
@effect(repeat_interval=0.5)
def _add_ice(self):
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(2, 8),
scale=0.4,
spread=0.2,
chunk_type="ice",
)
@effect(repeat_interval=0.05)
def _add_iceground(self):
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(1, 2),
scale=random.uniform(0, 0.5),
spread=1.0,
chunk_type="ice",
emit_type="stickers",
)
@effect(repeat_interval=0.25)
def _add_slime(self):
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(1, 10),
scale=0.4,
spread=0.2,
chunk_type="slime",
)
@effect(repeat_interval=0.25)
def _add_metal(self):
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(1, 4),
scale=0.4,
spread=0.1,
chunk_type="metal",
)
@effect(repeat_interval=0.75)
def _add_splinter(self):
bs.emitfx(
position=self.node.position,
velocity=self.node.velocity,
count=random.randint(1, 5),
scale=0.5,
spread=0.2,
chunk_type="splinter",
)
@effect(repeat_interval=0.25)
def _add_fairydust(self):
velocity = 2
calculate_position = lambda torso_position: torso_position - 0.25 + random.uniform(0, 0.5)
calculate_velocity = lambda node_velocity, multiplier: random.uniform(-velocity, velocity) + node_velocity * multiplier
position = tuple(calculate_position(coordinate)
for coordinate in self.node.torso_position)
velocity = (
calculate_velocity(self.node.velocity[0], 2),
calculate_velocity(self.node.velocity[1], 4),
calculate_velocity(self.node.velocity[2], 2),
)
bs.emitfx(
position=position,
velocity=velocity,
count=random.randint(1, 10),
spread=0.1,
emit_type="fairydust",
)
def apply() -> None:
bascenev1lib.actor.playerspaz.PlayerSpaz = NewPlayerSpaz

167
dist/ba_root/mods/spazmod/tag.py vendored Normal file
View file

@ -0,0 +1,167 @@
from playersData import pdata
import babase
import bauiv1 as bui
import bascenev1 as bs
import setting
import _babasefrom stats import mystats
sett = setting.get_settings_data()
def addtag(node, player):
session_player = player.sessionplayer
account_id = session_player.get_v1_account_id()
customtag_ = pdata.get_custom()
customtag = customtag_['customtag']
roles = pdata.get_roles()
p_roles = pdata.get_player_roles(account_id)
tag = None
col = (0.5, 0.5, 1) # default color for custom tags
if account_id in customtag:
tag = customtag[account_id]
elif p_roles != []:
for role in roles:
if role in p_roles:
tag = roles[role]['tag']
col = (
0.7, 0.7, 0.7) if 'tagcolor' not in roles[role] else roles[role]['tagcolor']
break
if tag:
Tag(node, tag, col)
def addrank(node, player):
session_player = player.sessionplayer
account_id = session_player.get_v1_account_id()
rank = mystats.getRank(account_id)
if rank:
Rank(node, rank)
def addhp(node, spaz):
def showHP():
hp = spaz.hitpoints
if spaz.node.exists():
HitPoint(owner=node, prefix=str(int(hp)),
position=(0, 1.75, 0), shad=1.4)
else:
spaz.hptimer = None
spaz.hptimer = bs.Timer(100, babase.Call(
showHP), repeat=True, babase.TimeType.SIM, timeformat=babase.TimeFormat.MILLISECONDS)
class Tag(object):
def __init__(self, owner=None, tag="somthing", col=(1, 1, 1)):
self.node = owner
mnode = bs.newnode('math',
owner=self.node,
attrs={
'input1': (0, 1.5, 0),
'operation': 'add'
})
self.node.connectattr('torso_position', mnode, 'input2')
if '\\' in tag:
tag = tag.replace('\\d', ('\ue048'))
tag = tag.replace('\\c', ('\ue043'))
tag = tag.replace('\\h', ('\ue049'))
tag = tag.replace('\\s', ('\ue046'))
tag = tag.replace('\\n', ('\ue04b'))
tag = tag.replace('\\f', ('\ue04f'))
tag = tag.replace('\\g', ('\ue027'))
tag = tag.replace('\\i', ('\ue03a'))
tag = tag.replace('\\m', ('\ue04d'))
tag = tag.replace('\\t', ('\ue01f'))
tag = tag.replace('\\bs', ('\ue01e'))
tag = tag.replace('\\j', ('\ue010'))
tag = tag.replace('\\e', ('\ue045'))
tag = tag.replace('\\l', ('\ue047'))
tag = tag.replace('\\a', ('\ue020'))
tag = tag.replace('\\b', ('\ue00c'))
self.tag_text = bs.newnode('text',
owner=self.node,
attrs={
'text': tag,
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': tuple(col),
'scale': 0.01,
'h_align': 'center'
})
mnode.connectattr('output', self.tag_text, 'position')
if sett["enableTagAnimation"]:
bs.animate_array(node=self.tag_text, attr='color', size=3, keys={
0.2: (2, 0, 2),
0.4: (2, 2, 0),
0.6: (0, 2, 2),
0.8: (2, 0, 2),
1.0: (1, 1, 0),
1.2: (0, 1, 1),
1.4: (1, 0, 1)
}, loop=True)
class Rank(object):
def __init__(self, owner=None, rank=99):
self.node = owner
mnode = bs.newnode('math',
owner=self.node,
attrs={
'input1': (0, 1.2, 0),
'operation': 'add'
})
self.node.connectattr('torso_position', mnode, 'input2')
if (rank == 1):
rank = '\ue01f' + "#"+str(rank) + '\ue01f'
elif (rank == 2):
rank = '\ue01f' + "#"+str(rank) + '\ue01f'
elif (rank == 3):
rank = '\ue01f' + "#"+str(rank) + '\ue01f'
else:
rank = "#"+str(rank)
self.rank_text = bs.newnode('text',
owner=self.node,
attrs={
'text': rank,
'in_world': True,
'shadow': 1.0,
'flatness': 1.0,
'color': (1, 1, 1),
'scale': 0.01,
'h_align': 'center'
})
mnode.connectattr('output', self.rank_text, 'position')
class HitPoint(object):
def __init__(self, position=(0, 1.5, 0), owner=None, prefix='0', shad=1.2):
self.position = position
self.node = owner
m = bs.newnode('math', owner=self.node, attrs={
'input1': self.position, 'operation': 'add'})
self.node.connectattr('torso_position', m, 'input2')
prefix = int(prefix) / 10
preFix = u"\ue047" + str(prefix) + u"\ue047"
self._Text = bs.newnode('text',
owner=self.node,
attrs={
'text': preFix,
'in_world': True,
'shadow': shad,
'flatness': 1.0,
'color': (1, 1, 1) if int(prefix) >= 20 else (1.0, 0.2, 0.2),
'scale': 0.01,
'h_align': 'center'})
m.connectattr('output', self._Text, 'position')
def a():
self._Text.delete()
m.delete()
self.timer = bs.Timer(100, babase.Call(
a), babase.TimeType.SIM, timeformat=babase.TimeFormat.MILLISECONDS)

7
dist/ba_root/mods/stats/__init__.py vendored Normal file
View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

297
dist/ba_root/mods/stats/mystats.py vendored Normal file
View file

@ -0,0 +1,297 @@
import setting
import _babase
import json
import os
import shutil
import threading
import datetime
import urllib.request
from bascenev1._activitytypes import *
damage_data = {}
ranks = []
top3Name = []
our_settings = setting.get_settings_data()
base_path = os.path.join(_babase.env()['python_directory_user'], "stats" + os.sep)
statsfile = base_path + 'stats.json'
cached_stats = {}
statsDefault = {
"pb-IF4VAk4a": {
"rank": 65,
"name": "pb-IF4VAk4a",
"scores": 0,
"total_damage": 0.0,
"kills": 0,
"deaths": 0,
"games": 18,
"kd": 0.0,
"avg_score": 0.0,
"aid": "pb-IF4VAk4a",
"last_seen": "2022-04-26 17:01:13.715014"
}
}
seasonStartDate = None
def get_all_stats():
global seasonStartDate
if os.path.exists(statsfile):
with open(statsfile, 'r', encoding='utf8') as f:
try:
jsonData = json.loads(f.read())
except:
f = open(statsfile+".backup", encoding='utf-8')
jsonData = json.load(f)
try:
stats = jsonData["stats"]
seasonStartDate = datetime.datetime.strptime(
jsonData["startDate"], "%d-%m-%Y")
_babase.season_ends_in_days = our_settings["statsResetAfterDays"] - (
datetime.datetime.now() - seasonStartDate).days
if (datetime.datetime.now() - seasonStartDate).days >= our_settings["statsResetAfterDays"]:
backupStatsFile()
seasonStartDate = datetime.datetime.now()
return statsDefault
return stats
except OSError as e:
print(e)
return jsonData
else:
return {}
def backupStatsFile():
shutil.copy(statsfile, statsfile.replace(
".json", "") + str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + ".json")
def dump_stats(s: dict):
global seasonStartDate
if seasonStartDate == None:
seasonStartDate = datetime.datetime.now()
s = {"startDate": seasonStartDate.strftime("%d-%m-%Y"), "stats": s}
if os.path.exists(statsfile):
shutil.copyfile(statsfile, statsfile+".backup")
with open(statsfile, 'w', encoding='utf8') as f:
f.write(json.dumps(s, indent=4, ensure_ascii=False))
f.close()
else:
print('Stats file not found!')
def get_stats_by_id(account_id: str):
a = get_cached_stats()
if account_id in a:
return a[account_id]
else:
return None
def get_cached_stats():
return cached_stats
def get_sorted_stats(stats):
entries = [(a['scores'], a['kills'], a['deaths'], a['games'],
a['name'], a['aid']) for a in stats.values()]
# this gives us a list of kills/names sorted high-to-low
entries.sort(key=lambda x: x[1] or 0, reverse=True)
return entries
def refreshStats():
global cached_stats
# lastly, write a pretty html version.
# our stats url could point at something like this...
pStats = get_all_stats()
cached_stats = pStats
entries = get_sorted_stats(pStats)
rank = 0
toppersIDs = []
_ranks = []
for entry in entries:
if True:
rank += 1
scores = str(entry[0])
kills = str(entry[1])
deaths = str(entry[2])
games = str(entry[3])
name = str(entry[4])
aid = str(entry[5])
if rank < 6:
toppersIDs.append(aid)
# The below kd and avg_score will not be added to website's html document, it will be only added in stats.json
try:
kd = str(float(kills) / float(deaths))
kd_int = kd.split('.')[0]
kd_dec = kd.split('.')[1]
p_kd = kd_int + '.' + kd_dec[:3]
except Exception:
p_kd = "0"
try:
avg_score = str(float(scores) / float(games))
avg_score_int = avg_score.split('.')[0]
avg_score_dec = avg_score.split('.')[1]
p_avg_score = avg_score_int + '.' + avg_score_dec[:3]
except Exception:
p_avg_score = "0"
if damage_data and aid in damage_data:
dmg = damage_data[aid]
dmg = str(str(dmg).split('.')[
0] + '.' + str(dmg).split('.')[1][:3])
else:
dmg = 0
_ranks.append(aid)
pStats[str(aid)]["rank"] = int(rank)
pStats[str(aid)]["scores"] = int(scores)
# not working properly
pStats[str(aid)]["total_damage"] += float(dmg)
pStats[str(aid)]["games"] = int(games)
pStats[str(aid)]["kills"] = int(kills)
pStats[str(aid)]["deaths"] = int(deaths)
pStats[str(aid)]["kd"] = float(p_kd)
pStats[str(aid)]["avg_score"] = float(p_avg_score)
global ranks
ranks = _ranks
dump_stats(pStats)
updateTop3Names(toppersIDs[0:3])
from playersData import pdata
pdata.update_toppers(toppersIDs)
def update(score_set):
"""
Given a Session's ScoreSet, tallies per-account kills
and passes them to a background thread to process and
store.
"""
# look at score-set entries to tally per-account kills for this round
account_kills = {}
account_deaths = {}
account_scores = {}
for p_entry in score_set.get_records().values():
account_id = p_entry.player.get_v1_account_id()
if account_id is not None:
account_kills.setdefault(account_id, 0) # make sure exists
account_kills[account_id] += p_entry.accum_kill_count
account_deaths.setdefault(account_id, 0) # make sure exists
account_deaths[account_id] += p_entry.accum_killed_count
account_scores.setdefault(account_id, 0) # make sure exists
account_scores[account_id] += p_entry.accumscore
# Ok; now we've got a dict of account-ids and kills.
# Now lets kick off a background thread to load existing scores
# from disk, do display-string lookups for accounts that need them,
# and write everything back to disk (along with a pretty html version)
# We use a background thread so our server doesn't hitch while doing this.
if account_scores:
UpdateThread(account_kills, account_deaths, account_scores).start()
class UpdateThread(threading.Thread):
def __init__(self, account_kills, account_deaths, account_scores):
threading.Thread.__init__(self)
self._account_kills = account_kills
self.account_deaths = account_deaths
self.account_scores = account_scores
def run(self):
# pull our existing stats from disk
import datetime
try:
stats = get_all_stats()
except:
stats = {}
# now add this batch of kills to our persistant stats
for account_id, kill_count in self._account_kills.items():
# add a new entry for any accounts that dont have one
if account_id not in stats:
# also lets ask the master-server for their account-display-str.
# (we only do this when first creating the entry to save time,
# though it may be smart to refresh it periodically since
# it may change)
stats[account_id] = {'rank': 0,
'name': "deafult name",
'scores': 0,
'total_damage': 0,
'kills': 0,
'deaths': 0,
'games': 0,
'kd': 0,
'avg_score': 0,
'last_seen': str(datetime.datetime.now()),
'aid': str(account_id)}
# Temporary codes to change 'name_html' to 'name'
# if 'name_html' in stats[account_id]:
# stats[account_id].pop('name_html')
# stats[account_id]['name'] = 'default'
url = "http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID=" + account_id
data = urllib.request.urlopen(url)
if data is not None:
try:
name = json.loads(data.read())["profileDisplayString"]
except ValueError:
stats[account_id]['name'] = "???"
else:
stats[account_id]['name'] = name
# now increment their kills whether they were already there or not
stats[account_id]['kills'] += kill_count
stats[account_id]['deaths'] += self.account_deaths[account_id]
stats[account_id]['scores'] += self.account_scores[account_id]
stats[account_id]['last_seen'] = str(datetime.datetime.now())
# also incrementing the games played and adding the id
stats[account_id]['games'] += 1
stats[account_id]['aid'] = str(account_id)
# dump our stats back to disk
tempppp = None
from datetime import datetime
dump_stats(stats)
# aaand that's it! There IS no step 27!
now = datetime.now()
update_time = now.strftime("%S:%M:%H - %d %b %y")
# print(f"Added {str(len(self._account_kills))} account's stats entries. || {str(update_time)}")
refreshStats()
def getRank(acc_id):
global ranks
if ranks == []:
refreshStats()
if acc_id in ranks:
return ranks.index(acc_id) + 1
def updateTop3Names(ids):
global top3Name
names = []
for id in ids:
url = "http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID=" + id
data = urllib.request.urlopen(url)
if data is not None:
try:
name = json.loads(data.read())["profileDisplayString"]
if (not name):
raise ValueError
except ValueError:
names.append("???")
else:
names.append(name)
top3Name = names

18
dist/ba_root/mods/stats/stats.json vendored Normal file
View file

@ -0,0 +1,18 @@
{
"startDate": "06-08-2023",
"stats": {
"pb-IF4VAk4a": {
"rank": 1,
"name": "pb-IF4VAk4a",
"scores": 0,
"total_damage": 0.0,
"kills": 0,
"deaths": 0,
"games": 18,
"kd": 0.0,
"avg_score": 0.0,
"aid": "pb-IF4VAk4a",
"last_seen": "2022-04-26 17:01:13.715014"
}
}
}

View file

@ -0,0 +1,18 @@
{
"startDate": "06-08-2023",
"stats": {
"pb-IF4VAk4a": {
"rank": 1,
"name": "pb-IF4VAk4a",
"scores": 0,
"total_damage": 0.0,
"kills": 0,
"deaths": 0,
"games": 18,
"kd": 0.0,
"avg_score": 0.0,
"aid": "pb-IF4VAk4a",
"last_seen": "2022-04-26 17:01:13.715014"
}
}
}

91
dist/ba_root/mods/tools/ServerUpdate.py vendored Normal file
View file

@ -0,0 +1,91 @@
from playersData import pdata
import time
import _thread
import urllib.request
from efro.terminal import Clr
import json
import requests
import _babase
VERSION = 71
def check():
data = {'name': _babase.app.server._config.party_name,
'port': str(_babase.get_game_port()),
'build': _babase.app.build_number,
'bcsversion': VERSION}
_thread.start_new_thread(updateProfilesJson, ())
_thread.start_new_thread(checkChangelog, ())
_thread.start_new_thread(postStatus, (data,))
def updateProfilesJson():
profiles = pdata.get_profiles()
for id in profiles:
if "spamCount" not in profiles[id]:
profiles[id]["spamCount"] = 0
profiles[id]["lastSpam"] = time.time()
pdata.commit_profiles(profiles)
def postStatus(data):
try:
res = requests.post('https://bcsservers.ballistica.workers.dev/ping',
json=data)
except:
pass
def checkSpammer(data):
def checkMaster(data):
try:
res = requests.post('https://bcsservers.ballistica.workers.dev/checkspammer',
json=data)
except:
pass
# TODO handle response and kick player based on status
_thread.start_new_thread(checkMaster, (data,))
return
def fetchChangelogs():
url = "https://raw.githubusercontent.com/imayushsaini/Bombsquad-Ballistica-Modded-Server/public-server/dist/ba_root/mods/changelogs.json"
if 2*2 == 4:
try:
data = urllib.request.urlopen(url)
changelog = json.loads(data.read())
except:
return None
else:
return changelog
def checkChangelog():
changelog = fetchChangelogs()
if changelog == None:
print(
f'{Clr.BRED} UNABLE TO CHECK UPDATES , CHECK MANUALLY FROM URL {Clr.RST}', flush=True)
else:
msg = ""
avail = False
for log in changelog:
if int(log) > VERSION:
avail = True
if not avail:
print(
f'{Clr.BGRN}{Clr.WHT} YOU ARE ON LATEST VERSION {Clr.RST}', flush=True)
else:
print(f'{Clr.BYLW}{Clr.BLU} UPDATES AVAILABLE {Clr.RST}', flush=True)
for log in changelog:
if int(log) > VERSION:
msg = changelog[log]["time"]
print(f'{Clr.CYN} {msg} {Clr.RST}', flush=True)
msg = changelog[log]["log"]
print(f'{Clr.MAG} {msg} {Clr.RST}', flush=True)

7
dist/ba_root/mods/tools/__init__.py vendored Normal file
View file

@ -0,0 +1,7 @@
"""Common bits of functionality shared between all efro projects.
Things in here should be hardened, highly type-safe, and well-covered by unit
tests since they are widely used in live client and server code.
license : MIT, see LICENSE for more details.
"""

87
dist/ba_root/mods/tools/account.py vendored Normal file
View file

@ -0,0 +1,87 @@
# ba_meta require api 8
from __future__ import annotations
import babase
import bauiv1 as bui
import bascenev1 as bs
import logging
from efro.error import CommunicationError
from playersData import pdata
import bacommon.cloud
STATUS_CHECK_INTERVAL_SECONDS = 2.0
class AccountUtil:
def __init__(self):
self._proxyid: str | None = None
self._proxykey: str | None = None
bs.sign_out_v1()
babase.app.cloud.send_message_cb(bacommon.cloud.LoginProxyRequestMessage(),
on_response=babase.Call(self._on_proxy_request_response))
def _on_proxy_request_response(self, response: bacommon.cloud.LoginProxyRequestResponse | Exception) -> None:
if isinstance(response, Exception):
logging.error("error occured")
logging.critical("Falling back to V1 account")
bs.sign_in_v1('Local')
return
address = bs.get_master_server_address(
version=2) + response.url
logging.debug("Copy this URL to your browser : " + address)
self._proxyid = response.proxyid
self._proxykey = response.proxykey
bs.timer(STATUS_CHECK_INTERVAL_SECONDS,
babase.Call(self._ask_for_status))
def _ask_for_status(self) -> None:
assert self._proxyid is not None
assert self._proxykey is not None
babase.app.cloud.send_message_cb(
bacommon.cloud.LoginProxyStateQueryMessage(
proxyid=self._proxyid, proxykey=self._proxykey),
on_response=babase.Call(self._got_status))
def _got_status(
self, response: bacommon.cloud.LoginProxyStateQueryResponse | Exception
) -> None:
# For now, if anything goes wrong on the server-side, just abort
# with a vague error message. Can be more verbose later if need be.
if (isinstance(response, bacommon.cloud.LoginProxyStateQueryResponse)
and response.state is response.State.FAIL):
logging.error("error occured ..terminating login request")
logging.critical("Falling back to V1 account")
bs.sign_in_v1('Local')
# If we got a token, set ourself as signed in. Hooray!
if (isinstance(response, bacommon.cloud.LoginProxyStateQueryResponse)
and response.state is response.State.SUCCESS):
assert response.credentials is not None
babase.app.accounts_v2.set_primary_credentials(response.credentials)
bs.timer(3, self._logged_in)
return
# If we're still waiting, ask again soon.
if (isinstance(response, Exception)
or response.state is response.State.WAITING):
bs.timer(STATUS_CHECK_INTERVAL_SECONDS,
babase.Call(self._ask_for_status))
def _logged_in(self):
logging.info("Logged in as: " +
bs.get_v1_account_display_string())
def updateOwnerIps():
if "owner" in pdata.get_roles():
accountIds = pdata.get_roles()["owner"]["ids"]
profiles = pdata.get_profiles()
for account_id in accountIds:
if account_id in profiles:
profile = profiles[account_id]
if "lastIP" in profile:
ip = profile["lastIP"]
_babase.append_owner_ip(ip)

BIN
dist/ba_root/mods/tools/corelib.so vendored Normal file

Binary file not shown.

89
dist/ba_root/mods/tools/file_handle.py vendored Normal file
View file

@ -0,0 +1,89 @@
"""Module to handle operations with file."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
__all__ = ["OpenJson", "JsonFile", "PathNotExistsError"]
from typing import TYPE_CHECKING
from dataclasses import dataclass
import json
import os
import re
from filelock import FileLock
if TYPE_CHECKING:
pass
class PathNotExistsError(Exception):
"""Error telling path does not exits."""
@dataclass
class JsonFile:
"""Object to handle simple operations with json file."""
path: str
def load(self, **kw) -> dict:
"""Loads the json file."""
if not os.path.exists(self.path):
PathNotExistsError(f"Path does not exists. {self.path}")
with FileLock(self.path):
with open(self.path, mode="r", encoding="utf-8") as json_file:
try:
data = json.load(json_file, **kw)
except json.JSONDecodeError:
print(f"Could not load json. {self.path}", end="")
print("Creating json in the file.", end="")
data = {}
self.dump(data)
return data
def dump(self, data: dict, **kw) -> None:
"""Dumps the json file."""
if not os.path.exists(self.path):
PathNotExistsError(f"Path does not exists. {self.path}")
with FileLock(self.path):
with open(self.path, mode="w", encoding="utf-8") as json_file:
json.dump(data, json_file, **kw)
def format(self, data: dict) -> None:
"""Dumps the json file."""
if not os.path.exists(self.path):
PathNotExistsError(f"Path does not exists. {self.path}")
with FileLock(self.path):
output = json.dumps(data, indent=4)
output2 = re.sub(r'": \[\s+', '": [', output)
output3 = re.sub(r'",\s+', '", ', output2)
output4 = re.sub(r'"\s+\]', '"]', output3)
with open(self.path, mode="w", encoding="utf-8") as json_file:
json_file.write(output4)
class OpenJson:
"""Context manager to open json files.
Json files opened with this will be file locked. If
json file is not readable then It will create new dict."""
def __init__(self, path: str) -> None:
self.json_obj = JsonFile(path)
def __enter__(self) -> JsonFile:
return self.json_obj
def __exit__(self, _type, value, traceback):
if traceback:
print(traceback)

65
dist/ba_root/mods/tools/healthcheck.py vendored Normal file
View file

@ -0,0 +1,65 @@
"""
Simple utility to monitor current CPU,Network,RAM usage.
Author: ChatGpt
"""
from . import logger
import asyncio
import psutil
from threading import Thread
async def monitor_network():
elapsed_time = 60
while True:
# Wait for 60 seconds
await asyncio.sleep(elapsed_time)
# Get the current network counters
net_io_counters = psutil.net_io_counters()
# Calculate the upload and download speeds in bytes/sec
# seconds
upload_speed = (net_io_counters.bytes_sent - monitor_network.bytes_sent) / elapsed_time
download_speed = (net_io_counters.bytes_recv - monitor_network.bytes_recv) / elapsed_time
# Convert the speeds to human-readable format
upload_speed = f"{upload_speed/1024:.2f} KB/s"
download_speed = f"{download_speed/1024:.2f} KB/s"
logger.log(f"CPU: {psutil.cpu_percent()}% RAM: {psutil.virtual_memory().percent}% Upload: {upload_speed} Download: {download_speed}")
# Update the counters
monitor_network.bytes_sent = net_io_counters.bytes_sent
monitor_network.bytes_recv = net_io_counters.bytes_recv
async def reset_counters():
while True:
# Wait for 2+2 minutes
await asyncio.sleep(120 + 120)
# Reset the network counters for each network interface separately
psutil.net_io_counters(pernic=True)
def main():
# Initialize the counters
net_io_counters = psutil.net_io_counters()
monitor_network.bytes_sent = net_io_counters.bytes_sent
monitor_network.bytes_recv = net_io_counters.bytes_recv
# Start the network monitoring and counter resetting coroutines
loop = asyncio.new_event_loop()
# Start the event loop in a separate thread
t = Thread(target=loop.run_forever)
t.start()
# Schedule the coroutines in the event loop
asyncio.run_coroutine_threadsafe(monitor_network(), loop=loop)
asyncio.run_coroutine_threadsafe(reset_counters(), loop=loop)

165
dist/ba_root/mods/tools/logger.py vendored Normal file
View file

@ -0,0 +1,165 @@
"""Module to Keeps the log of multiple things."""
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import requests
import time
import json
from typing import TYPE_CHECKING
from dataclasses import dataclass, field
import os
import datetime
import shutil
import threading
import setting
import _babase
import fcntl
if TYPE_CHECKING:
pass
SETTINGS = setting.get_settings_data()
SERVER_DATA_PATH = os.path.join(
_babase.env()["python_directory_user"], "serverData" + os.sep
)
if SETTINGS["discordbot"]["enable"]:
from features import discord_bot
WEBHOOK_URL = SETTINGS["discordWebHook"]["webhookURL"]
@dataclass
class RecentLogs:
"""Saves the recent logs."""
chats: list[str] = field(default_factory=list)
joinlog: list[str] = field(default_factory=list)
cmndlog: list[str] = field(default_factory=list)
misclogs: list[str] = field(default_factory=list)
logs = RecentLogs()
webhook_queue = []
def log(msg: str, mtype: str = "sys") -> None:
"""Cache and dumps the log."""
if SETTINGS["discordbot"]["enable"]:
message = msg.replace("||", "|")
discord_bot.push_log("***" + mtype + ":***" + message)
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = f"{current_time} + : {msg} \n"
if SETTINGS["discordWebHook"]["enable"]:
webhook_queue.append(msg.replace("||", "|"))
if mtype == "chat":
logs.chats.append(msg)
if len(logs.chats) > 10:
dumplogs(logs.chats, "chat").start()
logs.chats = []
elif mtype == "playerjoin":
logs.joinlog.append(msg)
if len(logs.joinlog) > 3:
dumplogs(logs.joinlog, "joinlog").start()
logs.joinlog = []
elif mtype == "chatcmd":
logs.cmndlog.append(msg)
if len(logs.cmndlog) > 3:
dumplogs(logs.cmndlog, "cmndlog").start()
logs.cmndlog = []
else:
logs.misclogs.append(msg)
if len(logs.misclogs) > 5:
dumplogs(logs.misclogs, "sys").start()
logs.misclogs = []
class dumplogs(threading.Thread):
"""Dumps the logs in the server data."""
def __init__(self, msg, mtype="sys"):
super().__init__()
self.msg = msg
self.type = mtype
def run(self):
if self.type == "chat":
log_path = SERVER_DATA_PATH + "Chat Logs.log"
elif self.type == "joinlog":
log_path = SERVER_DATA_PATH + "joining.log"
elif self.type == "cmndlog":
log_path = SERVER_DATA_PATH + "cmndusage.log"
else:
log_path = SERVER_DATA_PATH + "systemlogs.log"
if os.path.exists(log_path):
if os.stat(log_path).st_size > 1000000:
self.copy_file(
log_path, log_path+str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
self.write_file(log_path, self.msg)
def write_file(self, file_path, data):
file = open(file_path, "a+", encoding="utf-8")
fcntl.flock(file.fileno(), fcntl.LOCK_EX)
try:
for msg in data:
file.write(msg)
finally:
fcntl.flock(file.fileno(), fcntl.LOCK_UN)
file.close()
def copy_file(self, file_path, dest_path):
lock_file = open(file_path, "r")
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
try:
shutil.copy(file_path, dest_path)
except Exception as e:
print("Error occurred while copying file:", str(e))
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
lock_file.close()
os.remove(file_path)
def send_webhook_message():
global webhook_queue
msg = None
for msg_ in webhook_queue:
msg = msg_ + "\n"
webhook_queue = []
if msg:
payload = {
"content": msg,
"username": _babase.app.server._config.party_name
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(
WEBHOOK_URL, data=json.dumps(payload), headers=headers)
def schedule_webhook():
while True:
send_webhook_message()
time.sleep(8)
if SETTINGS["discordWebHook"]["enable"]:
thread = threading.Thread(target=schedule_webhook)
thread.start()

View file

@ -0,0 +1,151 @@
import time
import shutil
import random
import string
import json
import base64
import ecdsa
import os
import _babasefrom datetime import datetime
vapidkeys = {}
subscriptions = {}
subscribed_players = {}
PLAYERS_DATA_PATH = os.path.join(
_babase.env()["python_directory_user"], "playersData" + os.sep
)
def get_vapid_keys():
global vapidkeys
if vapidkeys != {}:
return vapidkeys
try:
f = open(".keys", "r")
vapidkeys = json.load(f)
f.close()
return vapidkeys
except:
pk = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p)
vk = pk.get_verifying_key()
vapidkeys = {
'private_key': base64.urlsafe_b64encode(pk.to_string()).rstrip(b'=').decode('utf-8'),
'public_key': base64.urlsafe_b64encode(b'\x04' + vk.to_string()).rstrip(b'=').decode('utf-8')
}
f = open(".keys", "w")
json.dump(vapidkeys, f)
f.close()
return vapidkeys
def send_push_notification(subscription, payload):
from pywebpush import webpush
try:
# Send the push notification using the subscription and payload
webpush(subscription_info=subscription, data=json.dumps(payload),
vapid_private_key=get_vapid_keys()["private_key"], vapid_claims={
'sub': 'mailto:{}'.format("test@ballistica.net"),
})
print("Push notification sent successfully")
except Exception as e:
print("Error sending push notification:", str(e))
# if we already have that browser subscription saved get id or generate new one
def get_subscriber_id(sub):
subscriber_id = None
for key, value in subscriptions.items():
if value["endpoint"] == sub["endpoint"]:
subscriber_id = key
break
if not subscriber_id:
subscriber_id = generate_random_string(6)
subscriptions[subscriber_id] = sub
return subscriber_id
def generate_random_string(length):
letters = string.ascii_letters + string.digits
return ''.join(random.choice(letters) for _ in range(length))
def subscribe(sub, account_id, name):
id = get_subscriber_id(sub)
if account_id in subscribed_players:
if id not in subscribed_players[account_id]["subscribers"]:
subscribed_players[account_id]["subscribers"].append(id)
subscribed_players[account_id]["name"] = name
else:
subscribed_players[account_id] = {"subscribers": [id], "name": name}
send_push_notification(sub, {"notification": {
"title": "Notification working !", "body": f'subscribed {name}'}})
def player_joined(pb_id):
now = datetime.now()
if pb_id in subscribed_players:
if "last_notification" in subscribed_players[pb_id] and (now - subscribed_players[pb_id]["last_notification"]).seconds < 15 * 60:
pass
else:
subscribed_players[pb_id]["last_notification"] = now
subscribes = subscribed_players[pb_id]["subscribers"]
for subscriber_id in subscribes:
sub = subscriptions[subscriber_id]
send_push_notification(
sub, {
"notification": {
"title": f'{subscribed_players[pb_id]["name"] } is playing now',
"body": f'Join {_babase.app.server._config.party_name} server {subscribed_players[pb_id]["name"]} is waiting for you ',
"icon": "assets/icons/icon-96x96.png",
"vibrate": [100, 50, 100],
"requireInteraction": True,
"data": {"dateOfArrival": datetime.now().strftime("%Y-%m-%d %H:%M:%S")},
"actions": [{"action": "nothing", "title": "Launch Bombsquad"}],
}
})
def loadCache():
global subscriptions
global subscribed_players
try:
f = open(PLAYERS_DATA_PATH+"subscriptions.json", "r")
subscriptions = json.load(f)
f.close()
except:
f = open(PLAYERS_DATA_PATH+"subscriptions.json.backup", "r")
subscriptions = json.load(f)
f.close()
try:
f = open(PLAYERS_DATA_PATH+"subscribed_players.json", "r")
subscribed_players = json.load(f)
f.close()
except:
f = open(PLAYERS_DATA_PATH+"subscribed_players.json.backup", "r")
subscribed_players = json.load(f)
f.close()
def dump_cache():
if subscriptions != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "subscriptions.json",
PLAYERS_DATA_PATH + "subscriptions.json.backup")
with open(PLAYERS_DATA_PATH + "subscriptions.json", "w") as f:
json.dump(subscriptions, f, indent=4)
if subscribed_players != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "subscribed_players.json",
PLAYERS_DATA_PATH + "subscribed_players.json.backup")
with open(PLAYERS_DATA_PATH + "subscribed_players.json", "w") as f:
json.dump(subscribed_players, f, indent=4)
time.sleep(60)
dump_cache()
loadCache()

122
dist/ba_root/mods/tools/playlist.py vendored Normal file
View file

@ -0,0 +1,122 @@
# ba_meta require api 8
# Thanks to Rikko for playlist fetch by code
import babase
import bauiv1 as bui
import bascenev1 as bs
import _babase
#session change by smoothy
from bascenev1._freeforallsession import FreeForAllSession
from bascenev1._dualteamsession import DualTeamSession
from bascenev1._coopsession import CoopSession
import time
import _thread
import setting
from babase._general import Call
settings = setting.get_settings_data()
_babase.app.classic.coop_session_args['max_players']=14
_babase.app.classic.coop_session_args['campaign']="Default"
_babase.app.classic.coop_session_args['level']="Onslaught Training"
def set_playlist(content):
if content is None:
return
_playlists_var = "{} Playlists".format(content["playlistType"])
playlists = _babase.app.config[_playlists_var]
playlist = playlists[content["playlistName"]]
bs.chatmessage("Fetched playlist:"+content["playlistName"])
typename = (
'teams' if content['playlistType'] == 'Team Tournament' else
'ffa' if content['playlistType'] == 'Free-for-All' else '??')
return set_playlist_inline(playlist,typename)
def set_playlist_inline(playlist,newPLaylistType):
session = bs.get_foreground_host_session()
if (isinstance(session,DualTeamSession) or isinstance(session,CoopSession)) and newPLaylistType=='ffa':
bs.get_foreground_host_session().end()
_thread.start_new_thread(withDelay,(FreeForAllSession,playlist,))
elif (isinstance(session,FreeForAllSession) or isinstance(session,CoopSession))and newPLaylistType=="teams":
bs.get_foreground_host_session().end()
_thread.start_new_thread(withDelay,(DualTeamSession,playlist,))
else:
updatePlaylist(playlist)
def withDelay(session,playlist):
time.sleep(1)
_babase.pushcall(Call(updateSession,session,playlist),from_other_thread=True)
def updateSession(session,playlist):
bs.new_host_session(session)
if playlist:
updatePlaylist(playlist)
def updatePlaylist(playlist):
session = bs.get_foreground_host_session()
content = babase._playlist.filter_playlist(
playlist,
sessiontype=type(session),
add_resolved_type=True,
)
playlist = babase._multiteamsession.ShuffleList(content, shuffle=False)
session._playlist = playlist
set_next_map(session, playlist.pull_next())
def set_next_map(session, game_map):
session._next_game_spec = game_map
with babase.Context(session):
session._instantiate_next_game()
def playlist(code):
bui.app.plus.add_v1_account_transaction(
{
'type': 'IMPORT_PLAYLIST',
'code': str(code),
'overwrite': True
},
callback=set_playlist)
bui.app.plus.run_v1_account_transactions()
def setPlaylist(para):
if para.isdigit():
playlist(para)
elif para=="coop":
_thread.start_new_thread(withDelay,(CoopSession,None,))
elif para in settings["playlists"]:
playlist(settings["playlists"][para])
else:
bs.chatmessage("Available Playlist")
for play in settings["playlists"]:
bs.chatmessage(play)
def flush_playlists():
print("Clearing old playlists..")
for playlist in _babase.app.config["Team Tournament Playlists"]:
bui.app.plus.add_v1_account_transaction(
{
"type":"REMOVE_PLAYLIST",
"playlistType":"Team Tournament",
"playlistName":playlist
})
bui.app.plus.run_v1_account_transactions()
for playlist in _babase.app.config["Free-for-All Playlists"]:
bui.app.plus.add_v1_account_transaction(
{
"type":"REMOVE_PLAYLIST",
"playlistType":"Free-for-All",
"playlistName":playlist
})
bui.app.plus.run_v1_account_transactions()

420
dist/ba_root/mods/tools/servercheck.py vendored Normal file
View file

@ -0,0 +1,420 @@
# Released under the MIT License. See LICENSE for details.
from serverData import serverdata
from playersData import pdata
import _babaseimport babase.internal
import urllib.request
import json
from datetime import datetime, timedelta
import time
import babase
import bauiv1 as bui
import bascenev1 as bs
from babase._general import Call
import threading
import setting
import _thread
from tools import logger
from features import profanity
from playersData import pdata
blacklist = pdata.get_blacklist()
settings = setting.get_settings_data()
ipjoin = {}
class checkserver(object):
def start(self):
self.players = []
self.t1 = bs.Timer(1, babase.Call(self.check),
repeat=True, babase.TimeType.REAL)
def check(self):
newPlayers = []
ipClientMap = {}
deviceClientMap = {}
for ros in babase.internal.get_game_roster():
ip = _babase.get_client_ip(ros["client_id"])
device_id = _babase.get_client_public_device_uuid(ros["client_id"])
if (device_id == None):
device_id = _babase.get_client_device_uuid(ros["client_id"])
if device_id not in deviceClientMap:
deviceClientMap[device_id] = [ros["client_id"]]
else:
deviceClientMap[device_id].append(ros["client_id"])
if len(deviceClientMap[device_id]) >= settings['maxAccountPerIP']:
_babase.chatmessage(f"Only {settings['maxAccountPerIP']} player per IP allowed, disconnecting this device.", clients=[
ros["client_id"]])
babase.internal.disconnect_client(ros["client_id"])
logger.log(f'Player disconnected, reached max players per device || {ros["account_id"]}',
"playerjoin")
continue
if ip not in ipClientMap:
ipClientMap[ip] = [ros["client_id"]]
else:
ipClientMap[ip].append(ros["client_id"])
if len(ipClientMap[ip]) >= settings['maxAccountPerIP']:
_babase.chatmessage(f"Only {settings['maxAccountPerIP']} player per IP allowed, disconnecting this device.", clients=[
ros["client_id"]])
babase.internal.disconnect_client(ros["client_id"])
logger.log(f'Player disconnected, reached max players per IP address || {ros["account_id"]}',
"playerjoin")
continue
newPlayers.append(ros['account_id'])
if ros['account_id'] not in self.players and ros[
'client_id'] != -1:
# new player joined lobby
d_str = ros['display_string']
d_str2 = profanity.censor(d_str)
try:
logger.log(
f'{d_str} || {ros["account_id"]} || joined server',
"playerjoin")
logger.log(f'{ros["account_id"]} {ip} {device_id}')
except:
pass
if d_str2 != d_str:
_bs.broadcastmessage(
"Profanity in Id , change your ID and join back",
color=(1, 0, 0), transient=True,
clients=[ros['client_id']])
try:
logger.log(f'{d_str} || { ros["account_id"] } || kicked by profanity check',
"sys")
except:
pass
babase.internal.disconnect_client(ros['client_id'], 1)
return
if settings["whitelist"] and ros["account_id"] != None:
if ros["account_id"] not in pdata.CacheData.whitelist:
_bs.broadcastmessage("Not in whitelist,contact admin",
color=(1, 0, 0), transient=True,
clients=[ros['client_id']])
logger.log(
f'{d_str} || { ros["account_id"]} | kicked > not in whitelist')
babase.internal.disconnect_client(ros['client_id'])
return
if ros['account_id'] != None:
if ros['account_id'] in serverdata.clients:
on_player_join_server(ros['account_id'],
serverdata.clients[
ros['account_id']], ip, device_id)
else:
# from local cache, then call on_player_join_server
LoadProfile(ros['account_id'], ip, device_id).start()
self.players = newPlayers
def on_player_join_server(pbid, player_data, ip, device_id):
global ipjoin
now = time.time()
# player_data=pdata.get_info(pbid)
clid = 113
device_string = ""
for ros in babase.internal.get_game_roster():
if ros["account_id"] == pbid:
clid = ros["client_id"]
device_string = ros['display_string']
if ip in ipjoin:
lastjoin = ipjoin[ip]["lastJoin"]
joincount = ipjoin[ip]["count"]
if now - lastjoin < 15:
joincount += 1
if joincount > 2:
_bs.broadcastmessage("Joining too fast , slow down dude", # its not possible now tho, network layer will catch it before reaching here
color=(1, 0, 1), transient=True,
clients=[clid])
logger.log(f'{pbid} || kicked for joining too fast')
babase.internal.disconnect_client(clid)
_thread.start_new_thread(reportSpam, (pbid,))
return
else:
joincount = 0
ipjoin[ip]["count"] = joincount
ipjoin[ip]["lastJoin"] = now
else:
ipjoin[ip] = {"lastJoin": now, "count": 0}
if pbid in serverdata.clients:
serverdata.clients[pbid]["lastJoin"] = now
if player_data != None: # player data is in serevrdata or in local.json cache
serverdata.recents.append(
{"client_id": clid, "deviceId": device_string, "pbid": pbid})
serverdata.recents = serverdata.recents[-20:]
if check_ban(ip, device_id, pbid):
_babase.chatmessage(
'sad ,your account is flagged contact server owner for unban', clients=[clid])
babase.internal.disconnect_client(clid)
return
if get_account_age(player_data["accountAge"]) < \
settings["minAgeToJoinInHours"]:
for ros in babase.internal.get_game_roster():
if ros['account_id'] == pbid:
_bs.broadcastmessage(
"New Accounts not allowed here , come back later",
color=(1, 0, 0), transient=True,
clients=[ros['client_id']])
logger.log(pbid + " | kicked > reason:Banned account")
babase.internal.disconnect_client(ros['client_id'])
return
else:
current_time = datetime.now()
if pbid not in serverdata.clients:
# ahh , lets reset if plyer joining after some long time
serverdata.clients[pbid] = player_data
serverdata.clients[pbid]["warnCount"] = 0
serverdata.clients[pbid]["lastWarned"] = time.time()
serverdata.clients[pbid]["verified"] = False
serverdata.clients[pbid]["rejoincount"] = 1
serverdata.clients[pbid]["lastJoin"] = time.time()
if pbid in blacklist["kick-vote-disabled"] and current_time < datetime.strptime(blacklist["kick-vote-disabled"][pbid]["till"], "%Y-%m-%d %H:%M:%S"):
_babase.disable_kickvote(pbid)
serverdata.clients[pbid]["lastIP"] = ip
device_id = _babase.get_client_public_device_uuid(clid)
if (device_id == None):
device_id = _babase.get_client_device_uuid(clid)
serverdata.clients[pbid]["deviceUUID"] = device_id
verify_account(pbid, player_data) # checked for spoofed ids
logger.log(
f'{pbid} ip: {serverdata.clients[pbid]["lastIP"]} , Device id: {device_id}')
_bs.broadcastmessage(settings["regularWelcomeMsg"] + " " + device_string,
color=(0.60, 0.8, 0.6), transient=True,
clients=[clid])
if (settings["ballistica_web"]["enable"]):
from . import notification_manager
notification_manager.player_joined(pbid)
else:
# fetch id for first time.
thread = FetchThread(
target=my_acc_age,
callback=save_age,
pb_id=pbid,
display_string=device_string
)
thread.start()
_bs.broadcastmessage(settings["firstTimeJoinMsg"], color=(0.6, 0.8, 0.6),
transient=True, clients=[clid])
if (settings["ballistica_web"]["enable"]):
from . import notification_manager
notification_manager.player_joined(pbid)
# pdata.add_profile(pbid,d_string,d_string)
def check_ban(ip, device_id, pbid, log=True):
current_time = datetime.now()
if ip in blacklist["ban"]['ips'] and current_time < datetime.strptime(blacklist["ban"]["ips"][ip]["till"], "%Y-%m-%d %H:%M:%S"):
msg = f' reason: matched IP | {blacklist["ban"]["ips"][ip]["reason"]} , Till : {blacklist["ban"]["ips"][ip]["till"]}'
if log:
logger.log(f'{pbid} | kicked > {msg}')
return True
return msg
elif device_id in blacklist["ban"]["deviceids"] and current_time < datetime.strptime(blacklist["ban"]["deviceids"][device_id]["till"], "%Y-%m-%d %H:%M:%S"):
msg = f'reason: matched deviceId | {blacklist["ban"]["deviceids"][device_id]["reason"]}, Till : {blacklist["ban"]["deviceids"][device_id]["till"]}'
if log:
logger.log(
f'{pbid} | kicked > {msg}')
return True
return msg
elif pbid in blacklist["ban"]["ids"] and current_time < datetime.strptime(blacklist["ban"]["ids"][pbid]["till"], "%Y-%m-%d %H:%M:%S"):
msg = f'reason: matched ID | {blacklist["ban"]["ids"][pbid]["reason"]} , Till : {blacklist["ban"]["ids"][pbid]["till"]}'
if log:
logger.log(
f'{pbid} | kicked > {msg}')
return True
return msg
return False
def verify_account(pb_id, p_data):
d_string = ""
for ros in babase.internal.get_game_roster():
if ros['account_id'] == pb_id:
d_string = ros['display_string']
if d_string not in p_data['display_string']:
thread2 = FetchThread(
target=get_device_accounts,
callback=save_ids,
pb_id=pb_id,
display_string=d_string
)
thread2.start()
else:
serverdata.clients[pb_id]["verified"] = True
# ============== IGNORE BELOW CODE =======================
def _make_request_safe(request, retries=2, raise_err=True):
try:
return request()
except:
if retries > 0:
time.sleep(1)
return _make_request_safe(request, retries=retries - 1,
raise_err=raise_err)
if raise_err:
raise
def get_account_creation_date(pb_id):
# thanks rikko
account_creation_url = "http://bombsquadgame.com/accountquery?id=" + pb_id
account_creation = _make_request_safe(
lambda: urllib.request.urlopen(account_creation_url))
if account_creation is not None:
try:
account_creation = json.loads(account_creation.read())
except ValueError:
pass
else:
creation_time = account_creation["created"]
creation_time = map(str, creation_time)
creation_time = datetime.strptime("/".join(creation_time),
"%Y/%m/%d/%H/%M/%S")
# Convert to IST
creation_time += timedelta(hours=5, minutes=30)
return str(creation_time)
def get_device_accounts(pb_id):
url = "http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID=" + pb_id
data = _make_request_safe(lambda: urllib.request.urlopen(url))
if data is not None:
try:
accounts = json.loads(data.read())["accountDisplayStrings"]
except ValueError:
return ['???']
else:
return accounts
# ======= yes fucking threading code , dont touch ==============
# ============ file I/O =============
class LoadProfile(threading.Thread):
def __init__(self, pb_id, ip, device_id):
threading.Thread.__init__(self)
self.pbid = pb_id
self.ip = ip
self.device_id = device_id
def run(self):
player_data = pdata.get_info(self.pbid)
_babase.pushcall(Call(on_player_join_server, self.pbid, player_data, self.ip, self.device_id),
from_other_thread=True)
# ================ http ================
class FetchThread(threading.Thread):
def __init__(self, target, callback=None, pb_id="ji",
display_string="XXX"):
super(FetchThread, self).__init__(target=self.target_with_callback,
args=(pb_id, display_string,))
self.callback = callback
self.method = target
def target_with_callback(self, pb_id, display_string):
data = self.method(pb_id)
if self.callback is not None:
self.callback(data, pb_id, display_string)
def my_acc_age(pb_id):
return get_account_creation_date(pb_id)
def save_age(age, pb_id, display_string):
_babase.pushcall(Call(pdata.add_profile, pb_id, display_string,
display_string, age), from_other_thread=True)
time.sleep(2)
thread2 = FetchThread(
target=get_device_accounts,
callback=save_ids,
pb_id=pb_id,
display_string=display_string
)
thread2.start()
if get_account_age(age) < settings["minAgeToJoinInHours"]:
msg = "New Accounts not allowed to play here , come back tmrw."
logger.log(pb_id + "|| kicked > new account")
_babase.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True)
def save_ids(ids, pb_id, display_string):
pdata.update_display_string(pb_id, ids)
if display_string not in ids:
msg = "Spoofed Id detected , Goodbye"
_babase.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True)
serverdata.clients[pb_id]["verified"] = False
logger.log(
pb_id + "|| kicked , for using spoofed id " + display_string)
else:
serverdata.clients[pb_id]["verified"] = True
def kick_by_pb_id(pb_id, msg):
for ros in babase.internal.get_game_roster():
if ros['account_id'] == pb_id:
_bs.broadcastmessage(msg, transient=True, clients=[ros['client_id']])
babase.internal.disconnect_client(ros['client_id'])
def get_account_age(ct):
creation_time = datetime.strptime(ct, "%Y-%m-%d %H:%M:%S")
now = datetime.now()
delta = now - creation_time
delta_hours = delta.total_seconds() / (60 * 60)
return delta_hours
def reportSpam(id):
now = time.time()
profiles = pdata.get_profiles()
if id in profiles:
count = profiles[id]["spamCount"]
if now - profiles[id]["lastSpam"] < 2 * 24 * 60 * 60:
count += 1
if count > 3:
logger.log(id+" auto banned for spamming")
# by default ban for 1 day , change here if you want
pdata.ban_player(id, 1, "auto ban exceed warn count")
else:
count = 0
profiles[id]["spamCount"] = count
profiles[id]["lastSpam"] = now
def on_join_request(ip):
now = time.time()
if ip in serverdata.ips:
lastRequest = serverdata.ips[ip]["lastRequest"]
count = serverdata.ips[ip]["count"]
if now - lastRequest < 5:
count += 1
if count > 40:
_babase.ban_ip(ip)
else:
count = 0
serverdata.ips[ip] = {"lastRequest": time.time(), "count": count}
else:
serverdata.ips[ip] = {"lastRequest": time.time(), "count": 0}

View file

@ -0,0 +1,113 @@
from typing import TYPE_CHECKING
from efro.terminal import Clr
import _babaseimport ba
if TYPE_CHECKING:
from typing import Any
def _access_check_response(self, data) -> None:
if data is None:
print('error on UDP port access check (internet down?)')
else:
addr = data['address']
port = data['port']
addrstr = f' {addr}'
poststr = ''
_babase.our_ip = addr
_babase.our_port = port
if data['accessible']:
# _fetch_public_servers()
_babase.queue_chcker_timer = bs.Timer(8, babase.Call(simple_queue_checker), repeat=True, babase.TimeType.REAL)
print(
f'{Clr.SBLU}Master server access check of{addrstr}'
f' udp port {port} succeeded.\n'
f'Your server appears to be'
f' joinable from the internet .{poststr}{Clr.RST}'
)
if self._config.party_is_public:
print(
f'{Clr.SBLU}Your party {self._config.party_name}'
f' visible in public party list.{Clr.RST}'
)
else:
print(
f'{Clr.SBLU}Your private party {self._config.party_name}'
f'can be joined by {addrstr} {port}.{Clr.RST}'
)
else:
print(
f'{Clr.SRED}Master server access check of{addrstr}'
f' udp port {port} failed.\n'
f'Your server does not appear to be'
f' joinable from the internet. Please check your firewall or instance security group.{poststr}{Clr.RST}'
)
def _fetch_public_servers():
bui.app.plus.add_v1_account_transaction(
{
'type': 'PUBLIC_PARTY_QUERY',
'proto': babase.app.protocol_version,
'lang': bs.app.lang.language,
},
callback=babase.Call(_on_public_party_response),
)
bui.app.plus.run_v1_account_transactions()
def _on_public_party_response(result):
if result is None:
return
parties_in = result['l']
queue_id = None
for party_in in parties_in:
addr = party_in['a']
assert isinstance(addr, str)
port = party_in['p']
assert isinstance(port, int)
if addr == _babase.our_ip and str(port) == str(_babase.our_port):
queue_id = party_in['q']
# aah sad , public party result dont include our own server
if queue_id:
_babase.our_queue_id = queue_id
_babase.queue_chcker_timer = bs.timer(6, babase.Call(check_queue), repeat=True)
else:
print("Something is wrong , why our server is not in public list.")
def check_queue():
bui.app.plus.add_v1_account_transaction(
{'type': 'PARTY_QUEUE_QUERY', 'q': _babase.our_queue_id},
callback=babase.Call(on_update_response),
)
bui.app.plus.run_v1_account_transactions()
# lets dont spam our own queue
bui.app.plus.add_v1_account_transaction(
{'type': 'PARTY_QUEUE_REMOVE', 'q': _babase.our_queue_id}
)
bui.app.plus.run_v1_account_transactions()
def on_update_response(response):
allowed_to_join = response["c"]
players_in_queue = response["e"]
max_allowed_in_server = _babase.app.server._config.max_party_size
current_players = len(_babase.get_game_roster())
# print(allowed_to_join)
if allowed_to_join:
# looks good , yipee
_babase.set_public_party_queue_enabled(True)
return
if not allowed_to_join and len(players_in_queue) > 1 and current_players < max_allowed_in_server:
# something is wrong , lets disable queue for some time
_babase.set_public_party_queue_enabled(False)
def simple_queue_checker():
max_allowed_in_server = _babase.app.server._config.max_party_size
current_players = len(_babase.get_game_roster())
if current_players < max_allowed_in_server:
_babase.set_public_party_queue_enabled(False)
else:
_babase.set_public_party_queue_enabled(True)