def load_config(args): # Load config logging.info("Reading config file") glob.conf = config.config("config.ini") # Read additional config file logging.info("Reading additional config file") with open(glob.conf.config["custom"]["config"], "r") as f: logging.info("Add-on conf = {}".format( glob.conf.config["custom"]["config"])) glob.conf.extra = json.load(f) logging.info("Connecting to MySQL db") glob.db = dbConnector.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], max(1, MAX_WORKERS)) # Set verbose glob.debug = args.verbose # Disable MySQL db warnings (it spams 'Unsafe statement written to the binary log using statement...' # because we use UPDATE with LIMIT 1 when updating performance points after recalculation warnings.filterwarnings("ignore", category=MySQLdb.Warning)
def ping(): try: glob.db.execute("SELECT 1+1") log.info("[AUTOMATED QUERY] has been execute!") except: log.info( "[AUTOMATED QUERY] error! the connection will restart!") glob.db = dbConnector.db( glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"]))
def ping(): try: glob.db.execute("SELECT 1+1") #SOLUSI BIAR CEPAT MENINGGAL #os.execv(sys.executable, [sys.executable] + sys.argv) log.info("[AUTOMATED QUERY] has been execute!") except: log.info( "[AUTOMATED QUERY] error! the connection will restart!") #os.execv(sys.executable, [sys.executable] + sys.argv) //automated restart lets when error glob.db = dbConnector.db( glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"]))
def main(): # CLI stuff parser = argparse.ArgumentParser( description="pp recalc tool for ripple, new version.") recalc_group = parser.add_mutually_exclusive_group(required=False) recalc_group.add_argument("-r", "--recalc", help="calculates pp for all high scores", required=False, action="store_true") recalc_group.add_argument("-z", "--zero", help="calculates pp for 0 pp high scores", required=False, action="store_true") recalc_group.add_argument( "-i", "--id", help="calculates pp for the score with this score_id", required=False) recalc_group.add_argument( "-m", "--mods", help="calculates pp for high scores with these mods (flags)", required=False) recalc_group.add_argument( "-g", "--gamemode", help= "calculates pp for scores played on this game mode (std:0, taiko:1, ctb:2, mania:3)", required=False) recalc_group.add_argument("-l", "--loved", help="calculate pp for loved maps", required=False) recalc_group.add_argument( "-u", "--userid", help="calculates pp for high scores set by a specific user (user_id)", required=False) recalc_group.add_argument( "-b", "--beatmapid", help= "calculates pp for high scores played on a specific beatmap (beatmap_id)", required=False) recalc_group.add_argument( "-fhd", "--fixstdhd", help= "calculates pp for std hd high scores (14/05/2018 pp algorithm changes)", required=False, action="store_true") parser.add_argument("-w", "--workers", help="number of workers. {} by default. Max {}".format( MAX_WORKERS // 2, MAX_WORKERS), required=False) parser.add_argument("-cs", "--chunksize", help="score chunks size", required=False) parser.add_argument("-v", "--verbose", help="verbose/debug mode", required=False, action="store_true") args = parser.parse_args() # Logging progressbar.streams.wrap_stderr() logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) logging.info("Running under {}".format("UNIX" if UNIX else "WIN32")) # Load config logging.info("Reading config file") glob.conf = config.config("config.ini") # Get workers from arguments if set workers_number = MAX_WORKERS // 2 if args.workers is not None: workers_number = int(args.workers) # Get chunk size from arguments if set chunk_size = None if args.chunksize is not None: chunk_size = int(args.chunksize) # Disable MySQL db warnings (it spams 'Unsafe statement written to the binary log using statement...' # because we use UPDATE with LIMIT 1 when updating performance points after recalculation warnings.filterwarnings("ignore", category=MySQLdb.Warning) # Connect to MySQL logging.info("Connecting to MySQL db") glob.db = dbConnector.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], max(workers_number, MAX_WORKERS)) # Set verbose glob.debug = args.verbose # Get recalculator recalculators_gen = { "zero": lambda: SimpleRecalculator(("scores_auto.completed = 3", "pp = 0")), "recalc": lambda: SimpleRecalculator(("scores_auto.completed = 3", "pp > 750")), "mods": lambda: SimpleRecalculator( ("scores_auto.completed = 3", "mods & %s > 0"), (args.mods, )), "id": lambda: SimpleRecalculator(("scores_auto.id = %s", ), (args.id, )), "gamemode": lambda: SimpleRecalculator(( "scores_auto.completed = 3", "scores_auto.play_mode = %s", ), (args.gamemode, )), "loved": lambda: SimpleRecalculator( ("scores_auto.completed = 3", "beatmaps.ranked = 5")), "userid": lambda: SimpleRecalculator(( "scores_auto.completed = 3", "scores_auto.userid = %s", ), (args.userid, )), "beatmapid": lambda: SimpleRecalculator(( "scores_auto.completed = 3", "beatmaps.beatmap_id = %s", ), (args.beatmapid, )), "fixstdhd": lambda: SimpleRecalculator( ("scores_auto.completed = 3", "scores_auto.play_mode = 0", "scores_auto.mods & 8 > 0")) } recalculator = None for k, v in vars(args).items(): if v is not None and ((type(v) is bool and v) or type(v) is not bool): if k in recalculators_gen: recalculator = recalculators_gen[k]() break # Execute mass recalc if recalculator is not None: mass_recalc(recalculator, workers_number, chunk_size) else: logging.warning("No recalc option specified") parser.print_help()
sys.exit() # Create data folder if needed consoleHelper.printNoNl("> Checking folders... ") paths = [".data"] for i in paths: if not os.path.exists(i): os.makedirs(i, 0o770) consoleHelper.printDone() # Connect to db try: consoleHelper.printNoNl("> Connecting to MySQL database... ") glob.db = dbConnector.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"])) consoleHelper.printNoNl(" ") consoleHelper.printDone() except: # Exception while connecting to db consoleHelper.printError() consoleHelper.printColored( "[!] Error while connection to database. Please check your config.ini and run the server again", bcolors.RED) raise # Connect to redis try: consoleHelper.printNoNl("> Connecting to redis... ")
consoleHelper.printNoNl("> Reading config file... ") glob.conf = config.config("config.ini") glob.debug = generalUtils.stringToBool(glob.conf.config["server"]["debug"]) consoleHelper.printDone() # Get workers from arguments if set workers = 0 if args.workers is not None: workers = int(args.workers) # Connect to MySQL try: consoleHelper.printNoNl("> Connecting to MySQL db") glob.db = dbConnector.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], max(workers, MAX_WORKERS)) consoleHelper.printNoNl(" ") consoleHelper.printDone() except: consoleHelper.printError() consoleHelper.printColored( "[!] Error while connection to database. Please check your config.ini and run the server again", bcolors.RED) raise # Set verbose glob.debug = args.verbose # Operations
".data/catch_the_pp", glob.conf["BEATMAPS_FOLDER"], glob.conf["SCREENSHOTS_FOLDER"] ] + glob.conf["REPLAYS_FOLDERS"] for i in paths: if not os.path.exists(i): os.makedirs(i, 0o770) consoleHelper.printDone() # Connect to db try: consoleHelper.printNoNl("> Connecting to MySQL database... ") glob.db = dbConnector.db( glob.conf["DB_HOST"], glob.conf["DB_PORT"], glob.conf["DB_USERNAME"], glob.conf["DB_PASSWORD"], glob.conf["DB_NAME"], glob.conf["DB_WORKERS"] ) consoleHelper.printNoNl(" ") consoleHelper.printDone() except: # Exception while connecting to db consoleHelper.printError() consoleHelper.printColored("[!] Error while connection to database. Please check your config.ini and run the server again", bcolors.RED) raise # Connect to redis try: consoleHelper.printNoNl("> Connecting to redis... ") glob.redis = redis.Redis(
def main(): # CLI stuff parser = argparse.ArgumentParser( description="pp recalc tool for ripple, new version.") recalc_group = parser.add_mutually_exclusive_group(required=False) recalc_group.add_argument("-r", "--recalc", help="calculates pp for all high scores", required=False, action="store_true") recalc_group.add_argument("-z", "--zero", help="calculates pp for 0 pp high scores", required=False, action="store_true") recalc_group.add_argument( "-i", "--id", help="calculates pp for the score with this score_id", required=False) recalc_group.add_argument( "-m", "--mods", help="calculates pp for high scores with these mods (flags)", required=False) recalc_group.add_argument( "-x", "--relax", help="calculates pp for relax/autopilot scores (is_relax = 1)", required=False, action="store_true") recalc_group.add_argument( "-g", "--gamemode", help= "calculates pp for scores played on this game mode (std:0, taiko:1, ctb:2, mania:3)", required=False) recalc_group.add_argument( "-u", "--userid", help="calculates pp for high scores set by a specific user (user_id)", required=False) recalc_group.add_argument( "-b", "--beatmapid", help= "calculates pp for high scores played on a specific beatmap (beatmap_id)", required=False) recalc_group.add_argument( "-fhd", "--fixstdhd", help= "calculates pp for std hd high scores (14/05/2018 pp algorithm changes)", required=False, action="store_true") parser.add_argument("-w", "--workers", help="number of workers. {} by default. Max {}".format( MAX_WORKERS // 2, MAX_WORKERS), required=False) parser.add_argument("-v", "--verbose", help="verbose/debug mode", required=False, action="store_true") parser.add_argument( "-nodl", "--no-download", help= "do not download non-existing maps. This will cause all scores on non-cached " "map to fail, but will speed everything up if all maps are present.", required=False, action="store_true") args = parser.parse_args() # Logging progressbar.streams.wrap_stderr() logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) logging.root.setLevel( level=logging.DEBUG if args.verbose else logging.INFO) logging.info("Running under {}".format("UNIX" if UNIX else "WIN32")) # Load config logging.info("Reading config file") glob.conf = Config() # Get workers from arguments if set workers_number = MAX_WORKERS // 2 if args.workers is not None: workers_number = int(args.workers) # Disable MySQL db warnings (it spams 'Unsafe statement written to the binary log using statement...' # because we use UPDATE with LIMIT 1 when updating performance points after recalculation warnings.filterwarnings("ignore", category=pymysql.Warning) # Connect to MySQL logging.info("Connecting to MySQL db") glob.db = dbConnector.db( host=glob.conf["DB_HOST"], port=glob.conf["DB_PORT"], user=glob.conf["DB_USERNAME"], password=glob.conf["DB_PASSWORD"], database=glob.conf["DB_NAME"], autocommit=True, charset="utf8", ) # Set verbose glob.conf["DEBUG"] = args.verbose # Get recalculator recalculators_gen = { "zero": lambda: SimpleRecalculator(("scores.completed = 3", "pp = 0")), "recalc": lambda: SimpleRecalculator(("scores.completed = 3", )), "mods": lambda: SimpleRecalculator(("scores.completed = 3", "mods & %s > 0"), (args.mods, )), "id": lambda: SimpleRecalculator(("scores.id = %s", ), (args.id, )), "gamemode": lambda: SimpleRecalculator(( "scores.completed = 3", "scores.play_mode = %s", ), (args.gamemode, )), "userid": lambda: SimpleRecalculator(( "scores.completed = 3", "scores.userid = %s", ), (args.userid, )), "beatmapid": lambda: SimpleRecalculator(( "scores.completed = 3", "beatmaps.beatmap_id = %s", ), (args.beatmapid, )), "fixstdhd": lambda: SimpleRecalculator( ("scores.completed = 3", "scores.play_mode = 0", "scores.mods & 8 > 0")), "relax": lambda: SimpleRecalculator( ("scores.is_relax = 1", "scores.completed = 3")) } recalculator = None for k, v in vars(args).items(): if v is not None and ((type(v) is bool and v) or type(v) is not bool): if k in recalculators_gen: recalculator = recalculators_gen[k]() break # Execute mass recalc if recalculator is not None: mass_recalc(recalculator, workers_number, no_download=args.no_download) else: logging.warning("No recalc option specified") parser.print_help()
def main(): parser = argparse.ArgumentParser( description=consoleHelper.ASCII + "\n\nLatest Essential Tatoe Server v{}\nBy The Ripple Team".format( glob.VERSION), formatter_class=argparse.RawTextHelpFormatter) parser.add_argument("-p", "--port", help="Run on a specific port (bypasses config.ini)", required=False) parser.add_argument( "-s", "--stats-port", help="Run prometheus on a specific port (bypasses config.ini)", required=False) parser.add_argument("-q", "--quiet", help="Log less stuff during startup", required=False, default=False, action="store_true") cli_args = parser.parse_args() # AGPL license agreement try: agpl.check_license("ripple", "LETS") except agpl.LicenseError as e: logging.error(str(e)) sys.exit(1) try: if not cli_args.quiet: consoleHelper.printServerStartHeader(True) def loudLog(s, f=logging.info): if not cli_args.quiet: f(s) # Read config loudLog("Reading config file... ") glob.conf = Config() # Create data/oppai maps folder if needed loudLog("Checking folders... ") paths = (".data", glob.conf["BEATMAPS_FOLDER"], glob.conf["SCREENSHOTS_FOLDER"], glob.conf["FAILED_REPLAYS_FOLDER"], glob.conf["REPLAYS_FOLDER"]) for i in paths: if not os.path.exists(i): os.makedirs(i, 0o770) # Connect to db try: loudLog("Connecting to MySQL database") glob.db = dbConnector.db(host=glob.conf["DB_HOST"], port=glob.conf["DB_PORT"], user=glob.conf["DB_USERNAME"], password=glob.conf["DB_PASSWORD"], database=glob.conf["DB_NAME"], autocommit=True, charset="utf8") glob.db.fetch("SELECT 1") except: # Exception while connecting to db logging.error( "Error while connection to database. Please check your config.ini and run the server again" ) raise # Connect to redis try: loudLog("Connecting to redis") glob.redis = redis.Redis(glob.conf["REDIS_HOST"], glob.conf["REDIS_PORT"], glob.conf["REDIS_DATABASE"], glob.conf["REDIS_PASSWORD"]) glob.redis.ping() except: # Exception while connecting to db logging.error( "Error while connection to redis. Please check your config.ini and run the server again" ) raise # Empty redis cache try: glob.redis.eval( "return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "lets:*") except redis.exceptions.ResponseError: # Script returns error if there are no keys starting with peppy:* pass # Save lets version in redis glob.redis.set("lets:version", glob.VERSION) # Create threads pool try: loudLog("Creating threads pool") glob.pool = ThreadPool(glob.conf["THREADS"]) except: logging.error( "Error while creating threads pool. Please check your config.ini and run the server again" ) raise # Check osuapi if not glob.conf["OSU_API_ENABLE"]: logging.warning( "osu!api features are disabled. If you don't have a " "valid beatmaps table, all beatmaps will show as unranked") if glob.conf["BEATMAP_CACHE_EXPIRE"] > 0: logging.warning( "IMPORTANT! Your beatmapcacheexpire in config.ini is > 0 and osu!api " "features are disabled.\nWe do not recommend this, because too old " "beatmaps will be shown as unranked.\nSet beatmapcacheexpire to 0 to " "disable beatmap latest update check and fix that issue.") # Load achievements #loudLog("Loading achievements") #try: # secret.achievements.utils.load_achievements() #except: # logging.error("Error while loading achievements") # raise glob.ACHIEVEMENTS_VERSION = "0.0.0" # Set achievements version glob.redis.set("lets:achievements_version", glob.ACHIEVEMENTS_VERSION) loudLog("Achievements version is {}".format(glob.ACHIEVEMENTS_VERSION)) # Check if s3 is enabled if not glob.conf.s3_enabled: loudLog("S3 is disabled!", logging.warning) else: c = glob.db.fetch( "SELECT COUNT(*) AS c FROM s3_replay_buckets WHERE max_score_id IS NULL" )["c"] if c != 1: logging.error( "There must be only one bucket flagged as WRITE bucket! You have {}." .format(c), ) sys.exit() # Discord if glob.conf.schiavo_enabled: glob.schiavo = schiavo.schiavo(glob.conf["SCHIAVO_URL"], "**lets**") else: logging.warning("Schiavo logging is disabled!") # Server port try: if cli_args.port: loudLog( "Running on port {}, bypassing config.ini".format( cli_args.port), logging.warning) glob.serverPort = int(cli_args.port) else: glob.serverPort = glob.conf["HTTP_PORT"] except: logging.error( "Invalid server port! Please check your config.ini and run the server again" ) raise # Prometheus port try: if cli_args.stats_port: loudLog( "Running stats exporter on port {}, bypassing config.ini". format(cli_args.stats_port), logging.warning) glob.statsPort = int(cli_args.stats_port) elif glob.conf["PROMETHEUS_PORT"]: glob.statsPort = int(glob.conf["PROMETHEUS_PORT"]) except: logging.error( "Invalid stats port! Please check your config.ini and run the server again" ) raise # Make app glob.application = make_app() # Set up sentry if glob.conf.sentry_enabled: glob.application.sentry_client = AsyncSentryClient( glob.conf["SENTRY_DSN"], release=glob.VERSION) else: loudLog("Sentry logging is disabled!", logging.warning) # Set up Datadog if glob.conf.datadog_enabled: glob.dog = datadogClient.datadogClient( glob.conf["DATADOG_API_KEY"], glob.conf["DATADOG_APP_KEY"], constant_tags=["worker:{}".format(glob.serverPort)]) else: glob.dog = datadogClient.datadogClient() loudLog("Datadog stats tracking is disabled!", logging.warning) # Connect to pubsub channels t = pubSub.listener( glob.redis, { "lets:beatmap_updates": beatmapUpdateHandler.handler(), "lets:reload_aql": lambda x: x == b"reload" and glob.aqlThresholds.reload(), }) t.setDaemon(True) t.start() # Check debug mods if glob.conf["DEBUG"]: logging.warning("Server running in debug mode.") # Close main thread db connection as we don't need it anymore glob.threadScope.dbClose() # Server start message and console output logging.info("L.E.T.S. is listening for clients on {}:{}...".format( glob.conf["HTTP_HOST"], glob.serverPort)) # log.discord("bunker", "Server started!") # Start Tornado def term(_, __): tornado.ioloop.IOLoop.instance().add_callback_from_signal( lambda: tornado.ioloop.IOLoop.instance().stop()) signal.signal(signal.SIGINT, term) signal.signal(signal.SIGTERM, term) if glob.statsPort is not None: logging.info("Stats exporter listening on 0.0.0.0:{}".format( glob.statsPort)) prometheus_client.start_http_server(glob.statsPort, addr="0.0.0.0") glob.application.listen(glob.serverPort, address=glob.conf["HTTP_HOST"]) tornado.ioloop.IOLoop.instance().start() logging.debug("IOLoop stopped") finally: # Perform some clean up logging.info("Disposing server") glob.fileBuffers.flushAll() if glob.redis.connection_pool is not None: glob.redis.connection_pool.disconnect() # TODO: properly dispose mysql connections if glob.pool is not None: # Close db conn in each thread glob.pool.imap(lambda *_: glob.threadScope.dbClose(), [None] * glob.conf["THREADS"], chunksize=1) # Wait for everything else to finish (should always terminate immediately) glob.pool.close() glob.pool.join() logging.info("Goodbye!")
def main() -> int: # AGPL license agreement try: agpl.check_license("ripple", "LETS") except agpl.LicenseError as e: print(str(e)) return 1 try: consoleHelper.printServerStartHeader(True) # Read config consoleHelper.printNoNl("> Reading config file... ") glob.conf = config.config("config.ini") if glob.conf.default: # We have generated a default config.ini, quit server consoleHelper.printWarning() consoleHelper.printColored( "[!] config.ini not found. A default one has been generated.", bcolors.YELLOW) consoleHelper.printColored( "[!] Please edit your config.ini and run the server again.", bcolors.YELLOW) return 1 # If we haven't generated a default config.ini, check if it's valid if not glob.conf.checkConfig(): consoleHelper.printError() consoleHelper.printColored( "[!] Invalid config.ini. Please configure it properly", bcolors.RED) consoleHelper.printColored( "[!] Delete your config.ini to generate a default one", bcolors.RED) return 1 else: consoleHelper.printDone() # Read additional config file consoleHelper.printNoNl("> Loading additional config file... ") try: if not os.path.isfile(glob.conf.config["custom"]["config"]): consoleHelper.printWarning() consoleHelper.printColored( "[!] Missing config file at {}; A default one has been generated at this location." .format(glob.conf.config["custom"]["config"]), bcolors.YELLOW) shutil.copy("common/default_config.json", glob.conf.config["custom"]["config"]) with open(glob.conf.config["custom"]["config"], "r") as f: glob.conf.extra = json.load(f) consoleHelper.printDone() except: consoleHelper.printWarning() consoleHelper.printColored( "[!] Unable to load custom config at {}".format( glob.conf.config["custom"]["config"]), bcolors.RED) return 1 # Create data/oppai maps folder if needed consoleHelper.printNoNl("> Checking folders... ") paths = [ ".data", ".data/oppai", ".data/catch_the_pp", glob.conf.config["server"]["replayspath"], "{}_relax".format(glob.conf.config["server"]["replayspath"]), glob.conf.config["server"]["beatmapspath"], glob.conf.config["server"]["screenshotspath"] ] for i in paths: if not os.path.exists(i): os.makedirs(i, 0o770) consoleHelper.printDone() # Connect to db try: consoleHelper.printNoNl("> Connecting to MySQL database... ") glob.db = dbConnector.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"])) consoleHelper.printNoNl(" ") consoleHelper.printDone() except: # Exception while connecting to db consoleHelper.printError() consoleHelper.printColored( "[!] Error while connection to database. Please check your config.ini and run the server again", bcolors.RED) raise # Connect to redis try: consoleHelper.printNoNl("> Connecting to redis... ") glob.redis = redis.Redis(glob.conf.config["redis"]["host"], glob.conf.config["redis"]["port"], glob.conf.config["redis"]["database"], glob.conf.config["redis"]["password"]) glob.redis.ping() consoleHelper.printNoNl(" ") consoleHelper.printDone() except: # Exception while connecting to db consoleHelper.printError() consoleHelper.printColored( "[!] Error while connection to redis. Please check your config.ini and run the server again", bcolors.RED) raise # Empty redis cache #TODO: do we need this? try: glob.redis.eval( "return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "lets:*") except redis.exceptions.ResponseError: # Script returns error if there are no keys starting with peppy:* pass # Save lets version in redis glob.redis.set("lets:version", glob.VERSION) # Create threads pool try: consoleHelper.printNoNl("> Creating threads pool... ") glob.pool = ThreadPool(int(glob.conf.config["server"]["threads"])) consoleHelper.printDone() except: consoleHelper.printError() consoleHelper.printColored( "[!] Error while creating threads pool. Please check your config.ini and run the server again", bcolors.RED) # Load achievements consoleHelper.printNoNl("> Loading achievements... ") try: achievements = glob.db.fetchAll("SELECT * FROM achievements") for achievement in achievements: condition = eval( f"lambda score, mode_vn, stats: {achievement.pop('cond')}") glob.achievements.append( Achievement(_id=achievement['id'], file=achievement['icon'], name=achievement['name'], desc=achievement['description'], cond=condition)) except Exception as e: consoleHelper.printError() consoleHelper.printColored( "[!] Error while loading achievements! ({})".format( traceback.format_exc()), bcolors.RED, ) return 1 consoleHelper.printDone() # Set achievements version glob.redis.set("lets:achievements_version", glob.ACHIEVEMENTS_VERSION) consoleHelper.printColored( "Achievements version is {}".format(glob.ACHIEVEMENTS_VERSION), bcolors.YELLOW) # Print disallowed mods into console (Used to also assign it into variable but has been moved elsewhere) unranked_mods = [ key for key, value in glob.conf.extra["common"] ["rankable-mods"].items() if not value ] consoleHelper.printColored( "Unranked mods: {}".format(", ".join(unranked_mods)), bcolors.YELLOW) # Print allowed beatmap rank statuses allowed_beatmap_rank = [ key for key, value in glob.conf.extra["lets"] ["allowed-beatmap-rankstatus"].items() if value ] consoleHelper.printColored( "Allowed beatmap rank statuses: {}".format( ", ".join(allowed_beatmap_rank)), bcolors.YELLOW) # Make array of bools to respective rank id's glob.conf.extra["_allowed_beatmap_rank"] = [ getattr(rankedStatuses, key) for key in allowed_beatmap_rank ] # Store the allowed beatmap rank id's into glob # Discord if generalUtils.stringToBool(glob.conf.config["discord"]["enable"]): glob.schiavo = schiavo.schiavo( glob.conf.config["discord"]["boturl"], "**lets**") else: consoleHelper.printColored( "[!] Warning! Discord logging is disabled!", bcolors.YELLOW) # Check debug mods glob.debug = generalUtils.stringToBool( glob.conf.config["server"]["debug"]) if glob.debug: consoleHelper.printColored( "[!] Warning! Server running in debug mode!", bcolors.YELLOW) # Server port try: serverPort = int(glob.conf.config["server"]["port"]) except: consoleHelper.printColored( "[!] Invalid server port! Please check your config.ini and run the server again", bcolors.RED) # Make app glob.application = make_app() # Set up sentry try: glob.sentry = generalUtils.stringToBool( glob.conf.config["sentry"]["enable"]) if glob.sentry: glob.application.sentry_client = AsyncSentryClient( glob.conf.config["sentry"]["dsn"], release=glob.VERSION) else: consoleHelper.printColored( "[!] Warning! Sentry logging is disabled!", bcolors.YELLOW) except: consoleHelper.printColored( "[!] Error while starting Sentry client! Please check your config.ini and run the server again", bcolors.RED) # Set up Datadog try: if generalUtils.stringToBool( glob.conf.config["datadog"]["enable"]): glob.dog = datadogClient.datadogClient( glob.conf.config["datadog"]["apikey"], glob.conf.config["datadog"]["appkey"]) else: consoleHelper.printColored( "[!] Warning! Datadog stats tracking is disabled!", bcolors.YELLOW) except: consoleHelper.printColored( "[!] Error while starting Datadog client! Please check your config.ini and run the server again", bcolors.RED) # Connect to pubsub channels pubSub.listener(glob.redis, { "lets:beatmap_updates": beatmapUpdateHandler.handler(), }).start() # Prometheus port statsPort = None try: if glob.conf.config["prometheus"]["port"]: statsPort = int(glob.conf.config["prometheus"]["port"]) except: consoleHelper.printColored( "Invalid stats port! Please check your config.ini and run the server again", bcolors.YELLOW) raise if statsPort: consoleHelper.printColored( "Stats exporter listening on localhost:{}".format(statsPort), bcolors.GREEN) prometheus_client.start_http_server(statsPort, addr="127.0.0.1") # Server start message and console output consoleHelper.printColored( "> L.E.T.S. is listening for clients on {}:{}...".format( glob.conf.config["server"]["host"], serverPort), bcolors.GREEN) # Start Tornado glob.application.listen(serverPort, address=glob.conf.config["server"]["host"]) tornado.ioloop.IOLoop.instance().start() finally: # Perform some clean up print("> Disposing server... ") glob.fileBuffers.flushAll() consoleHelper.printColored("Goodbye!", bcolors.GREEN) return 0
def main(): """A main function to execute code.""" try: # Server start consoleHelper.printServerStartHeader(True) # Create data folder if needed log.info("Checking folders... ") paths = (".data", ) for i in paths: if not os.path.exists(i): os.makedirs(i, 0o770) log.info("Complete!") # Connect to db and redis try: log.info("Connecting to MySQL database... ") glob.db = dbConnector.db(glob.config.DB_HOST, glob.config.DB_USERNAME, glob.config.DB_PASSWORD, glob.config.DB_DATABASE, glob.config.DB_WORKERS) log.info("Connecting to redis... ") glob.redis = redis.Redis(glob.config.REDIS_HOST, glob.config.REDIS_PORT, glob.config.REDIS_DB, glob.config.REDIS_PASSWORD) glob.redis.ping() except Exception: # Exception while connecting to db log.error( "Error while connection to database and redis. Please ensure your config and try again." ) # Empty redis cache try: glob.redis.set("ripple:online_users", 0) glob.redis.eval( "return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "peppy:*") except redis.exceptions.ResponseError: # Script returns error if there are no keys starting with peppy:* pass # Save peppy version in redis glob.redis.set("peppy:version", glob.__version__) # Load bancho_settings try: log.info("Loading bancho settings from DB... ") glob.banchoConf = banchoConfig.banchoConfig() log.info("Complete!") except: log.error( "Error while loading bancho_settings. Please make sure the table in DB has all the required rows" ) raise # Delete old bancho sessions log.info("Deleting cached bancho sessions from DB... ") glob.tokens.deleteBanchoSessions() log.info("Complete!") # Create threads pool try: log.info("Creating threads pool... ") glob.pool = ThreadPool(glob.config.THREADS_COUNT) log.info("Complete!") except ValueError: log.error( "Error while creating threads pool. Please check your config.ini and run the server again" ) # Start fokabot log.info("Connecting RealistikBot...") fokabot.connect() log.info("Complete!") # Initialize chat channels log.info("Initializing chat channels... ") glob.channels.loadChannels() log.info("Complete!") # Initialize stremas log.info("Creating packets streams... ") glob.streams.add("main") glob.streams.add("lobby") log.info("Complete!") # Initialize user timeout check loop log.info("Initializing user timeout check loop... ") glob.tokens.usersTimeoutCheckLoop() log.info("Complete!") # Initialize spam protection reset loop log.info("Initializing spam protection reset loop... ") glob.tokens.spamProtectionResetLoop() log.info("Complete!") # Initialize multiplayer cleanup loop log.info("Initializing multiplayer cleanup loop... ") glob.matches.cleanupLoop() log.info("Complete!") # Debug mode glob.debug = DEBUG if glob.debug: log.warning("Server running in debug mode!") # Make app glob.application = make_app() # Server start message and console output log.info( f"pep.py listening for HTTP(s) clients on 127.0.0.1:{glob.config.PORT}..." ) # Connect to pubsub channels pubSub.listener( glob.redis, { "peppy:disconnect": disconnectHandler.handler(), "peppy:change_username": changeUsernameHandler.handler(), "peppy:reload_settings": lambda x: x == b"reload" and glob.banchoConf.reload(), "peppy:update_cached_stats": updateStatsHandler.handler(), "peppy:silence": updateSilenceHandler.handler(), "peppy:ban": banHandler.handler(), "peppy:notification": notificationHandler.handler(), "peppy:set_main_menu_icon": setMainMenuIconHandler.handler(), "peppy:refresh_privs": refreshPrivsHandler.handler(), "peppy:change_pass": changePassword.handler(), "peppy:bot_msg": bot_msg_handler.handler() }).start() # We will initialise namespace for fancy stuff. UPDATE: F**K OFF WEIRD PYTHON MODULE. glob.namespace = globals() | { mod: __import__(mod) for mod in sys.modules if mod != "glob" } # Start tornado glob.application.listen(glob.config.PORT) tornado.ioloop.IOLoop.instance().start() finally: system.dispose()