Update to v2.8.6 without release

- Code refactoring
- Update the song database
- Fix a bug of the friend system which arose in the last version
- Fix a bug of duplicate calculating finishing states when players finish playing the chart in Link Play.
This commit is contained in:
Lost-MSth
2022-05-26 18:56:33 +08:00
parent 96fbd26342
commit 9de62d3645
18 changed files with 756 additions and 218 deletions

View File

@@ -1,54 +1,72 @@
from setting import Config
from .error import ArcError, NoData
from .error import ArcError, InputError, NoData, ItemNotEnough
from .constant import Constant
from .item import Item
from .item import Item, ItemCore
class Level:
max_level = None
mid_level = 20
min_level = 1
level = None
exp = None
def __init__(self) -> None:
pass
self.max_level = None
self.level = None
self.exp = None
@property
def level_exp(self):
return Constant.LEVEL_STEPS[self.level]
def add_exp(self, exp_addition: float):
# 添加经验计算
exp = self.exp + exp_addition
if exp >= Constant.LEVEL_STEPS[self.max_level]:
self.exp = Constant.LEVEL_STEPS[self.max_level]
self.level = self.max_level
a = []
b = []
for i in Constant.LEVEL_STEPS:
a.append(i)
b.append(Constant.LEVEL_STEPS[i])
if exp < b[0]: # 向下溢出是异常状态不该被try捕获不然数据库无法回滚
raise ValueError('EXP value error.')
i = len(a) - 1
while exp < b[i]:
i -= 1
self.exp = exp
self.level = a[i]
class Skill:
skill_id = None
skill_id_uncap = None
skill_unlock_level = None
skill_requires_uncap = None
def __init__(self) -> None:
pass
self.skill_id = None
self.skill_id_uncap = None
self.skill_unlock_level = None
self.skill_requires_uncap = None
class Core(Item):
item_type = 'core'
amount = None
def __init__(self, core_type: str = '', amount: int = 0) -> None:
super().__init__()
self.item_id = core_type
self.amount = amount
self.is_available = True
def to_dict(self):
return {'core_type': self.item_id, 'amount': self.amount}
class CharacterValue:
start = None
mid = None
end = None
def __init__(self) -> None:
pass
def __init__(self, start: float = 0, mid: float = 0, end: float = 0) -> None:
self.set_parameter(start, mid, end)
@staticmethod
def _calc_char_value_20(level, stata, statb, lva=1, lvb=20):
@@ -82,20 +100,19 @@ class CharacterValue:
class Character:
character_id = None
name = None
char_type = None
is_uncapped = None
is_uncapped_override = None
skill = Skill()
level = Level()
frag = CharacterValue()
prog = CharacterValue()
overdrive = CharacterValue()
uncap_cores = None
def __init__(self) -> None:
pass
self.character_id = None
self.name = None
self.char_type = None
self.is_uncapped = None
self.is_uncapped_override = None
self.skill = Skill()
self.level = Level()
self.frag = CharacterValue()
self.prog = CharacterValue()
self.overdrive = CharacterValue()
self.uncap_cores = []
self.voice = None
@property
def uncap_cores_to_dict(self):
@@ -103,6 +120,7 @@ class Character:
class UserCharacter(Character):
database_table_name = 'user_char_full' if Config.CHARACTER_FULL_UNLOCK else 'user_char'
def __init__(self, c, character_id=None) -> None:
super().__init__()
@@ -122,12 +140,10 @@ class UserCharacter(Character):
def select_character_uncap_condition(self, user):
# parameter: user - User类或子类的实例
# 获取此角色的觉醒信息
if Config.CHARACTER_FULL_UNLOCK:
self.c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
{'a': user.user_id, 'b': self.character_id})
else:
self.c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
{'a': user.user_id, 'b': self.character_id})
self.c.execute('''select is_uncapped, is_uncapped_override from %s where user_id = :a and character_id = :b''' % self.database_table_name,
{'a': user.user_id, 'b': self.character_id})
x = self.c.fetchone()
if not x:
raise NoData('The character of the user does not exist.')
@@ -138,12 +154,10 @@ class UserCharacter(Character):
def select_character_info(self, user):
# parameter: user - User类或子类的实例
# 获取所给用户此角色信息
if not Config.CHARACTER_FULL_UNLOCK:
self.c.execute('''select * from user_char a,character b where a.user_id=? and a.character_id=b.character_id and a.character_id=?''',
(user.user_id, self.character_id))
else:
self.c.execute('''select * from user_char_full a,character b where a.user_id=? and a.character_id=b.character_id and a.character_id=?''',
(user.user_id, self.character_id))
self.c.execute('''select * from %s a,character b where a.user_id=? and a.character_id=b.character_id and a.character_id=?''' % self.database_table_name,
(user.user_id, self.character_id))
y = self.c.fetchone()
if y is None:
raise NoData('The character of the user does not exist.')
@@ -162,37 +176,40 @@ class UserCharacter(Character):
self.skill.skill_id_uncap = y[21]
self.skill.skill_unlock_level = y[19]
self.skill.skill_requires_uncap = y[20] == 1
if self.character_id == 21 or self.character_id == 46:
self.voice = [0, 1, 2, 3, 100, 1000, 1001]
self.select_character_core()
@property
def to_dict(self):
return {"is_uncapped_override": self.is_uncapped_override,
"is_uncapped": self.is_uncapped,
"uncap_cores": self.uncap_cores_to_dict,
"char_type": self.char_type,
"skill_id_uncap": self.skill.skill_id_uncap,
"skill_requires_uncap": self.skill.skill_requires_uncap,
"skill_unlock_level": self.skill.skill_unlock_level,
"skill_id": self.skill.skill_id,
"overdrive": self.overdrive.get_value(self.level),
"prog": self.overdrive.get_value(self.level),
"frag": self.overdrive.get_value(self.level),
"level_exp": self.level.level_exp,
"exp": self.level.exp,
"level": self.level.level,
"name": self.name,
"character_id": self.character_id
}
r = {"is_uncapped_override": self.is_uncapped_override,
"is_uncapped": self.is_uncapped,
"uncap_cores": self.uncap_cores_to_dict,
"char_type": self.char_type,
"skill_id_uncap": self.skill.skill_id_uncap,
"skill_requires_uncap": self.skill.skill_requires_uncap,
"skill_unlock_level": self.skill.skill_unlock_level,
"skill_id": self.skill.skill_id,
"overdrive": self.overdrive.get_value(self.level),
"prog": self.overdrive.get_value(self.level),
"frag": self.overdrive.get_value(self.level),
"level_exp": self.level.level_exp,
"exp": self.level.exp,
"level": self.level.level,
"name": self.name,
"character_id": self.character_id
}
if self.voice:
r['voice'] = self.voice
return r
def change_uncap_override(self, user):
# parameter: user - User类或子类的实例
# 切换觉醒状态
if not Config.CHARACTER_FULL_UNLOCK:
self.c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
{'a': user.user_id, 'b': self.character_id})
else:
self.c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
{'a': user.user_id, 'b': self.character_id})
self.c.execute('''select is_uncapped, is_uncapped_override from %s where user_id = :a and character_id = :b''' % self.database_table_name,
{'a': user.user_id, 'b': self.character_id})
x = self.c.fetchone()
if x is None or x[0] == 0:
@@ -201,13 +218,80 @@ class UserCharacter(Character):
self.c.execute('''update user set is_char_uncapped_override = :a where user_id = :b''', {
'a': 1 if x[1] == 0 else 0, 'b': user.user_id})
if not Config.CHARACTER_FULL_UNLOCK:
self.c.execute('''update user_char set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
'a': 1 if x[1] == 0 else 0, 'b': user.user_id, 'c': self.character_id})
else:
self.c.execute('''update user_char_full set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
'a': 1 if x[1] == 0 else 0, 'b': user.user_id, 'c': self.character_id})
self.c.execute('''update %s set is_uncapped_override = :a where user_id = :b and character_id = :c''' % self.database_table_name, {
'a': 1 if x[1] == 0 else 0, 'b': user.user_id, 'c': self.character_id})
def character_uncap(self):
self.is_uncapped_override = x[1] == 0
def character_uncap(self, user):
# parameter: user - User类或子类的实例
# 觉醒角色
pass
if Config.CHARACTER_FULL_UNLOCK:
# 全解锁了你觉醒个鬼啊
raise ArcError('All characters are available.')
if not self.uncap_cores:
self.select_character_core()
if self.is_uncapped is None:
self.c.execute(
'''select is_uncapped from user_char where user_id=? and character_id=?''', (user.user_id, self.character_id))
x = self.c.fetchone()
if x and x[0] == 1:
raise ArcError('The character has been uncapped.')
elif self.is_uncapped:
raise ArcError('The character has been uncapped.')
for i in self.uncap_cores:
self.c.execute(
'''select amount from user_item where user_id=? and item_id=? and type="core"''', (user.user_id, i.item_id))
y = self.c.fetchone()
if not y or i.amount > y[0]:
raise ItemNotEnough('The cores are not enough.')
for i in self.uncap_cores:
ItemCore(self.c, i, reverse=True).user_claim_item(user)
self.c.execute('''update user_char set is_uncapped=1, is_uncapped_override=0 where user_id=? and character_id=?''',
(user.user_id, self.character_id))
self.is_uncapped = True
self.is_uncapped_override = False
def upgrade(self, user, exp_addition: float):
# parameter: user - User类或子类的实例
# 升级角色
if Config.CHARACTER_FULL_UNLOCK:
# 全解锁了你升级个鬼啊
raise ArcError('All characters are available.')
if self.level.exp is None:
self.select_character_info(user)
if self.is_uncapped is None:
self.c.execute(
'''select is_uncapped from user_char where user_id=? and character_id=?''', (user.user_id, self.character_id))
x = self.c.fetchone()
if x:
self.is_uncapped = x[0] == 1
self.level.max_level = 30 if self.is_uncapped else 20
self.level.add_exp(exp_addition)
self.c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
(self.level.level, self.level.exp, user.user_id, self.character_id))
def upgrade_by_core(self, user, core):
# parameter: user - User类或子类的实例
# core - ItemCore类或子类的实例
# 以太之滴升级注意这里core.amount应该是负数
if core.item_id != 'core_generic':
raise ArcError('Core type error.')
if core.amount >= 0:
raise InputError(
'The amount of `core_generic` should be negative.')
core.user_claim_item(user)
self.upgrade(user, - core.amount * Constant.CORE_EXP)

View File

@@ -1,17 +1,9 @@
class ArcError(Exception):
api_error_code = -999
error_code = 108
message = None
extra_data = None
def __init__(self, message=None, error_code=None, api_error_code=None, extra_data=None) -> None:
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None:
self.message = message
if error_code:
self.error_code = error_code
if api_error_code:
self.api_error_code = api_error_code
if extra_data:
self.extra_data = extra_data
self.error_code = error_code
self.api_error_code = api_error_code
self.extra_data = extra_data
def __str__(self) -> str:
return repr(self.message)
@@ -19,7 +11,7 @@ class ArcError(Exception):
class InputError(ArcError):
# 输入类型错误
def __init__(self, message=None, error_code=None, api_error_code=-100, extra_data=None) -> None:
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None) -> None:
super().__init__(message, error_code, api_error_code, extra_data)
@@ -46,6 +38,24 @@ class UserBan(ArcError):
super().__init__(message, error_code, api_error_code, extra_data)
class ItemNotEnough(ArcError):
# 物品数量不足
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None:
super().__init__(message, error_code, api_error_code, extra_data)
class ItemUnavailable(ArcError):
# 物品不可用
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None:
super().__init__(message, error_code, api_error_code, extra_data)
class FriendError(ArcError):
# 好友系统出错
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None:
super().__init__(message, error_code, api_error_code, extra_data)
class NoAccess(ArcError):
# 无权限
pass

