mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2025-10-20 00:00:39 +00:00
bugs fix
This commit is contained in:
parent
14007f0b0e
commit
e9f6da8a2e
13 changed files with 76 additions and 70 deletions
|
|
@ -70,8 +70,8 @@ class PlayerSpaz(Spaz):
|
||||||
self.last_player_held_by: Optional[ba.Player] = None
|
self.last_player_held_by: Optional[ba.Player] = None
|
||||||
self._player = player
|
self._player = player
|
||||||
self._drive_player_position()
|
self._drive_player_position()
|
||||||
import custom_hooks
|
from spazmod import modifyspaz
|
||||||
custom_hooks.playerspaz_init(self._player)
|
modifyspaz.main(self.node,self._player)
|
||||||
|
|
||||||
# Overloads to tell the type system our return type based on doraise val.
|
# Overloads to tell the type system our return type based on doraise val.
|
||||||
|
|
||||||
|
|
|
||||||
2
dist/ba_root/mods/chatHandle/handlechat.py
vendored
2
dist/ba_root/mods/chatHandle/handlechat.py
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
from playersData import pdata
|
from playersData import pdata
|
||||||
from chatCMDS import chatcmd
|
from chatHandle.chatCMDS import chatcmd
|
||||||
from chatFilter import chatfilter
|
from chatFilter import chatfilter
|
||||||
import ba,_ba
|
import ba,_ba
|
||||||
|
|
||||||
|
|
|
||||||
6
dist/ba_root/mods/custom_hooks.py
vendored
6
dist/ba_root/mods/custom_hooks.py
vendored
|
|
@ -1,17 +1,17 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def filter_chat_message(msg,client_id):
|
def filter_chat_message(msg,client_id):
|
||||||
from chatHandle import handlechat
|
from chatHandle import handlechat
|
||||||
|
|
||||||
return handlechat.filter_chat_message(msg,client_id)
|
return handlechat.filter_chat_message(msg,client_id)
|
||||||
|
|
||||||
def on_app_launch():
|
def on_app_launch():
|
||||||
|
pass
|
||||||
#something
|
#something
|
||||||
|
|
||||||
def score_screen_on_begin(_stats):
|
def score_screen_on_begin(_stats):
|
||||||
|
pass
|
||||||
#stats
|
#stats
|
||||||
|
|
||||||
def playerspaz_init(player):
|
def playerspaz_init(player):
|
||||||
|
pass
|
||||||
#add tag,rank,effect
|
#add tag,rank,effect
|
||||||
|
|
||||||
|
|
|
||||||
36
dist/ba_root/mods/playersData/pdata.py
vendored
36
dist/ba_root/mods/playersData/pdata.py
vendored
|
|
@ -1,12 +1,14 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
import os,_ba,json
|
||||||
roles={}
|
roles={}
|
||||||
data={}
|
data={}
|
||||||
custom={}
|
custom={}
|
||||||
|
data_path = os.path.join(_ba.env()['python_directory_user'],"playersData" + os.sep)
|
||||||
|
|
||||||
def roles():
|
def get_roles():
|
||||||
global roles
|
global roles
|
||||||
if roles=={}:
|
if roles=={}:
|
||||||
f=open("roles.json","r")
|
f=open(data_path+"roles.json","r")
|
||||||
dat=json.loads(f.read())
|
dat=json.loads(f.read())
|
||||||
roles=dat
|
roles=dat
|
||||||
f.close()
|
f.close()
|
||||||
|
|
@ -14,7 +16,7 @@ def roles():
|
||||||
|
|
||||||
def create_role(role):
|
def create_role(role):
|
||||||
global roles
|
global roles
|
||||||
_roles=roles()
|
_roles=get_roles()
|
||||||
if role not in _roles:
|
if role not in _roles:
|
||||||
_roles[role]={
|
_roles[role]={
|
||||||
"tag":role,
|
"tag":role,
|
||||||
|
|
@ -31,7 +33,7 @@ def create_role(role):
|
||||||
|
|
||||||
def add_player_role(role,id):
|
def add_player_role(role,id):
|
||||||
global roles
|
global roles
|
||||||
_roles=roles()
|
_roles=get_roles()
|
||||||
if role in _roles:
|
if role in _roles:
|
||||||
_roles[role].ids.append(id)
|
_roles[role].ids.append(id)
|
||||||
roles=_roles
|
roles=_roles
|
||||||
|
|
@ -41,7 +43,7 @@ def add_player_role(role,id):
|
||||||
|
|
||||||
def add_command_role(role,command):
|
def add_command_role(role,command):
|
||||||
global roles
|
global roles
|
||||||
_roles=roles()
|
_roles=get_roles()
|
||||||
if role in _roles:
|
if role in _roles:
|
||||||
_roles[role].commands.append(command)
|
_roles[role].commands.append(command)
|
||||||
roles=_roles
|
roles=_roles
|
||||||
|
|
@ -52,7 +54,7 @@ def add_command_role(role,command):
|
||||||
|
|
||||||
def remove_player_role(role,id):
|
def remove_player_role(role,id):
|
||||||
global roles
|
global roles
|
||||||
_roles=roles()
|
_roles=get_roles()
|
||||||
if role in _roles:
|
if role in _roles:
|
||||||
_roles[role].ids.remove(id)
|
_roles[role].ids.remove(id)
|
||||||
roles=_roles
|
roles=_roles
|
||||||
|
|
@ -62,7 +64,7 @@ def remove_player_role(role,id):
|
||||||
|
|
||||||
def remove_command_role():
|
def remove_command_role():
|
||||||
global roles
|
global roles
|
||||||
_roles=roles()
|
_roles=get_roles()
|
||||||
if role in _roles:
|
if role in _roles:
|
||||||
_roles[role].commands.remove(command)
|
_roles[role].commands.remove(command)
|
||||||
roles=_roles
|
roles=_roles
|
||||||
|
|
@ -72,7 +74,7 @@ def remove_command_role():
|
||||||
|
|
||||||
def change_role_tag(role,tag):
|
def change_role_tag(role,tag):
|
||||||
global roles
|
global roles
|
||||||
_roles=roles()
|
_roles=get_roles()
|
||||||
if role in _roles:
|
if role in _roles:
|
||||||
_roles[role].tag=tag
|
_roles[role].tag=tag
|
||||||
roles=_roles
|
roles=_roles
|
||||||
|
|
@ -85,14 +87,14 @@ def commit(_roles):
|
||||||
global roles
|
global roles
|
||||||
if _roles=={}:
|
if _roles=={}:
|
||||||
return
|
return
|
||||||
f=open("roles.json",'w')
|
f=open(data_path+"roles.json",'w')
|
||||||
json.dump(_roles,f,indent=4)
|
json.dump(_roles,f,indent=4)
|
||||||
f.close()
|
f.close()
|
||||||
roles=_roles
|
roles=_roles
|
||||||
|
|
||||||
def get_role(acc_id):
|
def get_role(acc_id):
|
||||||
global roles
|
global roles
|
||||||
_roles =roles()
|
_roles =get_roles()
|
||||||
for role in _roles:
|
for role in _roles:
|
||||||
if acc_id in role["ids"]:
|
if acc_id in role["ids"]:
|
||||||
return role
|
return role
|
||||||
|
|
@ -100,10 +102,10 @@ def get_role(acc_id):
|
||||||
#======================= CUSTOM EFFECTS/TAGS ===============
|
#======================= CUSTOM EFFECTS/TAGS ===============
|
||||||
|
|
||||||
|
|
||||||
def custom():
|
def get_custom():
|
||||||
global custom
|
global custom
|
||||||
if custom=={}:
|
if custom=={}:
|
||||||
f=open("custom.json","r")
|
f=open(data_path+"custom.json","r")
|
||||||
dat=json.loads(f.read())
|
dat=json.loads(f.read())
|
||||||
custom=dat
|
custom=dat
|
||||||
f.close()
|
f.close()
|
||||||
|
|
@ -113,7 +115,7 @@ def custom():
|
||||||
|
|
||||||
def set_effect(effect,id):
|
def set_effect(effect,id):
|
||||||
global custom
|
global custom
|
||||||
_custom=custom()
|
_custom=get_custom()
|
||||||
_custom['customeffects'][id]=effect
|
_custom['customeffects'][id]=effect
|
||||||
custom=_custom
|
custom=_custom
|
||||||
commit_c()
|
commit_c()
|
||||||
|
|
@ -121,14 +123,14 @@ def set_effect(effect,id):
|
||||||
|
|
||||||
def set_tag(tag,id):
|
def set_tag(tag,id):
|
||||||
global custom
|
global custom
|
||||||
_custom=custom()
|
_custom=get_custom()
|
||||||
_custom['customtag'][id]=tag
|
_custom['customtag'][id]=tag
|
||||||
custom=_custom
|
custom=_custom
|
||||||
commit_c()
|
commit_c()
|
||||||
|
|
||||||
def remove_effect(id):
|
def remove_effect(id):
|
||||||
global custom
|
global custom
|
||||||
_custom=custom()
|
_custom=get_custom()
|
||||||
_custom['customeffects'].pop(id)
|
_custom['customeffects'].pop(id)
|
||||||
custom=_custom
|
custom=_custom
|
||||||
commit_c()
|
commit_c()
|
||||||
|
|
@ -136,7 +138,7 @@ def remove_effect(id):
|
||||||
|
|
||||||
def remove_tag(id):
|
def remove_tag(id):
|
||||||
global custom
|
global custom
|
||||||
_custom=custom()
|
_custom=get_custom()
|
||||||
_custom['customtag'].pop(id)
|
_custom['customtag'].pop(id)
|
||||||
custom=_custom
|
custom=_custom
|
||||||
commit_c()
|
commit_c()
|
||||||
|
|
@ -144,6 +146,6 @@ def remove_tag(id):
|
||||||
|
|
||||||
def commit_c():
|
def commit_c():
|
||||||
global custom
|
global custom
|
||||||
f=open("custom.json",'w')
|
f=open(data_path+"custom.json",'w')
|
||||||
json.dump(custom,f,indent=4)
|
json.dump(custom,f,indent=4)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
|
||||||
2
dist/ba_root/mods/playersData/roles.json
vendored
2
dist/ba_root/mods/playersData/roles.json
vendored
|
|
@ -18,7 +18,7 @@
|
||||||
"commands":[],
|
"commands":[],
|
||||||
"ids":[]
|
"ids":[]
|
||||||
},
|
},
|
||||||
"donor":{
|
"toppers":{
|
||||||
"tag":"DONOR",
|
"tag":"DONOR",
|
||||||
"tagcolor":(2,2,2),
|
"tagcolor":(2,2,2),
|
||||||
"commands":[],
|
"commands":[],
|
||||||
|
|
|
||||||
4
dist/ba_root/mods/setting.json
vendored
4
dist/ba_root/mods/setting.json
vendored
|
|
@ -5,5 +5,7 @@
|
||||||
"bottom left watermark":"join discord for fun",
|
"bottom left watermark":"join discord for fun",
|
||||||
"center highlights":["message 1","message 2","message 3"]
|
"center highlights":["message 1","message 2","message 3"]
|
||||||
},
|
},
|
||||||
"enabletags":true
|
"enabletags":true,
|
||||||
|
"enablerank":true,
|
||||||
|
"enableeffects":false
|
||||||
}
|
}
|
||||||
13
dist/ba_root/mods/setting.py
vendored
13
dist/ba_root/mods/setting.py
vendored
|
|
@ -1,20 +1,19 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
import ba,_ba,json,os
|
import ba,_ba,json,os
|
||||||
from stats import mystats
|
settingjson = os.path.join(_ba.env()['python_directory_user'],"setting.json")
|
||||||
statsFile = mystats.statsfile
|
|
||||||
|
|
||||||
def get_setting():
|
def get_setting():
|
||||||
s = {}
|
s = {}
|
||||||
f=open("setting.json","r")
|
f=open(settingjson,"r")
|
||||||
d = json.loads(f.read())
|
d = json.loads(f.read())
|
||||||
f.close()
|
f.close()
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def commit(updated_settings: dict):
|
def commit(updated_settings: dict):
|
||||||
if updated_settings == {}: return
|
if updated_settings == {}: return
|
||||||
f=open("setting.json",'w')
|
f=open(settingjson,'w')
|
||||||
json.dump(updated_settings,f,indent=4)
|
json.dump(updated_settings,f,indent=4)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def sendError(msg: str, ID: int = None):
|
def sendError(msg: str, ID: int = None):
|
||||||
if ID is not None:
|
if ID is not None:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
#
|
|
||||||
"""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.
|
|
||||||
"""
|
|
||||||
4
dist/ba_root/mods/spazmod/modifyspaz.py
vendored
4
dist/ba_root/mods/spazmod/modifyspaz.py
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
import tag
|
from spazmod import tag
|
||||||
import effects
|
from spazmod import effects
|
||||||
import setting
|
import setting
|
||||||
|
|
||||||
# all activites related to modify spaz by any how will be here
|
# all activites related to modify spaz by any how will be here
|
||||||
|
|
|
||||||
35
dist/ba_root/mods/spazmod/tag.py
vendored
35
dist/ba_root/mods/spazmod/tag.py
vendored
|
|
@ -1,11 +1,12 @@
|
||||||
|
|
||||||
import pdata
|
from playersData import pdata
|
||||||
|
import ba
|
||||||
def addtag(node,player):
|
def addtag(node,player):
|
||||||
session_player=player.sessionplayer
|
session_player=player.sessionplayer
|
||||||
account_id=session_player.get_account_id()
|
account_id=session_player.get_account_id()
|
||||||
customtag=pdata.custom()['customtag']
|
customtag_=pdata.get_custom()
|
||||||
roles=pdata.roles()
|
customtag=customtag_['customtag']
|
||||||
|
roles=pdata.get_roles()
|
||||||
role=pdata.get_role(account_id)
|
role=pdata.get_role(account_id)
|
||||||
tag=None
|
tag=None
|
||||||
if account_id in customtag:
|
if account_id in customtag:
|
||||||
|
|
@ -13,12 +14,19 @@ def addtag(node,player):
|
||||||
elif role:
|
elif role:
|
||||||
tag=roles[role]['tag']
|
tag=roles[role]['tag']
|
||||||
|
|
||||||
tag(node,tag)
|
Tag(node,tag)
|
||||||
|
from stats import mystats
|
||||||
def addrank(node,player):
|
def addrank(node,player):
|
||||||
|
session_player=player.sessionplayer
|
||||||
|
account_id=session_player.get_account_id()
|
||||||
|
rank=mystats.getRank(account_id)
|
||||||
|
|
||||||
class tag(object):
|
if rank:
|
||||||
def __init__(owner=None,tag="somthing"):
|
Rank(node,rank)
|
||||||
|
|
||||||
|
class Tag(object):
|
||||||
|
def __init__(self,owner=None,tag="somthing"):
|
||||||
|
self.node=owner
|
||||||
mnode = ba.newnode('math',
|
mnode = ba.newnode('math',
|
||||||
owner=self.node,
|
owner=self.node,
|
||||||
attrs={
|
attrs={
|
||||||
|
|
@ -34,17 +42,18 @@ class tag(object):
|
||||||
'shadow': 1.0,
|
'shadow': 1.0,
|
||||||
'flatness': 1.0,
|
'flatness': 1.0,
|
||||||
'color': (1,1,1),
|
'color': (1,1,1),
|
||||||
'scale': 0.02,
|
'scale': 0.01,
|
||||||
'h_align': 'center'
|
'h_align': 'center'
|
||||||
})
|
})
|
||||||
mnode.connectattr('output', self.tag_text, 'position')
|
mnode.connectattr('output', self.tag_text, 'position')
|
||||||
|
|
||||||
class rank(object):
|
class Rank(object):
|
||||||
def __init__(owner=None,rank=1):
|
def __init__(self,owner=None,rank=99):
|
||||||
|
self.node=owner
|
||||||
mnode = ba.newnode('math',
|
mnode = ba.newnode('math',
|
||||||
owner=self.node,
|
owner=self.node,
|
||||||
attrs={
|
attrs={
|
||||||
'input1': (0, 1.4, 0),
|
'input1': (0, 1.2, 0),
|
||||||
'operation': 'add'
|
'operation': 'add'
|
||||||
})
|
})
|
||||||
self.node.connectattr('torso_position', mnode, 'input2')
|
self.node.connectattr('torso_position', mnode, 'input2')
|
||||||
|
|
@ -56,7 +65,7 @@ class rank(object):
|
||||||
'shadow': 1.0,
|
'shadow': 1.0,
|
||||||
'flatness': 1.0,
|
'flatness': 1.0,
|
||||||
'color': (1,1,1),
|
'color': (1,1,1),
|
||||||
'scale': 0.02,
|
'scale': 0.01,
|
||||||
'h_align': 'center'
|
'h_align': 'center'
|
||||||
})
|
})
|
||||||
mnode.connectattr('output', self.rank_text, 'position')
|
mnode.connectattr('output', self.rank_text, 'position')
|
||||||
|
|
|
||||||
7
dist/ba_root/mods/spazmod/tag/__init__.py
vendored
7
dist/ba_root/mods/spazmod/tag/__init__.py
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
#
|
|
||||||
"""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.
|
|
||||||
"""
|
|
||||||
20
dist/ba_root/mods/stats/mystats.py
vendored
20
dist/ba_root/mods/stats/mystats.py
vendored
|
|
@ -31,7 +31,7 @@ html_start = f'''<!DOCTYPE html>
|
||||||
<style>table{table_style} th{heading_style}</style>
|
<style>table{table_style} th{heading_style}</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h3 style="text-align:center">Top 200 Players of {our_settings['server_name']}</h3>
|
<h3 style="text-align:center">Top 200 Players </h3>
|
||||||
<table border=1>
|
<table border=1>
|
||||||
<tr>
|
<tr>
|
||||||
<th><b>Rank</b></th>
|
<th><b>Rank</b></th>
|
||||||
|
|
@ -46,7 +46,9 @@ html_start = f'''<!DOCTYPE html>
|
||||||
def refreshStats():
|
def refreshStats():
|
||||||
# lastly, write a pretty html version.
|
# lastly, write a pretty html version.
|
||||||
# our stats url could point at something like this...
|
# our stats url could point at something like this...
|
||||||
stats = setting.getStats()
|
f=open(statsfile)
|
||||||
|
stats = json.loads(f.read())
|
||||||
|
|
||||||
f=open(htmlfile, 'w')
|
f=open(htmlfile, 'w')
|
||||||
f.write(html_start)
|
f.write(html_start)
|
||||||
entries = [(a['scores'], a['kills'], a['deaths'], a['games'], a['name_html'], a['aid']) for a in stats.values()]
|
entries = [(a['scores'], a['kills'], a['deaths'], a['games'], a['name_html'], a['aid']) for a in stats.values()]
|
||||||
|
|
@ -116,12 +118,13 @@ def refreshStats():
|
||||||
f.close()
|
f.close()
|
||||||
global ranks
|
global ranks
|
||||||
ranks=_ranks
|
ranks=_ranks
|
||||||
|
|
||||||
f2 = open(statsfile, "w")
|
f2 = open(statsfile, "w")
|
||||||
f2.write(json.dumps(pStats, indent=4))
|
f2.write(json.dumps(pStats, indent=4))
|
||||||
f2.close()
|
f2.close()
|
||||||
|
|
||||||
from playersData import pdata
|
from playersData import pdata
|
||||||
pdata.update_toppers(toppersIDS)
|
pdata.update_toppers(toppersIDs)
|
||||||
|
|
||||||
def update(score_set):
|
def update(score_set):
|
||||||
"""
|
"""
|
||||||
|
|
@ -149,7 +152,10 @@ def update(score_set):
|
||||||
# from disk, do display-string lookups for accounts that need them,
|
# from disk, do display-string lookups for accounts that need them,
|
||||||
# and write everything back to disk (along with a pretty html version)
|
# 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.
|
# 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()
|
print(account_kills)
|
||||||
|
print(account_scores)
|
||||||
|
if account_scores:
|
||||||
|
UpdateThread(account_kills, account_deaths, account_scores).start()
|
||||||
|
|
||||||
class UpdateThread(threading.Thread):
|
class UpdateThread(threading.Thread):
|
||||||
def __init__(self, account_kills, account_deaths, account_scores):
|
def __init__(self, account_kills, account_deaths, account_scores):
|
||||||
|
|
@ -157,14 +163,16 @@ class UpdateThread(threading.Thread):
|
||||||
self._account_kills = account_kills
|
self._account_kills = account_kills
|
||||||
self.account_deaths = account_deaths
|
self.account_deaths = account_deaths
|
||||||
self.account_scores = account_scores
|
self.account_scores = account_scores
|
||||||
|
print("init thread")
|
||||||
def run(self):
|
def run(self):
|
||||||
# pull our existing stats from disk
|
# pull our existing stats from disk
|
||||||
|
print("run thead")
|
||||||
try:
|
try:
|
||||||
if os.path.exists(statsfile):
|
if os.path.exists(statsfile):
|
||||||
with open(statsfile) as f:
|
with open(statsfile) as f:
|
||||||
stats = json.loads(f.read())
|
stats = json.loads(f.read())
|
||||||
except:
|
except:
|
||||||
return
|
stats={}
|
||||||
|
|
||||||
# now add this batch of kills to our persistant stats
|
# now add this batch of kills to our persistant stats
|
||||||
for account_id, kill_count in self._account_kills.items():
|
for account_id, kill_count in self._account_kills.items():
|
||||||
|
|
@ -213,4 +221,4 @@ def getRank(acc_id):
|
||||||
if ranks==[]:
|
if ranks==[]:
|
||||||
refreshStats()
|
refreshStats()
|
||||||
if acc_id in ranks:
|
if acc_id in ranks:
|
||||||
return ranks.index(acc_id)
|
return ranks.index(acc_id)+1
|
||||||
4
dist/ba_root/mods/tools/textonmap.py
vendored
4
dist/ba_root/mods/tools/textonmap.py
vendored
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#TODO need to set coordinates of text node , move timer values to settings.json
|
#TODO need to set coordinates of text node , move timer values to settings.json
|
||||||
from ba._enums import TimeType
|
from ba._enums import TimeType
|
||||||
import ba
|
import ba,_ba
|
||||||
import setting
|
import setting
|
||||||
class textonmap:
|
class textonmap:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -12,7 +12,7 @@ class textonmap:
|
||||||
left=_textonmap['bottom left watermark']
|
left=_textonmap['bottom left watermark']
|
||||||
top=_textonmap['top watermark']
|
top=_textonmap['top watermark']
|
||||||
|
|
||||||
self.timerr=ba.Timer(8,self.highlights,repeat=True)
|
self.timerr=ba.Timer(8,ba.Call(self.highlights),repeat=True)
|
||||||
|
|
||||||
self.left_watermark(left)
|
self.left_watermark(left)
|
||||||
self.top_message(top)
|
self.top_message(top)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue