[Enhance][Bug fix] Improve setting file & ...

- Fix a small bug that `best30` of API cannot have scores whose songs are not in database
- At present the setting file can be a module or a file with some of options
- Limiter can have multiple rules together now
This commit is contained in:
Lost-MSth
2022-10-16 15:49:49 +08:00
parent 68a83a29d2
commit ba36190f30
23 changed files with 458 additions and 86 deletions

View File

@@ -1,6 +1,6 @@
from setting import Config
from .error import ArcError, InputError, NoData, ItemNotEnough
from .config_manager import Config
from .constant import Constant
from .error import ArcError, InputError, ItemNotEnough, NoData
from .item import Item, ItemCore

View File

@@ -0,0 +1,81 @@
class Config:
'''
Default config
'''
HOST = '0.0.0.0'
PORT = 80
GAME_API_PREFIX = '/join/21'
ALLOW_APPVERSION = [] # list[str]
SET_LINKPLAY_SERVER_AS_SUB_PROCESS = True
LINKPLAY_HOST = '0.0.0.0'
LINKPLAY_UDP_PORT = 10900
LINKPLAY_TCP_PORT = 10901
LINKPLAY_AUTHENTICATION = 'my_link_play_server'
LINKPLAY_DISPLAY_HOST = ''
SSL_CERT = ''
SSL_KEY = ''
IS_APRILFOOLS = True
WORLD_RANK_MAX = 200
AVAILABLE_MAP = [] # list[str]
USERNAME = 'admin'
PASSWORD = 'admin'
SECRET_KEY = '1145141919810'
API_TOKEN = ''
DOWNLOAD_LINK_PREFIX = ''
DOWNLOAD_TIMES_LIMIT = 3000
DOWNLOAD_TIME_GAP_LIMIT = 1000
LOGIN_DEVICE_NUMBER_LIMIT = 1
ALLOW_LOGIN_SAME_DEVICE = False
ALLOW_BAN_MULTIDEVICE_USER_AUTO = True
ALLOW_INFO_LOG = False
ALLOW_WARNING_LOG = False
DEFAULT_MEMORIES = 0
UPDATE_WITH_NEW_CHARACTER_DATA = True
CHARACTER_FULL_UNLOCK = True
WORLD_SONG_FULL_UNLOCK = True
WORLD_SCENERY_FULL_UNLOCK = True
SAVE_FULL_UNLOCK = False
# ------------------------------------------
# You can change this to make another PTT mechanism.
BEST30_WEIGHT = 1 / 40
RECENT10_WEIGHT = 1 / 40
MAX_FRIEND_COUNT = 50
WORLD_MAP_FOLDER_PATH = './database/map/'
SONG_FILE_FOLDER_PATH = './database/songs/'
SONGLIST_FILE_PATH = './database/songs/songlist'
SQLITE_DATABASE_PATH = './database/arcaea_database.db'
class ConfigManager:
@staticmethod
def load(config):
for k, v in config.__dict__.items():
if k.startswith('__') or k.endswith('__'):
continue
if hasattr(Config, k):
setattr(Config, k, v)

View File