View File

@@ -1,7 +1,185 @@
from sympy import Nor
from .error import NoData, ItemUnavailable, ItemNotEnough, InputError
class Item:
item_id = None
item_type = None
is_available = None
def __init__(self) -> None:
self.item_id = None
self.__amount = None
self.is_available = None
@property
def amount(self):
return self.__amount
@amount.setter
def amount(self, value: int):
self.__amount = int(value)
def user_claim_item(self, user):
# parameter: user - User类或子类的实例
pass
class NormalItem(Item):
def __init__(self, c) -> None:
super().__init__()
self.c = c
def user_claim_item(self, user):
if not self.is_available:
self.c.execute(
'''select is_available from item where item_id=? and type=?''', (self.item_id, self.item_type))
x = self.c.fetchone()
if x:
if x[0] == 0:
self.is_available = False
raise ItemUnavailable('The item is unavailable.')
else:
self.is_available = True
else:
raise NoData('No item data.')
self.c.execute('''select exists(select * from user_item where user_id=? and item_id=? and type=?)''',
(user.user_id, self.item_id, self.item_type))
if self.c.fetchone() == (0,):
self.c.execute('''insert into user_item values(:a,:b,:c,1)''',
{'a': user.user_id, 'b': self.item_id, 'c': self.item_type})
class PositiveItem(Item):
def __init__(self, c) -> None:
super().__init__()
self.c = c
def user_claim_item(self, user):
self.c.execute('''select amount from user_item where user_id=? and item_id=? and type=?''',
(user.user_id, self.item_id, self.item_type))
x = self.c.fetchone()
if x:
if x[0] + self.amount < 0: # 数量不足
raise ItemNotEnough(
'The user does not have enough `%s`.' % self.item_id)
self.c.execute('''update user_item set amount=? where user_id=? and item_id=? and type=?''',
(x[0]+self.amount, user.user_id, self.item_id, self.item_type))
else:
if self.amount < 0: # 添加数量错误
raise InputError(
'The amount of `%s` is wrong.' % self.item_id)
self.c.execute('''insert into user_item values(?,?,?,?)''',
(user.user_id, self.item_id, self.item_type, self.amount))
class ItemCore(PositiveItem):
item_type = 'core'
def __init__(self, c, core=None, reverse=False) -> None:
super().__init__(c)
self.is_available = True
if core:
self.item_id = core.item_id
self.amount = - core.amount if reverse else core.amount
class ItemCharacter(Item):
item_type = 'character'
def __init__(self, c) -> None:
super().__init__()
self.c = c
self.is_available = True
def set_id(self, character_id):
# 将name: str转为character_id: int存到item_id里
if character_id.isdigit():
self.item_id = int(character_id)
else:
self.c.execute(
'''select character_id from character where name=?''', (character_id,))
x = self.c.fetchone()
if x:
self.item_id = x[0]
else:
raise NoData('No character `%s`.' % character_id)
def user_claim_item(self, user):
self.c.execute(
'''select exists(select * from user_char where user_id=? and character_id=?)''', (user.user_id, self.item_id))
if self.c.fetchone() == (0,):
self.c.execute(
'''insert into user_char values(?,?,1,0,0,0)''', (user.user_id, self.item_id))
class Memory(Item):
item_type = 'memory'
def __init__(self, c) -> None:
super().__init__()
self.c = c
self.is_available = True
def user_claim_item(self, user):
self.c.execute(
'''select ticket from user where user_id=?''', (user.user_id,))
x = self.c.fetchone()
if x is not None:
self.c.execute('''update user set ticket=? where user_id=?''',
(x[0]+self.amount, user.user_id))
else:
raise NoData('The ticket of the user is null.')
class Anni5tix(PositiveItem):
item_type = 'anni5tix'
def __init__(self, c) -> None:
super().__init__(c)
self.is_available = True
class WorldSong(NormalItem):
item_type = 'world_song'
def __init__(self, c) -> None:
super().__init__(c)
class WorldUnlock(NormalItem):
item_type = 'world_unlock'
def __init__(self, c) -> None:
super().__init__(c)
class Single(NormalItem):
item_type = 'single'
def __init__(self, c) -> None:
super().__init__(c)
class Pack(NormalItem):
item_type = 'pack'
def __init__(self, c) -> None:
super().__init__(c)
def get_user_cores(c, user) -> list:
# parameter: user - User类或子类的实例
# 得到用户的cores返回字典列表
r = []
c.execute(
'''select item_id, amount from user_item where user_id = ? and type="core"''', (user.user_id,))
x = c.fetchall()
if x:
for i in x:
if i[1]:
amount = i[1]
else:
amount = 0
r.append({'core_type': i[0], 'amount': amount})
return r

