Initial commit
This commit is contained in:
2
secret/achievements/__init__.py
Normal file
2
secret/achievements/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import utils
|
||||
from . import handlers
|
57
secret/achievements/common.py
Normal file
57
secret/achievements/common.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import math
|
||||
if __name__ != "common":
|
||||
from objects import glob
|
||||
import time
|
||||
import json
|
||||
from common.ripple import userUtils
|
||||
|
||||
def load_achievement_data(ACHIEVEMENT_BASE, ACHIEVEMENT_KEYS, ACHIEVEMENT_STRUCT):
|
||||
LENGTH = 0
|
||||
ACHIEVEMENTS = []
|
||||
|
||||
for struct in ACHIEVEMENT_STRUCT:
|
||||
LENGTH = max(LENGTH, len(ACHIEVEMENT_KEYS[struct]) * ACHIEVEMENT_STRUCT[struct])
|
||||
|
||||
entry = {x:0 for x in ACHIEVEMENT_STRUCT}
|
||||
for i in range(LENGTH):
|
||||
for struct in ACHIEVEMENT_STRUCT:
|
||||
entry[struct] = math.floor(i / ACHIEVEMENT_STRUCT[struct]) % len(ACHIEVEMENT_KEYS[struct])
|
||||
format_data = {x:ACHIEVEMENT_KEYS[x][entry[x]] for x in ACHIEVEMENT_KEYS}
|
||||
ACHIEVEMENTS.append({x: ACHIEVEMENT_BASE[x].format_map(format_data) for x in ACHIEVEMENT_BASE})
|
||||
|
||||
return ACHIEVEMENTS, LENGTH
|
||||
|
||||
def get_usercache(userID):
|
||||
user_cache = glob.redis.get("lets:user_achievement_cache:{}".format(userID))
|
||||
if user_cache is None:
|
||||
user_cache = {}
|
||||
else:
|
||||
user_cache = json.loads(user_cache.decode("utf-8"))
|
||||
|
||||
if "version" not in user_cache:
|
||||
# Load from sql database
|
||||
user_cache["version"] = userUtils.getAchievementsVersion(userID)
|
||||
db_achievements = [x["achievement_id"] for x in glob.db.fetchAll("SELECT achievement_id FROM users_achievements WHERE user_id = %s", [userID])]
|
||||
if "achievements" in user_cache:
|
||||
user_cache["achievements"] += db_achievements
|
||||
else:
|
||||
user_cache["achievements"] = db_achievements
|
||||
# Remove duplicates after merge
|
||||
user_cache["achievements"] = list(set(user_cache["achievements"]))
|
||||
|
||||
return user_cache
|
||||
|
||||
def add_pending_achievement(userID, achievementID):
|
||||
user_cache = get_usercache(userID)
|
||||
if len([x for x in user_cache["achievements"] if x in [achievementID, -achievementID]]) > 0:
|
||||
print("Tried to add achievement:{} to user:{}, but failed due to duplicate entry.".format(achievementID, userID))
|
||||
return
|
||||
|
||||
user_cache["achievements"].append(-achievementID)
|
||||
|
||||
# Remove duplicates after merge
|
||||
user_cache["achievements"] = list(set(user_cache["achievements"]))
|
||||
|
||||
glob.redis.set("lets:user_achievement_cache:{}".format(userID), json.dumps(user_cache), 1800)
|
||||
|
||||
userUtils.unlockAchievement(userID, achievementID)
|
58
secret/achievements/generate_sql.py
Normal file
58
secret/achievements/generate_sql.py
Normal file
@@ -0,0 +1,58 @@
|
||||
if __name__ != "__main__":
|
||||
print("This is ment to be runned as a toolkit for generating achievement database")
|
||||
exit()
|
||||
|
||||
import common
|
||||
from os.path import dirname, basename, isfile
|
||||
import glob
|
||||
import importlib
|
||||
import time
|
||||
import math
|
||||
|
||||
SQL_STRING = """
|
||||
CREATE TABLE IF NOT EXISTS `achievements` (
|
||||
`id` int(11) NOT NULL,
|
||||
`name` varchar(32) NOT NULL,
|
||||
`description` varchar(128) NOT NULL,
|
||||
`icon` varchar(32) NOT NULL,
|
||||
`version` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
INSERT INTO achievements (id, name, description, icon, version) VALUES
|
||||
"""
|
||||
|
||||
module_list = glob.glob("handlers/*.py")
|
||||
module_list = [basename(f)[:-3] for f in module_list if isfile(f) and not f.endswith("__init__.py")]
|
||||
# ^ cat face
|
||||
|
||||
modules = []
|
||||
for module in module_list:
|
||||
modules.append(importlib.import_module("handlers.{}".format(module)))
|
||||
|
||||
modules = sorted(modules, key=lambda k: k.ORDER)
|
||||
|
||||
SQL_INSERTS = []
|
||||
index = 1
|
||||
for module in modules:
|
||||
module.load()
|
||||
for achievement in module.ACHIEVEMENTS:
|
||||
SQL_INSERTS.append("({}, '{}', '{}', '{}', {})".format(
|
||||
index,
|
||||
achievement["name"].replace('"', '\\"').replace("'", "\\'"),
|
||||
achievement["description"].replace('"', '\\"').replace("'", "\\'"),
|
||||
achievement["icon"].replace('"', '\\"').replace("'", "\\'"),
|
||||
module.VERSION
|
||||
))
|
||||
index += 1
|
||||
|
||||
SQL_STRING += ",\n".join(SQL_INSERTS) + ";"
|
||||
|
||||
FILENAME = "achievements-{}.sql".format(math.floor(time.time()))
|
||||
with open(FILENAME, "w") as f:
|
||||
f.write(SQL_STRING)
|
||||
|
||||
print("Saved sql export into {}".format(FILENAME))
|
||||
print("Import this table into your database.")
|
||||
print("""NOTE: Avoid changing the ORDER variable inside the handlers at all cost as this will result in
|
||||
new achievement sql data not matching data of already achieved achievements by users.""")
|
||||
print("If you know what you are doing you know how to fix this if you still choose to ignore this warning")
|
55
secret/achievements/handlers/combo.py
Normal file
55
secret/achievements/handlers/combo.py
Normal file
@@ -0,0 +1,55 @@
|
||||
if __name__ != "handlers.combo":
|
||||
from secret.achievements import common
|
||||
from objects import glob
|
||||
else:
|
||||
import common
|
||||
|
||||
VERSION = 1
|
||||
ORDER = 1
|
||||
|
||||
# Loads the achievement length on load
|
||||
LENGTH = 0
|
||||
|
||||
ACHIEVEMENT_BASE = {
|
||||
"name": "{index} Combo (osu!{mode})",
|
||||
"description": "{index} big ones! You're moving up in the world!",
|
||||
"icon": "osu-combo-{index}"
|
||||
}
|
||||
|
||||
ACHIEVEMENT_KEYS = {
|
||||
"index": [500, 750, 1000, 2000],
|
||||
"mode": ["std", "taiko", "ctb", "mania"]
|
||||
}
|
||||
|
||||
# For every itteration index gets increased, while mode and mode_2 gets increased every 4 itterations
|
||||
ACHIEVEMENT_STRUCT = {
|
||||
"index": 1,
|
||||
"mode": 4
|
||||
}
|
||||
|
||||
ACHIEVEMENTS = []
|
||||
|
||||
def load():
|
||||
global ACHIEVEMENTS, LENGTH
|
||||
ACHIEVEMENTS, LENGTH = common.load_achievement_data(ACHIEVEMENT_BASE, ACHIEVEMENT_KEYS, ACHIEVEMENT_STRUCT)
|
||||
|
||||
def handle(mode, score, beatmap, user_data):
|
||||
return check(mode, score.maxCombo)
|
||||
|
||||
def check(mode, max_combo):
|
||||
achievement_ids = []
|
||||
indexies = [x for x in ACHIEVEMENT_KEYS["index"] if x <= max_combo]
|
||||
|
||||
for index in range(len(indexies)):
|
||||
achievement_ids.append(index + mode * 4)
|
||||
|
||||
return achievement_ids
|
||||
|
||||
def update(userID):
|
||||
achievement_ids = []
|
||||
|
||||
entries = glob.db.fetchAll("SELECT MAX(max_combo) AS max_combo, play_mode FROM scores WHERE userid = %s AND completed >= 2 GROUP BY play_mode", [userID])
|
||||
for entry in entries:
|
||||
achievement_ids += check(entry["play_mode"], entry["max_combo"])
|
||||
|
||||
return achievement_ids
|
116
secret/achievements/handlers/mods.py
Normal file
116
secret/achievements/handlers/mods.py
Normal file
@@ -0,0 +1,116 @@
|
||||
if __name__ != "handlers.mods":
|
||||
from secret.achievements import common
|
||||
from objects import glob
|
||||
from common.constants import mods
|
||||
else:
|
||||
import common
|
||||
|
||||
VERSION = 4
|
||||
ORDER = 4
|
||||
|
||||
# Loads the achievement length on load
|
||||
LENGTH = 0
|
||||
|
||||
ACHIEVEMENT_BASE = {
|
||||
"name": "{name}",
|
||||
"description": "{description}",
|
||||
"icon": "all-intro-{mod}"
|
||||
}
|
||||
|
||||
ACHIEVEMENT_KEYS = {
|
||||
"name": [
|
||||
"Finality",
|
||||
"Perfectionist",
|
||||
"Rock Around The Clock",
|
||||
"Time And A Half",
|
||||
"Sweet Rave Party",
|
||||
"Blindsight",
|
||||
"Are You Afraid Of The Dark?",
|
||||
"Dial It Right Back",
|
||||
"Risk Averse",
|
||||
"Slowboat",
|
||||
"Burned Out"
|
||||
],
|
||||
"description": [
|
||||
"High stakes, no regrets.",
|
||||
"Accept nothing but the best.",
|
||||
"You can't stop the rock.",
|
||||
"Having a right ol' time. One and a half of them, almost.",
|
||||
"Founded in the fine tradition of changing things that were just fine as they were.",
|
||||
"I can see just perfectly.",
|
||||
"Harder than it looks, probably because it's hard to look.",
|
||||
"Sometimes you just want to take it easy.",
|
||||
"Safety nets are fun!",
|
||||
"You got there. Eventually.",
|
||||
"One cannot always spin to win."
|
||||
],
|
||||
"mod": [
|
||||
"suddendeath",
|
||||
"perfect",
|
||||
"hardrock",
|
||||
"doubletime",
|
||||
"nightcore",
|
||||
"hidden",
|
||||
"flashlight",
|
||||
"easy",
|
||||
"nofail",
|
||||
"halftime",
|
||||
"spunout"
|
||||
]
|
||||
}
|
||||
|
||||
# For every itteration index gets increased, while mode and mode_2 gets increased every 4 itterations
|
||||
ACHIEVEMENT_STRUCT = {
|
||||
"name": 1,
|
||||
"description": 1,
|
||||
"mod": 1
|
||||
}
|
||||
|
||||
ACHIEVEMENTS = []
|
||||
|
||||
def load():
|
||||
global ACHIEVEMENTS, LENGTH
|
||||
ACHIEVEMENTS, LENGTH = common.load_achievement_data(ACHIEVEMENT_BASE, ACHIEVEMENT_KEYS, ACHIEVEMENT_STRUCT)
|
||||
|
||||
def handle(mode, score, beatmap, user_data):
|
||||
return check(score.mods)
|
||||
|
||||
def check(m):
|
||||
achievement_ids = []
|
||||
|
||||
# Yes I am braindead atm and dont want to think about it...
|
||||
if m & mods.SUDDENDEATH > 0:
|
||||
achievement_ids += [0]
|
||||
if m & mods.PERFECT > 0:
|
||||
achievement_ids += [1]
|
||||
if m & mods.HARDROCK > 0:
|
||||
achievement_ids += [2]
|
||||
if m & mods.DOUBLETIME > 0:
|
||||
achievement_ids += [3]
|
||||
if m & mods.NIGHTCORE > 0:
|
||||
achievement_ids += [4]
|
||||
if m & mods.HIDDEN > 0:
|
||||
achievement_ids += [5]
|
||||
if m & mods.FLASHLIGHT > 0:
|
||||
achievement_ids += [6]
|
||||
if m & mods.EASY > 0:
|
||||
achievement_ids += [7]
|
||||
if m & mods.NOFAIL > 0:
|
||||
achievement_ids += [8]
|
||||
if m & mods.HALFTIME > 0:
|
||||
achievement_ids += [9]
|
||||
if m & mods.SPUNOUT > 0:
|
||||
achievement_ids += [10]
|
||||
if m & mods.RELAX > 0:
|
||||
achievement_ids += [11]
|
||||
|
||||
return achievement_ids
|
||||
|
||||
def update(userID):
|
||||
achievement_ids = []
|
||||
|
||||
entries = glob.db.fetchAll("SELECT mods FROM scores WHERE userid = %s GROUP BY mods", [userID])
|
||||
for entry in entries:
|
||||
achievement_ids += check(entry["mods"])
|
||||
|
||||
return achievement_ids
|
63
secret/achievements/handlers/playcount.py
Normal file
63
secret/achievements/handlers/playcount.py
Normal file
@@ -0,0 +1,63 @@
|
||||
if __name__ != "handlers.playcount":
|
||||
from secret.achievements import common
|
||||
from objects import glob
|
||||
else:
|
||||
import common
|
||||
|
||||
VERSION = 5
|
||||
ORDER = 5
|
||||
|
||||
# Loads the achievement length on load
|
||||
LENGTH = 0
|
||||
|
||||
ACHIEVEMENT_BASE = {
|
||||
"name": "{index_formatted} Plays",
|
||||
"description": "{description}",
|
||||
"icon": "osu-plays-{index}"
|
||||
}
|
||||
|
||||
ACHIEVEMENT_KEYS = {
|
||||
"index": [5000, 15000, 25000, 50000],
|
||||
"index_formatted": ["5,000", "15,000", "25,000", "50,000"],
|
||||
"description": [
|
||||
"There's a lot more where that came from.",
|
||||
"Must.. click.. circles..",
|
||||
"There's no going back.",
|
||||
"You're here forever."
|
||||
]
|
||||
}
|
||||
|
||||
# For every itteration index gets increased, while mode and mode_2 gets increased every 4 itterations
|
||||
ACHIEVEMENT_STRUCT = {
|
||||
"index": 1,
|
||||
"index_formatted": 1,
|
||||
"description": 1
|
||||
}
|
||||
|
||||
ACHIEVEMENTS = []
|
||||
|
||||
def load():
|
||||
global ACHIEVEMENTS, LENGTH
|
||||
ACHIEVEMENTS, LENGTH = common.load_achievement_data(ACHIEVEMENT_BASE, ACHIEVEMENT_KEYS, ACHIEVEMENT_STRUCT)
|
||||
|
||||
def handle(mode, score, beatmap, user_data):
|
||||
if mode is not 0:
|
||||
return []
|
||||
return check(user_data["playcount"])
|
||||
|
||||
def check(playcount):
|
||||
achievement_ids = []
|
||||
indexies = [x for x in ACHIEVEMENT_KEYS["index"] if x <= playcount]
|
||||
|
||||
for index in range(len(indexies)):
|
||||
achievement_ids.append(index)
|
||||
|
||||
return achievement_ids
|
||||
|
||||
def update(userID):
|
||||
achievement_ids = []
|
||||
|
||||
playcount = glob.db.fetch("SELECT playcount_std FROM users_stats WHERE id = %s", [userID])["playcount_std"]
|
||||
achievement_ids += check(playcount)
|
||||
|
||||
return achievement_ids
|
114
secret/achievements/handlers/skillfc.py
Normal file
114
secret/achievements/handlers/skillfc.py
Normal file
@@ -0,0 +1,114 @@
|
||||
if __name__ != "handlers.skillfc":
|
||||
import math
|
||||
from secret.achievements import common
|
||||
from common.ripple import scoreUtils
|
||||
from objects import glob, beatmap
|
||||
else:
|
||||
import common
|
||||
|
||||
VERSION = 3
|
||||
ORDER = 3
|
||||
|
||||
# Loads the achievement length on load
|
||||
LENGTH = 0
|
||||
|
||||
ACHIEVEMENT_BASE = {
|
||||
"name": "{name}",
|
||||
"description": "{description}",
|
||||
"icon": "{mode}-skill-fc-{index}"
|
||||
}
|
||||
|
||||
ACHIEVEMENT_KEYS = {
|
||||
"index": [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
"mode": ["osu", "taiko", "fruits", "mania"],
|
||||
"name": [
|
||||
"Totality",
|
||||
"Keeping Time",
|
||||
"Sweet And Sour",
|
||||
"Keystruck",
|
||||
"Business As Usual",
|
||||
"To Your Own Beat",
|
||||
"Reaching The Core",
|
||||
"Keying In",
|
||||
"Building Steam",
|
||||
"Big Drums",
|
||||
"Clean Platter",
|
||||
"Hyperflow",
|
||||
"Moving Forward",
|
||||
"Adversity Overcome",
|
||||
"Between The Rain",
|
||||
"Breakthrough",
|
||||
"Paradigm Shift",
|
||||
"Demonslayer",
|
||||
"Addicted",
|
||||
"Everything Extra",
|
||||
"Anguish Quelled",
|
||||
"Rhythm's Call",
|
||||
"Quickening",
|
||||
"Level Breaker",
|
||||
"Never Give Up",
|
||||
"Time Everlasting",
|
||||
"Supersonic",
|
||||
"Step Up",
|
||||
"Aberration",
|
||||
"The Drummer's Throne",
|
||||
"Dashing Scarlet",
|
||||
"Behind The Veil"
|
||||
],
|
||||
"description": [
|
||||
"All the notes. Every single one.",
|
||||
"Two to go, please.",
|
||||
"Hey, this isn't so bad.",
|
||||
"Bet you feel good about that.",
|
||||
"Surprisingly difficult.",
|
||||
"Don't choke.",
|
||||
"Excellence is its own reward.",
|
||||
"They said it couldn't be done. They were wrong."
|
||||
]
|
||||
}
|
||||
|
||||
# For every itteration index gets increased, while mode and mode_2 gets increased every 4 itterations
|
||||
ACHIEVEMENT_STRUCT = {
|
||||
"name": 1,
|
||||
"mode": 1,
|
||||
"index": 4,
|
||||
"description": 4
|
||||
}
|
||||
|
||||
ACHIEVEMENTS = []
|
||||
|
||||
def load():
|
||||
global ACHIEVEMENTS, LENGTH
|
||||
ACHIEVEMENTS, LENGTH = common.load_achievement_data(ACHIEVEMENT_BASE, ACHIEVEMENT_KEYS, ACHIEVEMENT_STRUCT)
|
||||
|
||||
def handle(mode, score, beatmap, user_data):
|
||||
if not score.fullCombo: # No need to check if the score were not a fullcombo
|
||||
return []
|
||||
return check(mode, beatmap)
|
||||
|
||||
def check(mode, beatmap):
|
||||
achievement_ids = []
|
||||
|
||||
mode_str = scoreUtils.readableGameMode(mode)
|
||||
|
||||
mode_2 = mode_str.replace("osu", "std")
|
||||
stars = getattr(beatmap, "stars" + mode_2.title())
|
||||
|
||||
indexies = [x - 1 for x in ACHIEVEMENT_KEYS["index"] if x == math.floor(stars)]
|
||||
|
||||
for index in indexies:
|
||||
achievement_ids.append(mode + index * 4)
|
||||
|
||||
return achievement_ids
|
||||
|
||||
def update(userID):
|
||||
achievement_ids = []
|
||||
|
||||
entries = glob.db.fetchAll("SELECT beatmap_md5, play_mode FROM scores WHERE full_combo = 1 AND completed >= 2 AND userid = %s GROUP BY beatmap_md5, play_mode", [userID])
|
||||
for entry in entries:
|
||||
current_beatmap = beatmap.beatmap()
|
||||
current_beatmap.setDataFromDB(entry["beatmap_md5"])
|
||||
|
||||
achievement_ids += check(entry["play_mode"], current_beatmap)
|
||||
|
||||
return achievement_ids
|
112
secret/achievements/handlers/skillpass.py
Normal file
112
secret/achievements/handlers/skillpass.py
Normal file
@@ -0,0 +1,112 @@
|
||||
if __name__ != "handlers.skillpass":
|
||||
import math
|
||||
from secret.achievements import common
|
||||
from common.ripple import scoreUtils
|
||||
from objects import glob, beatmap
|
||||
else:
|
||||
import common
|
||||
|
||||
VERSION = 2
|
||||
ORDER = 2
|
||||
|
||||
# Loads the achievement length on load
|
||||
LENGTH = 0
|
||||
|
||||
ACHIEVEMENT_BASE = {
|
||||
"name": "{name}",
|
||||
"description": "{description}",
|
||||
"icon": "{mode}-skill-pass-{index}"
|
||||
}
|
||||
|
||||
ACHIEVEMENT_KEYS = {
|
||||
"index": [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
"mode": ["osu", "taiko", "fruits", "mania"],
|
||||
"name": [
|
||||
"Rising Star",
|
||||
"My First Don",
|
||||
"A Slice Of Life",
|
||||
"First Steps",
|
||||
"Constellation Prize",
|
||||
"Katsu Katsu Katsu",
|
||||
"Dashing Ever Forward",
|
||||
"No Normal Player",
|
||||
"Building Confidence",
|
||||
"Not Even Trying",
|
||||
"Zesty Disposition",
|
||||
"Impulse Drive",
|
||||
"Insanity Approaches",
|
||||
"Face Your Demons",
|
||||
"Hyperdash ON!",
|
||||
"Hyperspeed",
|
||||
"These Clarion Skies",
|
||||
"The Demon Within",
|
||||
"It's Raining Fruit",
|
||||
"Ever Onwards",
|
||||
"Above and Beyond",
|
||||
"Drumbreaker",
|
||||
"Fruit Ninja",
|
||||
"Another Surpassed",
|
||||
"Supremacy",
|
||||
"The Godfather",
|
||||
"Dreamcatcher",
|
||||
"Extra Credit",
|
||||
"Absolution",
|
||||
"Rhythm Incarnate",
|
||||
"Lord of the Catch",
|
||||
"Maniac"
|
||||
],
|
||||
"description": [
|
||||
"Can't go forward without the first steps.",
|
||||
"Definitely not a consolation prize. Now things start getting hard!",
|
||||
"Oh, you've SO got this.",
|
||||
"You're not twitching, you're just ready.",
|
||||
"Everything seems so clear now.",
|
||||
"A cut above the rest.",
|
||||
"All marvel before your prowess.",
|
||||
"My god, you're full of stars!"
|
||||
]
|
||||
}
|
||||
|
||||
# For every itteration index gets increased, while mode and mode_2 gets increased every 4 itterations
|
||||
ACHIEVEMENT_STRUCT = {
|
||||
"name": 1,
|
||||
"mode": 1,
|
||||
"index": 4,
|
||||
"description": 4
|
||||
}
|
||||
|
||||
ACHIEVEMENTS = []
|
||||
|
||||
def load():
|
||||
global ACHIEVEMENTS, LENGTH
|
||||
ACHIEVEMENTS, LENGTH = common.load_achievement_data(ACHIEVEMENT_BASE, ACHIEVEMENT_KEYS, ACHIEVEMENT_STRUCT)
|
||||
|
||||
def handle(mode, score, beatmap, user_data):
|
||||
return check(mode, beatmap)
|
||||
|
||||
def check(mode, beatmap):
|
||||
achievement_ids = []
|
||||
|
||||
mode_str = scoreUtils.readableGameMode(mode)
|
||||
|
||||
mode_2 = mode_str.replace("osu", "std")
|
||||
stars = getattr(beatmap, "stars" + mode_2.title())
|
||||
|
||||
indexies = [x - 1 for x in ACHIEVEMENT_KEYS["index"] if x == math.floor(stars)]
|
||||
|
||||
for index in indexies:
|
||||
achievement_ids.append(mode + index * 4)
|
||||
|
||||
return achievement_ids
|
||||
|
||||
def update(userID):
|
||||
achievement_ids = []
|
||||
|
||||
entries = glob.db.fetchAll("SELECT beatmap_md5, play_mode FROM scores WHERE completed = 3 AND userid = %s", [userID])
|
||||
for entry in entries:
|
||||
current_beatmap = beatmap.beatmap()
|
||||
current_beatmap.setDataFromDB(entry["beatmap_md5"])
|
||||
|
||||
achievement_ids += check(entry["play_mode"], current_beatmap)
|
||||
|
||||
return achievement_ids
|
19
secret/achievements/install/db.py
Normal file
19
secret/achievements/install/db.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import MySQLdb
|
||||
import install_glob as glob
|
||||
|
||||
def run():
|
||||
c = glob.sqlcon.cursor()
|
||||
make_0(c)
|
||||
alt_0(c)
|
||||
print("Eyy! Database has been updated.")
|
||||
|
||||
def make_0(c):
|
||||
c.execute("CREATE TABLE users_achievements (id int(11) NOT NULL,user_id int(11) NOT NULL,achievement_id int(11) NOT NULL,time int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1")
|
||||
c.execute("ALTER TABLE users_achievements ADD PRIMARY KEY (id)")
|
||||
c.execute("ALTER TABLE users_achievements MODIFY id int(11) NOT NULL AUTO_INCREMENT")
|
||||
|
||||
def alt_0(c):
|
||||
c.execute("ALTER TABLE users ADD achievements_version INT NOT NULL DEFAULT '0' AFTER rank")
|
||||
|
||||
def make_connect(obj):
|
||||
glob.sqlcon = MySQLdb.connect(**obj)
|
21
secret/achievements/install/init.py
Normal file
21
secret/achievements/install/init.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import db
|
||||
|
||||
def run():
|
||||
db.run()
|
||||
|
||||
def setup_args():
|
||||
db.make_connect(
|
||||
{
|
||||
"host": input("Host: "),
|
||||
"user": input("User: "),
|
||||
"passwd": input("Password: "),
|
||||
"db": input("Database: ")
|
||||
}
|
||||
)
|
||||
|
||||
def pass_args(obj):
|
||||
db.make_connect(obj)
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_args()
|
||||
run()
|
1
secret/achievements/install/install_glob.py
Normal file
1
secret/achievements/install/install_glob.py
Normal file
@@ -0,0 +1 @@
|
||||
sqlcon = None
|
115
secret/achievements/utils.py
Normal file
115
secret/achievements/utils.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from objects import glob
|
||||
from common.ripple import userUtils
|
||||
from os.path import dirname, basename, isfile
|
||||
import glob as _glob
|
||||
import importlib
|
||||
import json
|
||||
from secret.achievements import common
|
||||
|
||||
def load_achievements():
|
||||
"""Load all the achievements from handler list into glob.achievementClasses,
|
||||
and sets glob.ACHIEVEMENTS_VERSION to the highest version number in our achievement list.
|
||||
"""
|
||||
|
||||
modules = _glob.glob("secret/achievements/handlers/*.py")
|
||||
modules = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith("__init__.py")]
|
||||
# ^ cat face
|
||||
|
||||
for module in modules:
|
||||
module = importlib.import_module("secret.achievements.handlers." + module)
|
||||
module.load()
|
||||
if module.ORDER in glob.achievementClasses:
|
||||
print("\n!!! FOUND OVERLAPPING ACHIEVEMENT ORDER FOR {}!!!".format(module.ORDER))
|
||||
print("Unable to load {} due to {} already loaded in slot {}\n".format(module.__name__, glob.achievementClasses[module.ORDER].__name__, module.ORDER))
|
||||
continue
|
||||
glob.achievementClasses[module.ORDER] = module
|
||||
glob.ACHIEVEMENTS_VERSION = max(glob.ACHIEVEMENTS_VERSION, module.VERSION)
|
||||
|
||||
print("Loaded {} achievement classes!".format(len(glob.achievementClasses)), end=" ")
|
||||
|
||||
def unlock_achievements_update(userID, version):
|
||||
"""Scans the user for past achievements they should have unlocked
|
||||
|
||||
Arguments:
|
||||
userID {int} -- User id of a player
|
||||
version {int} -- Last achivement version the player had
|
||||
|
||||
Returns:
|
||||
Array -- List of achievements
|
||||
"""
|
||||
achievements = []
|
||||
|
||||
# Scan all past achivement versions from the user's achivement version to the latest
|
||||
index = 1
|
||||
for handler in glob.achievementClasses.values():
|
||||
if handler.VERSION > version:
|
||||
achievements += [x + index for x in handler.update(userID)]
|
||||
index += handler.LENGTH
|
||||
|
||||
# Update achivement version for user
|
||||
userUtils.updateAchievementsVersion(userID)
|
||||
|
||||
return achievements
|
||||
|
||||
def unlock_achievements(score, beatmap, user_data):
|
||||
"""Return array of achievements the current play recived
|
||||
|
||||
Arguments:
|
||||
score {Score} -- Score data recived from replay
|
||||
beatmap {Beatmap} -- Played beatmap
|
||||
user_data {dict} -- Info about the current player
|
||||
|
||||
Returns:
|
||||
Array -- List of achievements for the current play
|
||||
"""
|
||||
achievements = []
|
||||
|
||||
userID = userUtils.getID(score.playerName)
|
||||
user_cache = common.get_usercache(userID)
|
||||
|
||||
# Get current gamemode and change value std to osu
|
||||
gamemode_index = score.gameMode
|
||||
|
||||
# Check if user should run achivement recheck
|
||||
if user_cache["version"] < glob.ACHIEVEMENTS_VERSION:
|
||||
achievements += unlock_achievements_update(userID, user_cache["version"])
|
||||
|
||||
# Check if gameplay should get new achivement
|
||||
index = 1
|
||||
for handler in glob.achievementClasses.values():
|
||||
achievements += [x + index for x in handler.handle(gamemode_index, score, beatmap, user_data)]
|
||||
index += handler.LENGTH
|
||||
|
||||
# Add pending achievements that were added though redis or mysql
|
||||
achievements += [-x for x in user_cache["achievements"] if x < 0] # Negative achievements id's means its pending
|
||||
|
||||
# Remove pending achievements from redis object since we added it to the post achievements
|
||||
user_cache["achievements"] = [x for x in user_cache["achievements"] if x > 0]
|
||||
|
||||
# Remove duplicated achievements (incase of unlock_achievements_update adding stuff)
|
||||
achievements = list(set(achievements))
|
||||
|
||||
# Remove already achived achievements from list
|
||||
achievements = [x for x in achievements if x not in user_cache["achievements"]]
|
||||
|
||||
user_cache["achievements"] += achievements
|
||||
glob.redis.set("lets:user_achievement_cache:{}".format(userID), json.dumps(user_cache), 1800)
|
||||
|
||||
for achievement in achievements:
|
||||
userUtils.unlockAchievement(userID, achievement)
|
||||
|
||||
return achievements
|
||||
|
||||
def achievements_response(achievements):
|
||||
achievement_objects = []
|
||||
|
||||
index = 1
|
||||
for handler in glob.achievementClasses.values():
|
||||
achievement_objects += [handler.ACHIEVEMENTS[x - index] for x in achievements if len(handler.ACHIEVEMENTS) > x - index and x - index >= 0]
|
||||
index += handler.LENGTH
|
||||
|
||||
achievements_packed = []
|
||||
for achievement_object in achievement_objects:
|
||||
achievements_packed.append("+".join([achievement_object["icon"], achievement_object["name"], achievement_object["description"]]))
|
||||
|
||||
return "/".join(achievements_packed)
|
Reference in New Issue
Block a user