@@ -1,4 +1,4 @@
from setting import Config
from .config_manager import Config
class Constant:
@@ -9,6 +9,8 @@ class Constant:
STAMINA_RECOVER_TICK = 1800000
COURSE_STAMINA_COST = 4
CORE_EXP = 250
LEVEL_STEPS = {1: 0, 2: 50, 3: 100, 4: 150, 5: 200, 6: 300, 7: 450, 8: 650, 9: 900, 10: 1200, 11: 1600, 12: 2100, 13: 2700, 14: 3400, 15: 4200, 16: 5100,
@@ -19,19 +21,18 @@ class Constant:
AYU_UNCAP_BONUS_PROGRESS = 5
SKILL_FATALIS_WORLD_LOCKED_TIME = 3600000
MAX_FRIEND_COUNT = 50
MAX_FRIEND_COUNT = Config.MAX_FRIEND_COUNT
MY_RANK_MAX_LOCAL_POSITION = 5
MY_RANK_MAX_GLOBAL_POSITION = 9999
# You can change this to make another PTT mechanism.
BEST30_WEIGHT = 1 / 40
RECENT10_WEIGHT = 1 / 40
BEST30_WEIGHT = Config.BEST30_WEIGHT
RECENT10_WEIGHT = Config.RECENT10_WEIGHT
WORLD_MAP_FOLDER_PATH = './database/map/'
SONG_FILE_FOLDER_PATH = './database/songs/'
SONGLIST_FILE_PATH = './database/songs/songlist'
SQLITE_DATABASE_PATH = './database/arcaea_database.db'
WORLD_MAP_FOLDER_PATH = Config.WORLD_MAP_FOLDER_PATH
SONG_FILE_FOLDER_PATH = Config.SONG_FILE_FOLDER_PATH
SONGLIST_FILE_PATH = Config.SONGLIST_FILE_PATH
SQLITE_DATABASE_PATH = Config.SQLITE_DATABASE_PATH
DOWNLOAD_TIMES_LIMIT = Config.DOWNLOAD_TIMES_LIMIT
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
@@ -45,8 +46,6 @@ class Constant:
LINKPLAY_UDP_PORT = Config.LINKPLAY_UDP_PORT
LINKPLAY_AUTHENTICATION = Config.LINKPLAY_AUTHENTICATION
COURSE_STAMINA_COST = 4
# Well, I can't say a word when I see this.
FINALE_SWITCH = [
(0x0015F0, 0x00B032), (0x014C9A, 0x014408), (0x062585, 0x02783B),

View File

@@ -1,91 +1,111 @@
class ArcError(Exception):
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
self.message = message
self.error_code = error_code
self.api_error_code = api_error_code
self.message: str = message
self.error_code: int = error_code
self.api_error_code: int = api_error_code
self.extra_data = extra_data
self.status = status
self.status: int = status
def __str__(self) -> str:
return repr(self.message)
class InputError(ArcError):
# 输入类型错误
'''输入类型错误'''
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class DataExist(ArcError):
# 数据存在
'''数据存在'''
pass
class NoData(ArcError):
# 数据不存在
'''数据不存在'''
def __init__(self, message=None, error_code=108, api_error_code=-2, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class PostError(ArcError):
# 缺少输入
'''缺少输入'''
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class UserBan(ArcError):
# 用户封禁
'''用户封禁'''
def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class ItemNotEnough(ArcError):
# 物品数量不足
'''物品数量不足'''
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class ItemUnavailable(ArcError):
# 物品不可用
'''物品不可用'''
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class RedeemUnavailable(ArcError):
# 兑换码不可用
'''兑换码不可用'''
def __init__(self, message=None, error_code=505, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class MapLocked(ArcError):
# 地图锁定
'''地图锁定'''
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class StaminaNotEnough(ArcError):
# 体力不足
'''体力不足'''
def __init__(self, message=None, error_code=107, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class TicketNotEnough(ArcError):
# 记忆源点不足
'''记忆源点不足'''
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class FriendError(ArcError):
# 好友系统出错
'''好友系统出错'''
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class NoAccess(ArcError):
# 无权限
pass
'''无权限'''
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=403) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)
class Timeout(ArcError):
# 超时
'''超时'''
pass
class RateLimit(ArcError):
'''频率达到限制'''
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=429) -> None:
super().__init__(message, error_code, api_error_code, extra_data, status)

View File

@@ -1,5 +1,5 @@
from .config_manager import Config
from .error import InputError, ItemNotEnough, ItemUnavailable, NoData
from setting import Config
class Item:

View File

@@ -1,4 +1,4 @@
from limits import parse, strategies
from limits import parse_many, strategies
from limits.storage import storage_from_string
@@ -6,23 +6,26 @@ class ArcLimiter:
storage = storage_from_string("memory://")
strategy = strategies.FixedWindowRateLimiter(storage)
def __init__(self, limit: str = None, namespace: str = None):
self._limit = None
self.limit = limit
def __init__(self, limit_str: str = None, namespace: str = None):
self._limits = None
self.limits = limit_str
self.namespace = namespace
@property
def limit(self):
return self._limit
def limits(self):
return self._limits
@limit.setter
def limit(self, value):
@limits.setter
def limits(self, value: str):
if value is None:
return
self._limit = parse(value)
self._limits = parse_many(value)
def hit(self, key: str, cost: int = 1) -> bool:
return self.strategy.hit(self.limit, self.namespace, key, cost=cost)
flag = True
for limit in self.limits:
flag &= self.strategy.hit(limit, self.namespace, key, cost)
return flag
def test(self, key: str) -> bool:
return self.strategy.test(self.limit, self.namespace, key)
return all(self.strategy.test(limit, self.namespace, key) for limit in self.limits)

View File

@@ -1,10 +1,8 @@
import json
from time import time
from setting import Config
from core.constant import Constant
from .config_manager import Config
from .constant import Constant
from .error import InputError
from .util import md5

View File

@@ -580,4 +580,5 @@ class UserScoreList:
for score in self.scores:
self.c.execute(
'''select name from chart where song_id = ?''', (score.song.song_id,))
score.song.song_name = self.c.fetchone()[0]
x = self.c.fetchone()
score.song.song_name = x[0] if x else ''

View File

@@ -3,9 +3,8 @@ import hashlib
import time
from os import urandom
from setting import Config
from .character import UserCharacter, UserCharacterList
from .config_manager import Config
from .constant import Constant
from .error import (ArcError, DataExist, FriendError, InputError, NoAccess,
NoData, UserBan)