View File

@@ -0,0 +1,73 @@
from .song import Chart
class Score:
def __init__(self) -> None:
self.song = Chart()
self.score = None
self.shiny_perfect_count = None
self.perfect_count = None
self.near_count = None
self.miss_count = None
self.health = None
self.modifier = None
self.time_played = None
self.best_clear_type = None
self.clear_type = None
self.rating = None
def set_score(self, score: int, shiny_perfect_count: int, perfect_count: int, near_count: int, miss_count: int, health: int, modifier: int, time_played: int, clear_type: int):
self.score = score
self.shiny_perfect_count = shiny_perfect_count
self.perfect_count = perfect_count
self.near_count = near_count
self.miss_count = miss_count
self.health = health
self.modifier = modifier
self.time_played = time_played
self.clear_type = clear_type
@property
def is_valid(self) -> bool:
# 分数有效性检查
return True
@staticmethod
def calculate_rating(defnum: int, score: int) -> float:
# 计算rating-1视作Unrank
if not defnum or defnum <= 0:
# 谱面没定数或者定数小于等于0被视作Unrank
return -1
if score >= 10000000:
ptt = defnum + 2
elif score < 9800000:
ptt = defnum + (score-9500000) / 300000
if ptt < 0:
ptt = 0
else:
ptt = defnum + 1 + (score-9800000) / 200000
return ptt
def get_rating_by_calc(self) -> float:
# 通过计算得到本成绩的rating
self.rating = self.calculate_rating(self.song.defnum, self.score)
return self.rating
@property
def to_dict(self) -> dict:
return {
"rating": self.rating,
"modifier": self.modifier,
"time_played": self.time_played,
"health": self.health,
"clear_type": self.clear_type,
"miss_count": self.miss_count,
"near_count": self.near_count,
"perfect_count": self.perfect_count,
"shiny_perfect_count": self.shiny_perfect_count,
"score": self.score,
"difficulty": self.song.difficulty,
"song_id": self.song.song_id
}

