mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2026-08-15 13:04:30 +00:00
271 lines
8.2 KiB
Python
271 lines
8.2 KiB
Python
import datetime
|
|
import json
|
|
import os
|
|
import shutil
|
|
import threading
|
|
|
|
import _babase
|
|
import setting
|
|
|
|
damage_data = {}
|
|
|
|
ranks = []
|
|
top3Name = []
|
|
|
|
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
|
|
our_settings = setting.get_settings_data()
|
|
if os.path.exists(statsfile):
|
|
try:
|
|
with open(statsfile, 'r', encoding='utf8') as f:
|
|
jsonData = json.loads(f.read())
|
|
except Exception:
|
|
try:
|
|
with open(statsfile + ".backup", 'r', encoding='utf-8') as f:
|
|
jsonData = json.load(f)
|
|
except Exception:
|
|
return {}
|
|
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 is None:
|
|
seasonStartDate = datetime.datetime.now()
|
|
s = {"startDate": seasonStartDate.strftime("%d-%m-%Y"), "stats": s}
|
|
if os.path.exists(statsfile):
|
|
shutil.copyfile(statsfile, statsfile + ".backup")
|
|
try:
|
|
with open(statsfile, 'w', encoding='utf8') as f:
|
|
f.write(json.dumps(s, indent=4, ensure_ascii=False))
|
|
except Exception as e:
|
|
print(f"Error dumping stats: {e}")
|
|
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.get('scores', 0), a.get('kills', 0), a.get('deaths', 0), a.get('games', 0),
|
|
a.get('name', 'default'), a.get('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(stats_dict: dict | None = None):
|
|
global cached_stats, ranks, top3Name
|
|
|
|
if stats_dict is None:
|
|
pStats = get_all_stats()
|
|
else:
|
|
pStats = stats_dict
|
|
|
|
cached_stats = pStats
|
|
entries = get_sorted_stats(pStats)
|
|
|
|
# Configure the range for toppers and leaderboard
|
|
our_settings = setting.get_settings_data()
|
|
leaderboard_range = our_settings.get("leaderboard", {}).get("range", 3)
|
|
|
|
toppersIDs = []
|
|
_ranks = []
|
|
|
|
for rank, entry in enumerate(entries, start=1):
|
|
scores, kills, deaths, games, name, aid = entry
|
|
aid_str = str(aid)
|
|
_ranks.append(aid_str)
|
|
|
|
if rank <= leaderboard_range:
|
|
toppersIDs.append(aid_str)
|
|
|
|
player_stats = pStats.get(aid_str)
|
|
if player_stats:
|
|
player_stats["rank"] = rank
|
|
player_stats["scores"] = int(scores)
|
|
player_stats["games"] = int(games)
|
|
player_stats["kills"] = int(kills)
|
|
player_stats["deaths"] = int(deaths)
|
|
|
|
try:
|
|
kd = round(float(kills) / float(deaths), 3) if float(deaths) > 0 else float(kills)
|
|
except Exception:
|
|
kd = 0.0
|
|
|
|
try:
|
|
avg_score = round(float(scores) / float(games), 3) if float(games) > 0 else 0.0
|
|
except Exception:
|
|
avg_score = 0.0
|
|
|
|
dmg = 0.0
|
|
if damage_data and aid_str in damage_data:
|
|
try:
|
|
dmg = round(float(damage_data[aid_str]), 3)
|
|
except Exception:
|
|
pass
|
|
|
|
player_stats["total_damage"] = round(player_stats.get("total_damage", 0.0) + dmg, 3)
|
|
player_stats["kd"] = kd
|
|
player_stats["avg_score"] = avg_score
|
|
|
|
ranks = _ranks
|
|
|
|
# Extract top names directly from sorted local entries list without API calls
|
|
top3Name = [entry[4] for entry in entries[:leaderboard_range]]
|
|
|
|
dump_stats(pStats)
|
|
|
|
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.
|
|
"""
|
|
account_kills = {}
|
|
account_deaths = {}
|
|
account_scores = {}
|
|
account_names = {}
|
|
|
|
for p_entry in score_set.get_records().values():
|
|
try:
|
|
player = p_entry.player
|
|
if player is None:
|
|
continue
|
|
account_id = player.get_account_id()
|
|
if account_id is None:
|
|
continue
|
|
name = player.getname(True)
|
|
except Exception:
|
|
continue
|
|
|
|
account_kills.setdefault(account_id, 0)
|
|
account_kills[account_id] += p_entry.accum_kill_count
|
|
account_deaths.setdefault(account_id, 0)
|
|
account_deaths[account_id] += p_entry.accum_killed_count
|
|
account_scores.setdefault(account_id, 0)
|
|
account_scores[account_id] += p_entry.accumscore
|
|
account_names[account_id] = name
|
|
|
|
if account_scores:
|
|
UpdateThread(account_kills, account_deaths, account_scores, account_names).start()
|
|
|
|
|
|
class UpdateThread(threading.Thread):
|
|
def __init__(self, account_kills, account_deaths, account_scores, account_names):
|
|
super().__init__()
|
|
self._account_kills = account_kills
|
|
self.account_deaths = account_deaths
|
|
self.account_scores = account_scores
|
|
self.account_names = account_names
|
|
|
|
def run(self):
|
|
try:
|
|
stats = get_all_stats()
|
|
except Exception:
|
|
stats = {}
|
|
|
|
now_str = str(datetime.datetime.now())
|
|
for account_id, kill_count in self._account_kills.items():
|
|
if account_id not in stats:
|
|
stats[account_id] = {
|
|
'rank': 0,
|
|
'name': "default name",
|
|
'scores': 0,
|
|
'total_damage': 0.0,
|
|
'kills': 0,
|
|
'deaths': 0,
|
|
'games': 0,
|
|
'kd': 0.0,
|
|
'avg_score': 0.0,
|
|
'last_seen': now_str,
|
|
'aid': str(account_id)
|
|
}
|
|
|
|
if account_id in self.account_names:
|
|
stats[account_id]['name'] = self.account_names[account_id]
|
|
|
|
stats[account_id]['kills'] += kill_count
|
|
stats[account_id]['deaths'] += self.account_deaths.get(account_id, 0)
|
|
stats[account_id]['scores'] += self.account_scores.get(account_id, 0)
|
|
stats[account_id]['last_seen'] = now_str
|
|
stats[account_id]['games'] += 1
|
|
stats[account_id]['aid'] = str(account_id)
|
|
|
|
# Award tickets for playing and kills
|
|
try:
|
|
from shop import add_tickets
|
|
tickets_to_award = 10 + kill_count * 2
|
|
add_tickets(account_id, tickets_to_award)
|
|
except Exception as e:
|
|
print(f"Error awarding tickets to {account_id}: {e}")
|
|
|
|
refreshStats(stats)
|
|
|
|
|
|
def getRank(acc_id):
|
|
global ranks
|
|
if not ranks:
|
|
refreshStats()
|
|
if acc_id in ranks:
|
|
return ranks.index(acc_id) + 1
|
|
return None
|