View File

@@ -0,0 +1,7 @@
class Chart:
# defnum: chart const * 10
def __init__(self, song_id: str = None, difficulty: int = None, defnum: int = None) -> None:
self.song_id = song_id
self.difficulty = difficulty
self.defnum = defnum

View File

@@ -20,14 +20,17 @@ class Connect():
return self.c
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
if self.conn:
self.conn.rollback()
current_app.logger.error(
traceback.format_exception(exc_type, exc_val, exc_tb))
if self.conn:
self.conn.commit()
self.conn.close()
if exc_type is not None:
current_app.logger.error(
traceback.format_exception(exc_type, exc_val, exc_tb))
return True

View File

@@ -1,6 +1,9 @@
from .error import ArcError, InputError, DataExist, NoAccess, NoData, UserBan
from .error import ArcError, InputError, DataExist, NoAccess, NoData, UserBan, FriendError
from .constant import Constant
from .character import UserCharacter
from .score import Score
from .world import Map
from .item import get_user_cores
from setting import Config
import hashlib
import base64
@@ -8,23 +11,42 @@ import time
from os import urandom
class User:
name = None
email = None
password = None
user_id = None
user_code = None
def code_get_id(c, user_code: str) -> int:
# 用user_code获取user_id
c.execute('''select user_id from user where user_code = :a''',
{'a': user_code})
x = c.fetchone()
if x is not None:
user_id = int(x[0])
else:
raise NoData('No user.', 401, -3)
return user_id
class User:
def __init__(self) -> None:
pass
self.name = None
self.email = None
self.password = None
self.user_id = None
self.user_code = None
self.join_date = None
self.rating_ptt = None
self.ticket = None
self.world_rank_score = None
self.ban_flag = None
class UserRegister(User):
hash_pwd = None
def __init__(self, c) -> None:
super().__init__()
self.c = c
self.hash_pwd = None
def set_name(self, name: str):
if 3 <= len(name) <= 16:
@@ -115,15 +137,14 @@ class UserRegister(User):
class UserLogin(User):
# 密码和token的加密方式为 SHA-256
device_id = None
ip = None
hash_pwd = None
token = None
now = 0
def __init__(self, c) -> None:
super().__init__()
self.c = c
self.device_id = None
self.ip = None
self.hash_pwd = None
self.token = None
self.now = 0
def set_name(self, name: str):
self.name = name
@@ -230,11 +251,10 @@ class UserLogin(User):
class UserAuth(User):
token = None
def __init__(self, c) -> None:
super().__init__()
self.c = c
self.token = None
def token_get_id(self):
# 用token获取id没有考虑不同用户token相同情况说不定会有bug
@@ -248,33 +268,155 @@ class UserAuth(User):
return self.user_id
def code_get_id(self):
# 用user_code获取id
self.c.execute('''select user_id from user where user_code = :a''',
{'a': self.user_code})
x = self.c.fetchone()
if x is not None:
self.user_id = x[0]
else:
raise NoData('No user.', 401, -3)
return self.user_id
class UserOnline(User):
character = None
def __init__(self, c, user_id=None) -> None:
super().__init__()
self.c = c
self.user_id = user_id
self.character = None
self.is_skill_sealed = False
self.is_hide_rating = False
self.recent_score = Score()
self.favorite_character = -1
self.max_stamina_notification_enabled = False
self.prog_boost = 0
self.__cores = None
self.__friends = None
@property
def cores(self) -> list:
if self.__cores is None:
self.__cores = get_user_cores(self.c, self)
return self.__cores
@property
def friends(self) -> list:
# 得到用户的朋友列表
if self.__friends is None:
self.c.execute('''select user_id_other from friend where user_id_me = :user_id''', {
'user_id': self.user_id})
x = self.c.fetchall()
s = []
if x != [] and x[0][0] is not None:
for i in x:
self.c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
{'x': i[0], 'y': self.user_id})
is_mutual = True if self.c.fetchone() == (1,) else False
you = UserOnline(self.c, i[0])
you.select_user()
character = you.character if you.favorite_character is None else you.favorite_character
character.select_character_uncap_condition(you)
rating = you.rating_ptt if not you.is_hide_rating else -1
s.append({
"is_mutual": is_mutual,
"is_char_uncapped_override": character.is_uncapped_override,
"is_char_uncapped": character.is_uncapped,
"is_skill_sealed": you.is_skill_sealed,
"rating": rating,
"join_date": you.join_date,
"character": character.character_id,
"recent_score": you.recent_score_list,
"name": you.name,
"user_id": you.user_id
})
s.sort(key=lambda item: item["recent_score"][0]["time_played"] if len(
item["recent_score"]) > 0 else 0, reverse=True)
self.__friends = s
return self.__friends
@property
def recent_score_list(self) -> list:
# 用户最近一次成绩,是列表
if self.name is None:
self.select_user()
if self.recent_score.song.song_id is None:
return []
self.c.execute('''select best_clear_type from best_score where user_id=:u and song_id=:s and difficulty=:d''', {
'u': self.user_id, 's': self.recent_score.song.song_id, 'd': self.recent_score.song.difficulty})
y = self.c.fetchone()
best_clear_type = y[0] if y is not None else self.recent_score.clear_type
r = self.recent_score.to_dict
r["best_clear_type"] = best_clear_type
return [r]
def change_character(self, character_id: int, skill_sealed: bool = False):
# 用户角色改变,包括技能封印的改变
self.character = UserCharacter(self.c, character_id)
self.character.select_character_uncap_condition(self)
self.is_skill_sealed = skill_sealed
self.c.execute('''update user set is_skill_sealed = :a, character_id = :b, is_char_uncapped = :c, is_char_uncapped_override = :d where user_id = :e''', {
'a': 1 if skill_sealed else 0, 'b': self.character.character_id, 'c': self.character.is_uncapped, 'd': self.character.is_uncapped_override, 'e': self.user_id})
'a': 1 if self.is_skill_sealed else 0, 'b': self.character.character_id, 'c': self.character.is_uncapped, 'd': self.character.is_uncapped_override, 'e': self.user_id})
def select_user(self):
# 查user表所有信息
self.c.execute(
'''select * from user where user_id = :x''', {'x': self.user_id})
x = self.c.fetchone()
if not x:
raise NoData('No user.', 108, -3)
self.name = x[1]
self.join_date = int(x[3])
self.user_code = x[4]
self.rating_ptt = x[5]
self.character = UserCharacter(self.c, x[6])
self.is_skill_sealed = x[7] == 1
self.character.is_uncapped = x[8] == 1
self.character.is_uncapped_override = x[9] == 1
self.is_hide_rating = x[10] == 1
self.recent_score.song.song_id = x[11]
self.recent_score.song.difficulty = x[12]
self.recent_score.set_score(
x[13], x[14], x[15], x[16], x[17], x[18], x[19], x[20], x[21])
self.recent_score.rating = x[22]
self.favorite_character = None if x[23] == - \
1 else UserCharacter(self.c, x[23])
self.max_stamina_notification_enabled = x[24] == 1
self.current_map = Map(x[25])
self.ticket = x[26]
self.prog_boost = x[27]
self.email = x[28]
self.world_rank_score = x[29]
self.ban_flag = x[30]
self.next_fragstam_ts = x[31]
self.max_stamina_ts = x[32]
self.stamina = x[33]
def add_friend(self, friend_id: int):
# 加好友
if self.user_id == friend_id:
raise FriendError('Add yourself as a friend.', 604)
self.c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
{'x': self.user_id, 'y': friend_id})
if self.c.fetchone() == (0,):
self.c.execute('''insert into friend values(:a, :b)''', {
'a': self.user_id, 'b': friend_id})
else:
raise FriendError('The user has been your friend.', 602)
def delete_friend(self, friend_id: int):
# 删好友
self.c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
{'x': self.user_id, 'y': friend_id})
if self.c.fetchone() == (1,):
self.c.execute('''delete from friend where user_id_me = :x and user_id_other = :y''',
{'x': self.user_id, 'y': friend_id})
else:
raise FriendError('No user or the user is not your friend.', 401)

View File

@@ -0,0 +1,3 @@
class Map:
def __init__(self, map_id: str = None) -> None:
self.map_id = map_id

Binary file not shown.

View File

@@ -4,7 +4,7 @@ import json
# 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用
ARCAEA_SERVER_VERSION = 'v2.8.5'
ARCAEA_SERVER_VERSION = 'v2.8.6'
def main(path='./'):
@@ -387,7 +387,7 @@ def main(path='./'):
c.execute('''insert into item values(?,"core",1,'')''', (i,))
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote"]
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia"]
for i in world_songs:
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))

View File

@@ -1180,5 +1180,23 @@
],
"orig_price": 100,
"price": 100
},
{
"name": "mu",
"items": [
{
"type": "single",
"id": "mu",
"is_available": true
},
{
"type": "core",
"amount": 1,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 100,
"price": 100
}
]

View File

@@ -226,85 +226,6 @@ def user_me(user_id):
return error_return(108)
# 角色觉醒
@app.route(add_url_prefix('/<path:path>/uncap', True), methods=['POST'])
@server.auth.auth_required(request)
def character_first_uncap(user_id, path):
character_id = int(path[path.find('character')+10:])
r = server.character.char_uncap(user_id, character_id)
if r is not None:
return jsonify({
"success": True,
"value": r
})
else:
return error_return(108)
# 角色使用以太之滴
@app.route(add_url_prefix('/<path:path>/exp', True), methods=['POST'])
@server.auth.auth_required(request)
def character_exp(user_id, path):
character_id = int(path[path.find('character')+10:])
amount = int(request.form['amount'])
r = server.character.char_use_core(user_id, character_id, amount)
if r is not None:
return jsonify({
"success": True,
"value": r
})
else:
return error_return(108)
@app.route(add_url_prefix('/friend/me/add'), methods=['POST']) # 加好友
@server.auth.auth_required(request)
def add_friend(user_id):
friend_code = request.form['friend_code']
friend_id = server.auth.code_get_id(friend_code)
if friend_id is not None:
r = server.setme.arc_add_friend(user_id, friend_id)
if r is not None and r != 602 and r != 604:
return jsonify({
"success": True,
"value": {
"user_id": user_id,
"updatedAt": "2020-09-07T07:32:12.740Z",
"createdAt": "2020-09-06T10:05:18.471Z",
"friends": r
}
})
else:
if r is not None:
return error_return(r)
else:
return error_return(108)
else:
return error_return(401)
@app.route(add_url_prefix('/friend/me/delete'), methods=['POST']) # 删好友
@server.auth.auth_required(request)
def delete_friend(user_id):
friend_id = int(request.form['friend_id'])
if friend_id is not None:
r = server.setme.arc_delete_friend(user_id, friend_id)
if r is not None:
return jsonify({
"success": True,
"value": {
"user_id": user_id,
"updatedAt": "2020-09-07T07:32:12.740Z",
"createdAt": "2020-09-06T10:05:18.471Z",
"friends": r
}
})
else:
return error_return(108)
else:
return error_return(401)
# 好友排名默认最多50
@app.route(add_url_prefix('/score/song/friend'), methods=['GET'])
@server.auth.auth_required(request)
@@ -701,10 +622,10 @@ def multiplayer_update(user_id):
return error_return(error_code), 400
@app.route(add_url_prefix('/user/me/request_delete'), methods=['POST']) # 删除账号
@server.auth.auth_required(request)
def user_delete(user_id):
return error_return(151), 404
# @app.route(add_url_prefix('/user/me/request_delete'), methods=['POST']) # 删除账号
# @server.auth.auth_required(request)
# def user_delete(user_id):
# return error_return(151), 404
# 三个设置,写在最后降低优先级

View File

@@ -2,7 +2,9 @@ from flask import Blueprint
from setting import Config
from . import user
from . import auth
from . import friend
bp = Blueprint('server', __name__, url_prefix=Config.GAME_API_PREFIX)
bp.register_blueprint(user.bp)
bp.register_blueprint(auth.bp)
bp.register_blueprint(auth.bp)
bp.register_blueprint(friend.bp)

View File

@@ -1149,4 +1149,10 @@ class Constant:
}, {
"unlock_key": "cyberneciacatharsis|2|0",
"complete": 1
}, {
"unlock_key": "sanskia|1|0",
"complete": 1
}, {
"unlock_key": "sanskia|2|0",
"complete": 1
}]

View File

@@ -0,0 +1,49 @@
from flask import Blueprint, request
from core.sql import Connect
from core.error import ArcError
from core.user import UserOnline, code_get_id
from .func import error_return, success_return
from .auth import auth_required
bp = Blueprint('friend', __name__, url_prefix='/friend')
@bp.route('/me/add', methods=['POST']) # 加好友
@auth_required(request)
def add_friend(user_id):
with Connect() as c:
try:
friend_code = request.form['friend_code']
friend_id = code_get_id(c, friend_code)
user = UserOnline(c, user_id)
user.add_friend(friend_id)
return success_return({
"user_id": user.user_id,
"updatedAt": "2020-09-07T07:32:12.740Z",
"createdAt": "2020-09-06T10:05:18.471Z",
"friends": user.friends
})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/delete', methods=['POST']) # 删好友
@auth_required(request)
def delete_friend(user_id):
with Connect() as c:
try:
friend_id = int(request.form['friend_id'])
user = UserOnline(c, user_id)
user.delete_friend(friend_id)
return success_return({
"user_id": user.user_id,
"updatedAt": "2020-09-07T07:32:12.740Z",
"createdAt": "2020-09-06T10:05:18.471Z",
"friends": user.friends
})
except ArcError as e:
return error_return(e)
return error_return()

View File

@@ -3,6 +3,7 @@ from core.error import ArcError, NoAccess
from core.sql import Connect
from core.user import UserRegister, UserLogin, User, UserOnline
from core.character import UserCharacter
from core.item import ItemCore
from .func import error_return, success_return
from .auth import auth_required
from setting import Config
@@ -63,11 +64,49 @@ def toggle_uncap(user_id, character_id):
try:
user = User()
user.user_id = user_id
character = UserCharacter(c)
character.character_id = character_id
character = UserCharacter(c, character_id)
character.change_uncap_override(user)
character.select_character_info(user)
return success_return({'user_id': user.user_id, 'character': [character.to_dict]})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/character/<int:character_id>/uncap', methods=['POST']) # 角色觉醒
@auth_required(request)
def character_first_uncap(user_id, character_id):
with Connect() as c:
try:
user = UserOnline(c, user_id)
character = UserCharacter(c, character_id)
character.select_character_info(user)
character.character_uncap(user)
return success_return({'user_id': user.user_id, 'character': [character.to_dict], 'cores': user.cores})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/character/<int:character_id>/exp', methods=['POST']) # 角色使用以太之滴
@auth_required(request)
def character_exp(user_id, character_id):
with Connect() as c:
try:
user = UserOnline(c, user_id)
character = UserCharacter(c, character_id)
character.select_character_info(user)
core = ItemCore(c)
core.amount = - int(request.form['amount'])
core.item_id = 'core_generic'
character.upgrade_by_core(user, core)
return success_return({'user_id': user.user_id, 'character': [character.to_dict], 'cores': user.cores})
except ArcError as e:
return error_return(e)
return error_return()
@bp.route('/me/request_delete', methods=['POST']) # 删除账号
@auth_required(request)
def user_delete(user_id):
return error_return(ArcError('Cannot delete the account.', 151)), 404

View File

@@ -30,7 +30,7 @@ class Config():
Allowed game versions
If it is blank, all are allowed.
'''
ALLOW_APPVERSION = ['3.12.6', '3.12.6c', '3.12.8', '3.12.8c']
ALLOW_APPVERSION = ['3.12.6', '3.12.6c', '3.12.10', '3.12.10c']
'''
--------------------
'''

View File

@@ -140,6 +140,9 @@ class Room:
def is_finish(self):
# 是否全部进入结算
if self.state == 8:
return False
for i in self.players:
if i.player_id != 0 and (i.finish_flag == 0 or i.online == 0):
return False