mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-09 17:27:27 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96fbd26342 | ||
|
|
aa77a4c903 | ||
|
|
6075bda5d8 | ||
|
|
409c2c032b | ||
|
|
e8c15f5069 | ||
|
|
35c34f25d5 | ||
|
|
4b8c5a520e | ||
|
|
1235733ddf | ||
|
|
cb6425a0d1 | ||
|
|
3a1c731e24 |
30
README.md
30
README.md
@@ -66,21 +66,23 @@ It is just so interesting. What it can do is under exploration.
|
|||||||
> Tips: When updating, please keep the original database in case of data loss.
|
> Tips: When updating, please keep the original database in case of data loss.
|
||||||
|
|
||||||
|
|
||||||
### Version 2.8.1
|
### Version 2.8.5
|
||||||
- 适用于Arcaea 3.12.0版本 For Arcaea 3.12.0
|
- 适用于Arcaea 3.12.8版本 For Arcaea 3.12.8
|
||||||
- 更新了歌曲数据库 Update the song database.
|
- 更新了歌曲数据库 Update the song database.
|
||||||
- 新搭档**维塔**已解锁 Unlock the character **Vita**.
|
- 修复一个导致无法升级角色的拼写错误 Fix a typing error, which makes giving characters Exp wrong.
|
||||||
- 新增对**维塔**的技能支持 Add support for the skill of **Vita**.
|
- 尝试强制指定多进程启动方式为spawn,这可能对UNIX系统中UDP服务器的启动有所帮助 Try to forcibly specify the multiprocess startup mode as spawn, which may be helpful for the startup of UDP server in UNIX system.
|
||||||
- 修正世界模式进度计算方式 Revise the algorithm of world mode progress.
|
- 添加了注册API对外接口 Add an external API interface of user register.
|
||||||
- 修复世界模式下 **对立(风暴)** 数值计算错误的问题 Fix the wrong value of **Tairitsu(Tempest)** in the World Mode.
|
- 重构一些代码,顺手修复了登陆时因多设备登录封号的用户没有正确显示错误提示的问题 Refactoring some codes and in passing fix the problem that users which has been banned because of multiple devices do not show error messages correctly when logging in.
|
||||||
|
|
||||||
|
> 注意:
|
||||||
- 以下是累积更新 The following are cumulative updates:
|
> - 现在Flask最低版本要求提高到2.0
|
||||||
- #35 集成式接口优化(By Young-Lord) Optimize `aggregate` interface. (By Young-Lord)
|
> - 服务端可能不再完全支持低版本客户端
|
||||||
- 新增填写Link Play服务器地址的选项,解决地址无法正确自动获取的问题 Add the option of filling in Link Play server address, which can solve the problem that the address cannot be obtained automatically.
|
> - 对3.12.6c版本,愚人节开关打开时点击`网络`按钮会闪退,原因不明
|
||||||
- 修复高版本iOS客户端无法登陆的问题 Fix a bug that the high version of iOS client cannot log in.
|
>
|
||||||
- 修复有关下载的安全性问题 Fix a safety problem about downloading.
|
> Note:
|
||||||
- 修复有不同音频的Beyond谱面无法下载的问题 Fix a bug about unable to download a beyond chart which has another audio file.
|
> - Now the version of Flask which is required should be up to 2.0.
|
||||||
|
> - The server may no longer fully support lower version clients.
|
||||||
|
> - For version 3.12.6c, when the switch of April Fool's Day is on, clicking the `Network` button will make the client break down. The reason is not clear now.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -88,7 +90,7 @@ It is just so interesting. What it can do is under exploration.
|
|||||||
## 运行环境与依赖 Running environment and requirements
|
## 运行环境与依赖 Running environment and requirements
|
||||||
- Windows/Linux/Mac OS/Android...
|
- Windows/Linux/Mac OS/Android...
|
||||||
- Python 3
|
- Python 3
|
||||||
- Flask module, Cryptography module
|
- Flask module >= 2.0, Cryptography module
|
||||||
- Charles, IDA, proxy app... (optional)
|
- Charles, IDA, proxy app... (optional)
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
@@ -1,19 +1,34 @@
|
|||||||
|
from flask import jsonify
|
||||||
|
|
||||||
|
|
||||||
def code_get_msg(code):
|
def code_get_msg(code):
|
||||||
# api接口code获取msg,返回字符串
|
# api接口code获取msg,返回字符串
|
||||||
msg = {
|
msg = {
|
||||||
'0': '',
|
0: '',
|
||||||
'-1': 'See status code',
|
-1: 'See status code',
|
||||||
'-2': 'No data',
|
-2: 'No data',
|
||||||
'-3': 'No data or user',
|
-3: 'No data or user',
|
||||||
'-4': 'No user_id',
|
-4: 'No user_id',
|
||||||
'-100': 'Wrong post data',
|
-100: 'Wrong post data',
|
||||||
'-101': 'Wrong data type',
|
-101: 'Wrong data type',
|
||||||
'-102': 'Wrong query parameter',
|
-102: 'Wrong query parameter',
|
||||||
'-103': 'Wrong sort parameter',
|
-103: 'Wrong sort parameter',
|
||||||
'-104': 'Wrong sort order parameter',
|
-104: 'Wrong sort order parameter',
|
||||||
'-201': 'Wrong username or password',
|
-201: 'Wrong username or password',
|
||||||
'-202': 'User is banned',
|
-202: 'User is banned',
|
||||||
'-999': 'Unknown error'
|
-203: 'Username exists',
|
||||||
|
-204: 'Email address exists',
|
||||||
|
-999: 'Unknown error'
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg[str(code)]
|
return msg[code]
|
||||||
|
|
||||||
|
|
||||||
|
def return_encode(code: int = 0, data: dict = {}, status: int = 200, msg: str = ''):
|
||||||
|
# 构造返回,返回jsonify处理过后的response_class
|
||||||
|
if msg == '':
|
||||||
|
msg = code_get_msg(code)
|
||||||
|
if code < 0:
|
||||||
|
return jsonify({'status': status, 'code': code, 'data': {}, 'msg': msg})
|
||||||
|
else:
|
||||||
|
return jsonify({'status': status, 'code': code, 'data': data, 'msg': msg})
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ from flask import (
|
|||||||
)
|
)
|
||||||
import functools
|
import functools
|
||||||
import api.api_auth
|
import api.api_auth
|
||||||
import api.users
|
from . import users
|
||||||
import api.songs
|
import api.songs
|
||||||
from api.api_code import code_get_msg
|
from .api_code import code_get_msg, return_encode
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||||
|
bp.register_blueprint(users.bp)
|
||||||
|
|
||||||
|
|
||||||
class Query():
|
class Query():
|
||||||
@@ -62,16 +63,6 @@ def get_query_parameter(request, query_able=[], sort_able=[]):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def return_encode(code: int = 0, data: dict = {}, status: int = 200, msg: str = ''):
|
|
||||||
# 构造返回,返回jsonify处理过后的response_class
|
|
||||||
if msg == '':
|
|
||||||
msg = code_get_msg(code)
|
|
||||||
if code < 0:
|
|
||||||
return jsonify({'status': status, 'code': code, 'data': {}, 'msg': msg})
|
|
||||||
else:
|
|
||||||
return jsonify({'status': status, 'code': code, 'data': data, 'msg': msg})
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/')
|
@bp.route('/')
|
||||||
def ping():
|
def ping():
|
||||||
return return_encode()
|
return return_encode()
|
||||||
@@ -111,7 +102,7 @@ def token_delete(user):
|
|||||||
def users_get(query, user):
|
def users_get(query, user):
|
||||||
# 查询全用户信息
|
# 查询全用户信息
|
||||||
|
|
||||||
data = api.users.get_users(query)
|
data = users.get_users(query)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return return_encode(-2)
|
return return_encode(-2)
|
||||||
@@ -133,7 +124,7 @@ def users_user_get(user, user_id):
|
|||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
return return_encode(-1, {}, 403, 'No permission')
|
||||||
|
|
||||||
data = api.users.get_user_info(user_id)
|
data = users.get_user_info(user_id)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return return_encode(-3)
|
return return_encode(-3)
|
||||||
@@ -152,7 +143,7 @@ def users_user_b30_get(user, user_id):
|
|||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
return return_encode(-1, {}, 403, 'No permission')
|
||||||
|
|
||||||
data = api.users.get_user_b30(user_id)
|
data = users.get_user_b30(user_id)
|
||||||
|
|
||||||
if data['data'] == []:
|
if data['data'] == []:
|
||||||
return return_encode(-3)
|
return return_encode(-3)
|
||||||
@@ -173,7 +164,7 @@ def users_user_best_get(query, user, user_id):
|
|||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
return return_encode(-1, {}, 403, 'No permission')
|
||||||
|
|
||||||
data = api.users.get_user_best(user_id, query)
|
data = users.get_user_best(user_id, query)
|
||||||
|
|
||||||
if data['data'] == []:
|
if data['data'] == []:
|
||||||
return return_encode(-3)
|
return return_encode(-3)
|
||||||
@@ -192,7 +183,7 @@ def users_user_r30_get(user, user_id):
|
|||||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||||
return return_encode(-1, {}, 403, 'No permission')
|
return return_encode(-1, {}, 403, 'No permission')
|
||||||
|
|
||||||
data = api.users.get_user_r30(user_id)
|
data = users.get_user_r30(user_id)
|
||||||
|
|
||||||
if data['data'] == []:
|
if data['data'] == []:
|
||||||
return return_encode(-3)
|
return return_encode(-3)
|
||||||
|
|||||||
@@ -1,9 +1,48 @@
|
|||||||
|
from flask import (
|
||||||
|
Blueprint, request
|
||||||
|
)
|
||||||
|
|
||||||
|
from .api_code import code_get_msg, return_encode
|
||||||
|
from .api_auth import role_required
|
||||||
|
from core.user import UserRegister
|
||||||
|
from core.error import ArcError, PostError
|
||||||
from server.sql import Connect
|
from server.sql import Connect
|
||||||
from server.sql import Sql
|
from server.sql import Sql
|
||||||
import time
|
import time
|
||||||
import web.webscore
|
import web.webscore
|
||||||
import server.info
|
import server.info
|
||||||
|
|
||||||
|
bp = Blueprint('users', __name__, url_prefix='/users')
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('', methods=['POST'])
|
||||||
|
@role_required(request, ['change'])
|
||||||
|
def users_post(user):
|
||||||
|
# 注册用户
|
||||||
|
with Connect() as c:
|
||||||
|
new_user = UserRegister(c)
|
||||||
|
try:
|
||||||
|
if 'name' in request.json:
|
||||||
|
new_user.set_name(request.json['name'])
|
||||||
|
else:
|
||||||
|
raise PostError('No name provided.')
|
||||||
|
|
||||||
|
if 'password' in request.json:
|
||||||
|
new_user.set_password(request.json['password'])
|
||||||
|
else:
|
||||||
|
raise PostError('No password provided.')
|
||||||
|
|
||||||
|
if 'email' in request.json:
|
||||||
|
new_user.set_email(request.json['email'])
|
||||||
|
else:
|
||||||
|
raise PostError('No email provided.')
|
||||||
|
|
||||||
|
new_user.register()
|
||||||
|
except ArcError as e:
|
||||||
|
return return_encode(e.api_error_code)
|
||||||
|
|
||||||
|
return return_encode(0, {'user_id': new_user.user_id, 'user_code': new_user.user_code})
|
||||||
|
|
||||||
|
|
||||||
def get_users(query=None):
|
def get_users(query=None):
|
||||||
# 获取全用户信息,返回字典列表
|
# 获取全用户信息,返回字典列表
|
||||||
|
|||||||
0
latest version/core/__init__.py
Normal file
0
latest version/core/__init__.py
Normal file
213
latest version/core/character.py
Normal file
213
latest version/core/character.py
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
from setting import Config
|
||||||
|
from .error import ArcError, NoData
|
||||||
|
from .constant import Constant
|
||||||
|
from .item import Item
|
||||||
|
|
||||||
|
|
||||||
|
class Level:
|
||||||
|
max_level = None
|
||||||
|
mid_level = 20
|
||||||
|
min_level = 1
|
||||||
|
level = None
|
||||||
|
exp = None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def level_exp(self):
|
||||||
|
return Constant.LEVEL_STEPS[self.level]
|
||||||
|
|
||||||
|
|
||||||
|
class Skill:
|
||||||
|
skill_id = None
|
||||||
|
skill_id_uncap = None
|
||||||
|
skill_unlock_level = None
|
||||||
|
skill_requires_uncap = None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _calc_char_value_20(level, stata, statb, lva=1, lvb=20):
|
||||||
|
# 计算1~20级搭档数值的核心函数,返回浮点数,来自https://redive.estertion.win/arcaea/calc/
|
||||||
|
n = [0, 0, 0.0005831753900000081, 0.004665403120000065, 0.015745735529959858, 0.03732322495992008, 0.07289692374980007, 0.12596588423968, 0.2000291587694801, 0.29858579967923987, 0.42513485930893946,
|
||||||
|
0.5748651406910605, 0.7014142003207574, 0.7999708412305152, 0.8740341157603029, 0.9271030762501818, 0.962676775040091, 0.9842542644700301, 0.9953345968799998, 0.9994168246100001, 1]
|
||||||
|
e = n[lva] - n[lvb]
|
||||||
|
a = stata - statb
|
||||||
|
r = a / e
|
||||||
|
d = stata - n[lva] * r
|
||||||
|
|
||||||
|
return d + r * n[level]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _calc_char_value_30(level, stata, statb, lva=20, lvb=30):
|
||||||
|
# 计算21~30级搭档数值,返回浮点数
|
||||||
|
return (level - lva) * (statb - stata) / (lvb - lva) + stata
|
||||||
|
|
||||||
|
def set_parameter(self, start: float = 0, mid: float = 0, end: float = 0):
|
||||||
|
self.start = start
|
||||||
|
self.mid = mid
|
||||||
|
self.end = end
|
||||||
|
|
||||||
|
def get_value(self, level: Level):
|
||||||
|
if level.min_level <= level.level <= level.mid_level:
|
||||||
|
return self._calc_char_value_20(level.level, self.start, self.mid)
|
||||||
|
elif level.mid_level < level.level <= level.max_level:
|
||||||
|
return self._calc_char_value_30(level.level, self.mid, self.end)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uncap_cores_to_dict(self):
|
||||||
|
return [x.to_dict() for x in self.uncap_cores]
|
||||||
|
|
||||||
|
|
||||||
|
class UserCharacter(Character):
|
||||||
|
|
||||||
|
def __init__(self, c, character_id=None) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.c = c
|
||||||
|
self.character_id = character_id
|
||||||
|
|
||||||
|
def select_character_core(self):
|
||||||
|
# 获取此角色所需核心
|
||||||
|
self.c.execute(
|
||||||
|
'''select item_id, amount from char_item where character_id = ? and type="core"''', (self.character_id,))
|
||||||
|
x = self.c.fetchall()
|
||||||
|
if x:
|
||||||
|
self.uncap_cores = []
|
||||||
|
for i in x:
|
||||||
|
self.uncap_cores.append(Core(i[0], i[1]))
|
||||||
|
|
||||||
|
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})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if not x:
|
||||||
|
raise NoData('The character of the user does not exist.')
|
||||||
|
|
||||||
|
self.is_uncapped = x[0] == 1
|
||||||
|
self.is_uncapped_override = x[1] == 1
|
||||||
|
|
||||||
|
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))
|
||||||
|
y = self.c.fetchone()
|
||||||
|
if y is None:
|
||||||
|
raise NoData('The character of the user does not exist.')
|
||||||
|
|
||||||
|
self.name = y[7]
|
||||||
|
self.char_type = y[22]
|
||||||
|
self.is_uncapped = y[4] == 1
|
||||||
|
self.is_uncapped_override = y[5] == 1
|
||||||
|
self.level.level = y[2]
|
||||||
|
self.level.exp = y[3]
|
||||||
|
self.level.max_level = y[8]
|
||||||
|
self.frag.set_parameter(y[9], y[12], y[15])
|
||||||
|
self.prog.set_parameter(y[10], y[13], y[16])
|
||||||
|
self.overdrive.set_parameter(y[11], y[14], y[17])
|
||||||
|
self.skill.skill_id = y[18]
|
||||||
|
self.skill.skill_id_uncap = y[21]
|
||||||
|
self.skill.skill_unlock_level = y[19]
|
||||||
|
self.skill.skill_requires_uncap = y[20] == 1
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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})
|
||||||
|
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x is None or x[0] == 0:
|
||||||
|
raise ArcError('Unknown Error')
|
||||||
|
|
||||||
|
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})
|
||||||
|
|
||||||
|
def character_uncap(self):
|
||||||
|
# 觉醒角色
|
||||||
|
pass
|
||||||
16
latest version/core/constant.py
Normal file
16
latest version/core/constant.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
class Constant:
|
||||||
|
|
||||||
|
BAN_TIME = [1, 3, 7, 15, 31]
|
||||||
|
|
||||||
|
MAX_STAMINA = 12
|
||||||
|
|
||||||
|
STAMINA_RECOVER_TICK = 1800000
|
||||||
|
|
||||||
|
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,
|
||||||
|
17: 6100, 18: 7200, 19: 8500, 20: 10000, 21: 11500, 22: 13000, 23: 14500, 24: 16000, 25: 17500, 26: 19000, 27: 20500, 28: 22000, 29: 23500, 30: 25000}
|
||||||
|
|
||||||
|
ETO_UNCAP_BONUS_PROGRESS = 7
|
||||||
|
LUNA_UNCAP_BONUS_PROGRESS = 7
|
||||||
|
AYU_UNCAP_BONUS_PROGRESS = 5
|
||||||
51
latest version/core/error.py
Normal file
51
latest version/core/error.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
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:
|
||||||
|
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
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return repr(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class InputError(ArcError):
|
||||||
|
# 输入类型错误
|
||||||
|
def __init__(self, message=None, error_code=None, api_error_code=-100, extra_data=None) -> None:
|
||||||
|
super().__init__(message, error_code, api_error_code, extra_data)
|
||||||
|
|
||||||
|
|
||||||
|
class DataExist(ArcError):
|
||||||
|
# 数据存在
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoData(ArcError):
|
||||||
|
# 数据不存在
|
||||||
|
def __init__(self, message=None, error_code=None, api_error_code=-2, extra_data=None) -> None:
|
||||||
|
super().__init__(message, error_code, api_error_code, extra_data)
|
||||||
|
|
||||||
|
|
||||||
|
class PostError(ArcError):
|
||||||
|
# 缺少输入
|
||||||
|
def __init__(self, message=None, error_code=None, api_error_code=-100, extra_data=None) -> None:
|
||||||
|
super().__init__(message, error_code, api_error_code, extra_data)
|
||||||
|
|
||||||
|
|
||||||
|
class UserBan(ArcError):
|
||||||
|
# 用户封禁
|
||||||
|
def __init__(self, message=None, error_code=121, api_error_code=None, extra_data=None) -> None:
|
||||||
|
super().__init__(message, error_code, api_error_code, extra_data)
|
||||||
|
|
||||||
|
|
||||||
|
class NoAccess(ArcError):
|
||||||
|
# 无权限
|
||||||
|
pass
|
||||||
7
latest version/core/item.py
Normal file
7
latest version/core/item.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class Item:
|
||||||
|
item_id = None
|
||||||
|
item_type = None
|
||||||
|
is_available = None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
83
latest version/core/sql.py
Normal file
83
latest version/core/sql.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import sqlite3
|
||||||
|
from flask import current_app
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
|
class Connect():
|
||||||
|
# 数据库连接类,上下文管理
|
||||||
|
|
||||||
|
def __init__(self, file_path='./database/arcaea_database.db'):
|
||||||
|
"""
|
||||||
|
数据库连接,默认连接arcaea_database.db
|
||||||
|
接受:文件路径
|
||||||
|
返回:sqlite3连接操作对象
|
||||||
|
"""
|
||||||
|
self.file_path = file_path
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.conn = sqlite3.connect(self.file_path)
|
||||||
|
self.c = self.conn.cursor()
|
||||||
|
return self.c
|
||||||
|
|
||||||
|
def __exit__(self, 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
|
||||||
|
|
||||||
|
|
||||||
|
class Sql():
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def select(c, table_name, target_column=[], query=None):
|
||||||
|
# 执行查询单句sql语句,返回fetchall数据
|
||||||
|
# 使用准确查询,且在单表内
|
||||||
|
|
||||||
|
sql = 'select '
|
||||||
|
sql_dict = {}
|
||||||
|
if len(target_column) >= 2:
|
||||||
|
sql += target_column[0]
|
||||||
|
for i in range(1, len(target_column)):
|
||||||
|
sql += ',' + target_column[i]
|
||||||
|
sql += ' from ' + table_name
|
||||||
|
elif len(target_column) == 1:
|
||||||
|
sql += target_column[0] + ' from ' + table_name
|
||||||
|
else:
|
||||||
|
sql += '* from ' + table_name
|
||||||
|
|
||||||
|
where_field = []
|
||||||
|
where_value = []
|
||||||
|
if query:
|
||||||
|
for i in query.query:
|
||||||
|
where_field.append(i)
|
||||||
|
where_value.append(query.query[i])
|
||||||
|
|
||||||
|
if where_field and where_value:
|
||||||
|
sql += ' where '
|
||||||
|
sql += where_field[0] + '=:' + where_field[0]
|
||||||
|
sql_dict[where_field[0]] = where_value[0]
|
||||||
|
if len(where_field) >= 2:
|
||||||
|
for i in range(1, len(where_field)):
|
||||||
|
sql_dict[where_field[i]] = where_value[i]
|
||||||
|
sql += ' and ' + where_field[i] + '=:' + where_field[i]
|
||||||
|
|
||||||
|
if query and query.sort:
|
||||||
|
sql += ' order by ' + \
|
||||||
|
query.sort[0]['column'] + ' ' + query.sort[0]['order']
|
||||||
|
for i in range(1, len(query.sort)):
|
||||||
|
sql += ', ' + query.sort[i]['column'] + \
|
||||||
|
' ' + query.sort[i]['order']
|
||||||
|
|
||||||
|
if query and query.limit >= 0:
|
||||||
|
sql += ' limit :limit offset :offset'
|
||||||
|
sql_dict['limit'] = query.limit
|
||||||
|
sql_dict['offset'] = query.offset
|
||||||
|
|
||||||
|
c.execute(sql, sql_dict)
|
||||||
|
|
||||||
|
return c.fetchall()
|
||||||
280
latest version/core/user.py
Normal file
280
latest version/core/user.py
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
from .error import ArcError, InputError, DataExist, NoAccess, NoData, UserBan
|
||||||
|
from .constant import Constant
|
||||||
|
from .character import UserCharacter
|
||||||
|
from setting import Config
|
||||||
|
import hashlib
|
||||||
|
import base64
|
||||||
|
import time
|
||||||
|
from os import urandom
|
||||||
|
|
||||||
|
|
||||||
|
class User:
|
||||||
|
name = None
|
||||||
|
email = None
|
||||||
|
password = None
|
||||||
|
user_id = None
|
||||||
|
user_code = None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegister(User):
|
||||||
|
hash_pwd = None
|
||||||
|
|
||||||
|
def __init__(self, c) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.c = c
|
||||||
|
|
||||||
|
def set_name(self, name: str):
|
||||||
|
if 3 <= len(name) <= 16:
|
||||||
|
self.c.execute(
|
||||||
|
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||||
|
if self.c.fetchone() == (0,):
|
||||||
|
self.name = name
|
||||||
|
else:
|
||||||
|
raise DataExist('Username exists.', 101, -203)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise InputError('Username is invalid.')
|
||||||
|
|
||||||
|
def set_password(self, password: str):
|
||||||
|
if 8 <= len(password) <= 32:
|
||||||
|
self.password = password
|
||||||
|
self.hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||||
|
else:
|
||||||
|
raise InputError('Password is invalid.')
|
||||||
|
|
||||||
|
def set_email(self, email: str):
|
||||||
|
# 邮箱格式懒得多判断
|
||||||
|
if 4 <= len(email) <= 32 and '@' in email and '.' in email:
|
||||||
|
self.c.execute(
|
||||||
|
'''select exists(select * from user where email = :email)''', {'email': email})
|
||||||
|
if self.c.fetchone() == (0,):
|
||||||
|
self.email = email
|
||||||
|
else:
|
||||||
|
raise DataExist('Email address exists.', 102, -204)
|
||||||
|
else:
|
||||||
|
raise InputError('Email address is invalid.')
|
||||||
|
|
||||||
|
def _build_user_code(self):
|
||||||
|
# 生成9位的user_code,用的自然是随机
|
||||||
|
from random import randint
|
||||||
|
random_times = 0
|
||||||
|
|
||||||
|
while random_times <= 1000:
|
||||||
|
random_times += 1
|
||||||
|
user_code = ''.join([str(randint(0, 9)) for _ in range(9)])
|
||||||
|
self.c.execute('''select exists(select * from user where user_code = :user_code)''',
|
||||||
|
{'user_code': user_code})
|
||||||
|
if self.c.fetchone() == (0,):
|
||||||
|
break
|
||||||
|
|
||||||
|
if random_times <= 1000:
|
||||||
|
self.user_code = user_code
|
||||||
|
else:
|
||||||
|
raise ArcError('No available user code.')
|
||||||
|
|
||||||
|
def _build_user_id(self):
|
||||||
|
# 生成user_id,往后加1
|
||||||
|
self.c.execute('''select max(user_id) from user''')
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x[0] is not None:
|
||||||
|
self.user_id = x[0] + 1
|
||||||
|
else:
|
||||||
|
self.user_id = 2000001
|
||||||
|
|
||||||
|
def _insert_user_char(self):
|
||||||
|
# 为用户添加初始角色
|
||||||
|
self.c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||||
|
(self.user_id, 0, 1, 0, 0, 0))
|
||||||
|
self.c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||||
|
(self.user_id, 1, 1, 0, 0, 0))
|
||||||
|
self.c.execute(
|
||||||
|
'''select character_id, max_level, is_uncapped from character''')
|
||||||
|
x = self.c.fetchall()
|
||||||
|
if x:
|
||||||
|
for i in x:
|
||||||
|
exp = 25000 if i[1] == 30 else 10000
|
||||||
|
self.c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
|
||||||
|
(self.user_id, i[0], i[1], exp, i[2], 0))
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
now = int(time.time() * 1000)
|
||||||
|
self._build_user_code()
|
||||||
|
self._build_user_id()
|
||||||
|
self._insert_user_char()
|
||||||
|
|
||||||
|
self.c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||||
|
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket, prog_boost, email)
|
||||||
|
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', :memories, 0, :email)
|
||||||
|
''', {'user_code': self.user_code, 'user_id': self.user_id, 'join_date': now, 'name': self.name, 'password': self.hash_pwd, 'memories': Config.DEFAULT_MEMORIES, 'email': self.email})
|
||||||
|
self.c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||||
|
'user_id': self.user_id})
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def set_name(self, name: str):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def set_password(self, password: str):
|
||||||
|
self.password = password
|
||||||
|
self.hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||||
|
|
||||||
|
def set_device_id(self, device_id: str):
|
||||||
|
self.device_id = device_id
|
||||||
|
|
||||||
|
def set_ip(self, ip: str):
|
||||||
|
self.ip = ip
|
||||||
|
|
||||||
|
def _arc_auto_ban(self):
|
||||||
|
# 多设备自动封号机制,返回封号时长
|
||||||
|
self.c.execute('''delete from login where user_id=?''',
|
||||||
|
(self.user_id, ))
|
||||||
|
self.c.execute(
|
||||||
|
'''select ban_flag from user where user_id=?''', (self.user_id,))
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x and x[0] != '' and x[0] is not None:
|
||||||
|
last_ban_time = int(x[0].split(':', 1)[0])
|
||||||
|
i = 0
|
||||||
|
while i < len(Constant.BAN_TIME) - 1 and Constant.BAN_TIME[i] <= last_ban_time:
|
||||||
|
i += 1
|
||||||
|
ban_time = Constant.BAN_TIME[i]
|
||||||
|
else:
|
||||||
|
ban_time = Constant.BAN_TIME[0]
|
||||||
|
|
||||||
|
ban_flag = ':'.join(
|
||||||
|
(str(ban_time), str(self.now + ban_time * 86400000)))
|
||||||
|
self.c.execute('''update user set ban_flag=? where user_id=?''',
|
||||||
|
(ban_flag, self.user_id))
|
||||||
|
|
||||||
|
return ban_time * 86400000
|
||||||
|
|
||||||
|
def _check_device(self, device_list):
|
||||||
|
should_delete_num = len(
|
||||||
|
device_list) + 1 - Config.LOGIN_DEVICE_NUMBER_LIMIT
|
||||||
|
|
||||||
|
if not Config.ALLOW_LOGIN_SAME_DEVICE:
|
||||||
|
if self.device_id in device_list: # 对相同设备进行删除
|
||||||
|
self.c.execute('''delete from login where login_device=:a and user_id=:b''', {
|
||||||
|
'a': self.device_id, 'b': self.user_id})
|
||||||
|
should_delete_num = len(
|
||||||
|
device_list) + 1 - device_list.count(self.device_id) - Config.LOGIN_DEVICE_NUMBER_LIMIT
|
||||||
|
|
||||||
|
if should_delete_num >= 1: # 删掉多余token
|
||||||
|
if not Config.ALLOW_LOGIN_SAME_DEVICE and Config.ALLOW_BAN_MULTIDEVICE_USER_AUTO: # 自动封号检查
|
||||||
|
self.c.execute(
|
||||||
|
'''select count(*) from login where user_id=? and login_time>?''', (self.user_id, self.now-86400000))
|
||||||
|
if self.c.fetchone()[0] >= Config.LOGIN_DEVICE_NUMBER_LIMIT:
|
||||||
|
remaining_ts = self._arc_auto_ban()
|
||||||
|
raise UserBan('Too many devices logging in during 24 hours.', 105, extra_data={
|
||||||
|
'remaining_ts': remaining_ts})
|
||||||
|
|
||||||
|
self.c.execute('''delete from login where rowid in (select rowid from login where user_id=:user_id limit :a);''',
|
||||||
|
{'user_id': self.user_id, 'a': int(should_delete_num)})
|
||||||
|
|
||||||
|
def login(self, name: str = '', password: str = '', device_id: str = '', ip: str = ''):
|
||||||
|
if name:
|
||||||
|
self.set_name(name)
|
||||||
|
if password:
|
||||||
|
self.set_password(password)
|
||||||
|
if device_id:
|
||||||
|
self.set_device_id(device_id)
|
||||||
|
if ip:
|
||||||
|
self.set_ip(ip)
|
||||||
|
|
||||||
|
self.c.execute('''select user_id, password, ban_flag from user where name = :name''', {
|
||||||
|
'name': self.name})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x is None:
|
||||||
|
raise NoData('Username does not exist.', 104)
|
||||||
|
|
||||||
|
self.now = int(time.time() * 1000)
|
||||||
|
if x[2] is not None and x[2] != '':
|
||||||
|
# 自动封号检查
|
||||||
|
ban_timestamp = int(x[2].split(':', 1)[1])
|
||||||
|
if ban_timestamp > self.now:
|
||||||
|
raise UserBan('Too many devices logging in during 24 hours.', 105, extra_data={
|
||||||
|
'remaining_ts': ban_timestamp-self.now})
|
||||||
|
|
||||||
|
if x[1] == '':
|
||||||
|
# 账号封禁
|
||||||
|
raise UserBan('The account has been banned.', 106)
|
||||||
|
|
||||||
|
if x[1] != self.hash_pwd:
|
||||||
|
raise NoAccess('Wrong password.', 104)
|
||||||
|
|
||||||
|
self.user_id = str(x[0])
|
||||||
|
self.token = base64.b64encode(hashlib.sha256(
|
||||||
|
(self.user_id + str(self.now)).encode("utf8") + urandom(8)).digest()).decode()
|
||||||
|
|
||||||
|
self.c.execute(
|
||||||
|
'''select login_device from login where user_id = :user_id''', {"user_id": self.user_id})
|
||||||
|
y = self.c.fetchall()
|
||||||
|
if y:
|
||||||
|
self._check_device([i[0] if i[0] else '' for i in y])
|
||||||
|
|
||||||
|
self.c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
|
||||||
|
'user_id': self.user_id, 'access_token': self.token, 'device_id': self.device_id, 'time': self.now, 'ip': self.ip})
|
||||||
|
|
||||||
|
|
||||||
|
class UserAuth(User):
|
||||||
|
token = None
|
||||||
|
|
||||||
|
def __init__(self, c) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.c = c
|
||||||
|
|
||||||
|
def token_get_id(self):
|
||||||
|
# 用token获取id,没有考虑不同用户token相同情况,说不定会有bug
|
||||||
|
self.c.execute('''select user_id from login where access_token = :token''', {
|
||||||
|
'token': self.token})
|
||||||
|
x = self.c.fetchone()
|
||||||
|
if x is not None:
|
||||||
|
self.user_id = x[0]
|
||||||
|
else:
|
||||||
|
raise NoAccess('Wrong token.', -4)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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.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})
|
||||||
Binary file not shown.
@@ -4,7 +4,7 @@ import json
|
|||||||
|
|
||||||
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
||||||
|
|
||||||
ARCAEA_SERVER_VERSION = 'v2.8.1'
|
ARCAEA_SERVER_VERSION = 'v2.8.5'
|
||||||
|
|
||||||
|
|
||||||
def main(path='./'):
|
def main(path='./'):
|
||||||
@@ -223,7 +223,8 @@ def main(path='./'):
|
|||||||
price int,
|
price int,
|
||||||
orig_price int,
|
orig_price int,
|
||||||
discount_from int,
|
discount_from int,
|
||||||
discount_to int
|
discount_to int,
|
||||||
|
discount_reason text
|
||||||
);''')
|
);''')
|
||||||
c.execute('''create table if not exists purchase_item(purchase_name text,
|
c.execute('''create table if not exists purchase_item(purchase_name text,
|
||||||
item_id text,
|
item_id text,
|
||||||
@@ -311,34 +312,34 @@ def main(path='./'):
|
|||||||
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0]
|
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0]
|
||||||
|
|
||||||
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
||||||
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 50, 25, 58, 50, 61, 45, 45, 38, 34]
|
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 38, 25, 58, 50, 61, 45, 45, 38, 34]
|
||||||
|
|
||||||
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
||||||
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50]
|
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50]
|
||||||
|
|
||||||
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
||||||
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 50, 30, 49, 15, 34, 45, 45, 38, 67]
|
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67]
|
||||||
|
|
||||||
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
||||||
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60, 51]
|
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51]
|
||||||
|
|
||||||
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
|
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
|
||||||
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90, 75]
|
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75]
|
||||||
|
|
||||||
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
|
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
|
||||||
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60, 100]
|
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100]
|
||||||
|
|
||||||
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
||||||
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60, 51]
|
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51]
|
||||||
|
|
||||||
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
|
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
|
||||||
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90, 75]
|
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75]
|
||||||
|
|
||||||
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
|
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
|
||||||
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60, 100]
|
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100]
|
||||||
|
|
||||||
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
||||||
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]
|
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2]
|
||||||
|
|
||||||
char_core = {
|
char_core = {
|
||||||
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
||||||
@@ -367,7 +368,7 @@ def main(path='./'):
|
|||||||
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
||||||
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
||||||
else:
|
else:
|
||||||
if i != 5 and i != 46:
|
if i != 5:
|
||||||
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
|
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
|
||||||
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
||||||
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
||||||
@@ -386,7 +387,7 @@ def main(path='./'):
|
|||||||
c.execute('''insert into item values(?,"core",1,'')''', (i,))
|
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",
|
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"]
|
"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"]
|
||||||
for i in world_songs:
|
for i in world_songs:
|
||||||
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
|
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
|
||||||
|
|
||||||
@@ -399,6 +400,8 @@ def main(path='./'):
|
|||||||
('fragment', 'fragment', 1, ''))
|
('fragment', 'fragment', 1, ''))
|
||||||
c.execute('''insert into item values(?,?,?,?)''',
|
c.execute('''insert into item values(?,?,?,?)''',
|
||||||
('memory', 'memory', 1, ''))
|
('memory', 'memory', 1, ''))
|
||||||
|
c.execute('''insert into item values(?,?,?,?)''',
|
||||||
|
('anni5tix', 'anni5tix', 1, ''))
|
||||||
|
|
||||||
def insert_items(c, items):
|
def insert_items(c, items):
|
||||||
# 物品数据导入
|
# 物品数据导入
|
||||||
@@ -411,8 +414,12 @@ def main(path='./'):
|
|||||||
discount_to = -1
|
discount_to = -1
|
||||||
else:
|
else:
|
||||||
discount_to = i['discount_to']
|
discount_to = i['discount_to']
|
||||||
c.execute('''insert into purchase values(?,?,?,?,?)''',
|
if 'discount_reason' not in i:
|
||||||
(i['name'], i['price'], i['orig_price'], discount_from, discount_to))
|
discount_reason = ''
|
||||||
|
else:
|
||||||
|
discount_reason = i['discount_reason']
|
||||||
|
c.execute('''insert into purchase values(?,?,?,?,?,?)''',
|
||||||
|
(i['name'], i['price'], i['orig_price'], discount_from, discount_to, discount_reason))
|
||||||
for j in i['items']:
|
for j in i['items']:
|
||||||
if "_id" not in j:
|
if "_id" not in j:
|
||||||
_id = ''
|
_id = ''
|
||||||
|
|||||||
@@ -552,5 +552,23 @@
|
|||||||
],
|
],
|
||||||
"price": 400,
|
"price": 400,
|
||||||
"orig_price": 400
|
"orig_price": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lanota_append_1",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "pack",
|
||||||
|
"id": "lanota_append_1",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 3,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 300,
|
||||||
|
"price": 300
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1126,5 +1126,59 @@
|
|||||||
],
|
],
|
||||||
"orig_price": 100,
|
"orig_price": 100,
|
||||||
"price": 100
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "headbonkache",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "headbonkache",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "aurgelmir",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "aurgelmir",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "picopicotranslation",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "picopicotranslation",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
from flask import Flask, json, request, jsonify, send_from_directory
|
from flask import Flask, json, request, jsonify, send_from_directory
|
||||||
from logging.config import dictConfig
|
from logging.config import dictConfig
|
||||||
from setting import Config
|
from setting import Config
|
||||||
import base64
|
import server
|
||||||
import server.auth
|
import server.auth
|
||||||
import server.info
|
import server.info
|
||||||
import server.setme
|
import server.setme
|
||||||
@@ -17,10 +17,9 @@ import server.arcpurchase
|
|||||||
import server.init
|
import server.init
|
||||||
import server.character
|
import server.character
|
||||||
import server.arclinkplay
|
import server.arclinkplay
|
||||||
from udpserver.udp_main import link_play
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from multiprocessing import Process, Pipe
|
from multiprocessing import Process, Pipe, set_start_method
|
||||||
|
|
||||||
|
|
||||||
from urllib.parse import parse_qs, urlparse
|
from urllib.parse import parse_qs, urlparse
|
||||||
@@ -37,6 +36,7 @@ app.config['SESSION_TYPE'] = 'filesystem'
|
|||||||
app.register_blueprint(web.login.bp)
|
app.register_blueprint(web.login.bp)
|
||||||
app.register_blueprint(web.index.bp)
|
app.register_blueprint(web.index.bp)
|
||||||
app.register_blueprint(api.api_main.bp)
|
app.register_blueprint(api.api_main.bp)
|
||||||
|
app.register_blueprint(server.bp)
|
||||||
|
|
||||||
conn1, conn2 = Pipe()
|
conn1, conn2 = Pipe()
|
||||||
|
|
||||||
@@ -147,60 +147,6 @@ def favicon():
|
|||||||
return app.send_static_file('favicon.ico')
|
return app.send_static_file('favicon.ico')
|
||||||
|
|
||||||
|
|
||||||
@app.route(add_url_prefix('/auth/login'), methods=['POST']) # 登录接口
|
|
||||||
def login():
|
|
||||||
if 'AppVersion' in request.headers: # 版本检查
|
|
||||||
if Config.ALLOW_APPVERSION:
|
|
||||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
|
||||||
return error_return(1203)
|
|
||||||
|
|
||||||
headers = request.headers
|
|
||||||
id_pwd = headers['Authorization']
|
|
||||||
id_pwd = base64.b64decode(id_pwd[6:]).decode()
|
|
||||||
name, password = id_pwd.split(':', 1)
|
|
||||||
if 'DeviceId' in headers:
|
|
||||||
device_id = headers['DeviceId']
|
|
||||||
else:
|
|
||||||
device_id = 'low_version'
|
|
||||||
|
|
||||||
token, user_id, error_code, extra = server.auth.arc_login(
|
|
||||||
name, password, device_id, request.remote_addr)
|
|
||||||
if not error_code:
|
|
||||||
r = {"success": True, "token_type": "Bearer", 'user_id': user_id}
|
|
||||||
r['access_token'] = token
|
|
||||||
return jsonify(r)
|
|
||||||
else:
|
|
||||||
if extra:
|
|
||||||
return error_return(error_code, extra)
|
|
||||||
else:
|
|
||||||
return error_return(error_code)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route(add_url_prefix('/user'), methods=['POST']) # 注册接口
|
|
||||||
def register():
|
|
||||||
if 'AppVersion' in request.headers: # 版本检查
|
|
||||||
if Config.ALLOW_APPVERSION:
|
|
||||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
|
||||||
return error_return(5)
|
|
||||||
|
|
||||||
name = request.form['name']
|
|
||||||
password = request.form['password']
|
|
||||||
email = request.form['email']
|
|
||||||
if 'device_id' in request.form:
|
|
||||||
device_id = request.form['device_id']
|
|
||||||
else:
|
|
||||||
device_id = 'low_version'
|
|
||||||
|
|
||||||
user_id, token, error_code = server.auth.arc_register(
|
|
||||||
name, password, device_id, email, request.remote_addr)
|
|
||||||
if user_id is not None:
|
|
||||||
r = {"success": True, "value": {
|
|
||||||
'user_id': user_id, 'access_token': token}}
|
|
||||||
return jsonify(r)
|
|
||||||
else:
|
|
||||||
return error_return(error_code)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route(add_url_prefix('/purchase/bundle/pack'), methods=['GET']) # 曲包信息
|
@app.route(add_url_prefix('/purchase/bundle/pack'), methods=['GET']) # 曲包信息
|
||||||
@server.auth.auth_required(request)
|
@server.auth.auth_required(request)
|
||||||
def bundle_pack(user_id):
|
def bundle_pack(user_id):
|
||||||
@@ -280,43 +226,6 @@ def user_me(user_id):
|
|||||||
return error_return(108)
|
return error_return(108)
|
||||||
|
|
||||||
|
|
||||||
@app.route(add_url_prefix('/user/me/character'), methods=['POST']) # 角色切换
|
|
||||||
@server.auth.auth_required(request)
|
|
||||||
def character_change(user_id):
|
|
||||||
character_id = request.form['character']
|
|
||||||
skill_sealed = request.form['skill_sealed']
|
|
||||||
|
|
||||||
flag = server.setme.change_char(user_id, character_id, skill_sealed)
|
|
||||||
if flag:
|
|
||||||
return jsonify({
|
|
||||||
"success": True,
|
|
||||||
"value": {
|
|
||||||
"user_id": user_id,
|
|
||||||
"character": character_id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return error_return(108)
|
|
||||||
|
|
||||||
|
|
||||||
# 角色觉醒切换
|
|
||||||
@app.route(add_url_prefix('/<path:path>/toggle_uncap', True), methods=['POST'])
|
|
||||||
@server.auth.auth_required(request)
|
|
||||||
def character_uncap(user_id, path):
|
|
||||||
character_id = int(path[path.find('character')+10:])
|
|
||||||
r = server.setme.change_char_uncap(user_id, character_id)
|
|
||||||
if r is not None:
|
|
||||||
return jsonify({
|
|
||||||
"success": True,
|
|
||||||
"value": {
|
|
||||||
"user_id": user_id,
|
|
||||||
"character": [r]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return error_return(108)
|
|
||||||
|
|
||||||
|
|
||||||
# 角色觉醒
|
# 角色觉醒
|
||||||
@app.route(add_url_prefix('/<path:path>/uncap', True), methods=['POST'])
|
@app.route(add_url_prefix('/<path:path>/uncap', True), methods=['POST'])
|
||||||
@server.auth.auth_required(request)
|
@server.auth.auth_required(request)
|
||||||
@@ -632,10 +541,11 @@ def pack(user_id):
|
|||||||
|
|
||||||
# 单曲购买信息获取
|
# 单曲购买信息获取
|
||||||
@app.route(add_url_prefix('/purchase/bundle/single'), methods=['GET'])
|
@app.route(add_url_prefix('/purchase/bundle/single'), methods=['GET'])
|
||||||
def single():
|
@server.auth.auth_required(request)
|
||||||
|
def single(user_id):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": True,
|
"success": True,
|
||||||
"value": server.arcpurchase.get_single_purchase()
|
"value": server.arcpurchase.get_single_purchase(user_id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -879,6 +789,7 @@ def main():
|
|||||||
app.logger.info('Complete!')
|
app.logger.info('Complete!')
|
||||||
|
|
||||||
if Config.UDP_PORT and Config.UDP_PORT != '':
|
if Config.UDP_PORT and Config.UDP_PORT != '':
|
||||||
|
from udpserver.udp_main import link_play
|
||||||
process = [Process(target=link_play, args=(
|
process = [Process(target=link_play, args=(
|
||||||
conn2, Config.HOST, int(Config.UDP_PORT)))]
|
conn2, Config.HOST, int(Config.UDP_PORT)))]
|
||||||
[p.start() for p in process]
|
[p.start() for p in process]
|
||||||
@@ -898,6 +809,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
set_start_method("spawn")
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
flask
|
flask>=2.0
|
||||||
cryptography
|
cryptography
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
cd /d %~dp0
|
cd /d %~dp0
|
||||||
|
:: Develop server
|
||||||
python -B main.py
|
python -B main.py
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
from setting import Config
|
||||||
|
from . import user
|
||||||
|
from . import auth
|
||||||
|
|
||||||
|
bp = Blueprint('server', __name__, url_prefix=Config.GAME_API_PREFIX)
|
||||||
|
bp.register_blueprint(user.bp)
|
||||||
|
bp.register_blueprint(auth.bp)
|
||||||
@@ -46,7 +46,11 @@ def create_room(conn, user_id, client_song_map):
|
|||||||
song_unlock = get_song_unlock(client_song_map)
|
song_unlock = get_song_unlock(client_song_map)
|
||||||
|
|
||||||
conn.send((1, name, song_unlock))
|
conn.send((1, name, song_unlock))
|
||||||
data = conn.recv()
|
if conn.poll(10):
|
||||||
|
data = conn.recv()
|
||||||
|
else:
|
||||||
|
data = (-1,)
|
||||||
|
|
||||||
if data[0] == 0:
|
if data[0] == 0:
|
||||||
error_code = 0
|
error_code = 0
|
||||||
return error_code, {'roomCode': data[1],
|
return error_code, {'roomCode': data[1],
|
||||||
@@ -74,7 +78,11 @@ def join_room(conn, user_id, client_song_map, room_code):
|
|||||||
song_unlock = get_song_unlock(client_song_map)
|
song_unlock = get_song_unlock(client_song_map)
|
||||||
|
|
||||||
conn.send((2, name, song_unlock, room_code))
|
conn.send((2, name, song_unlock, room_code))
|
||||||
data = conn.recv()
|
if conn.poll(10):
|
||||||
|
data = conn.recv()
|
||||||
|
else:
|
||||||
|
data = (-1,)
|
||||||
|
|
||||||
if data[0] == 0:
|
if data[0] == 0:
|
||||||
error_code = 0
|
error_code = 0
|
||||||
return error_code, {'roomCode': data[1],
|
return error_code, {'roomCode': data[1],
|
||||||
@@ -96,7 +104,11 @@ def update_room(conn, user_id, token):
|
|||||||
error_code = 108
|
error_code = 108
|
||||||
|
|
||||||
conn.send((3, int(token)))
|
conn.send((3, int(token)))
|
||||||
data = conn.recv()
|
if conn.poll(10):
|
||||||
|
data = conn.recv()
|
||||||
|
else:
|
||||||
|
data = (-1,)
|
||||||
|
|
||||||
if data[0] == 0:
|
if data[0] == 0:
|
||||||
error_code = 0
|
error_code = 0
|
||||||
return error_code, {'roomCode': data[1],
|
return error_code, {'roomCode': data[1],
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ from server.sql import Connect
|
|||||||
import server.item
|
import server.item
|
||||||
import server.character
|
import server.character
|
||||||
import time
|
import time
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
def int2b(x):
|
||||||
@@ -13,7 +12,7 @@ def int2b(x):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_purchase(c, type='pack'):
|
def get_purchase(c, user_id, type='pack'):
|
||||||
# 读取packs内容,返回字典列表
|
# 读取packs内容,返回字典列表
|
||||||
c.execute(
|
c.execute(
|
||||||
'''select * from purchase where purchase_name in (select purchase_name from purchase_item where type = :a)''', {'a': type})
|
'''select * from purchase where purchase_name in (select purchase_name from purchase_item where type = :a)''', {'a': type})
|
||||||
@@ -50,7 +49,6 @@ def get_purchase(c, type='pack'):
|
|||||||
"amount": amount
|
"amount": amount
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
if t is not None:
|
if t is not None:
|
||||||
# 放到列表头
|
# 放到列表头
|
||||||
items = [t, items]
|
items = [t, items]
|
||||||
@@ -62,19 +60,27 @@ def get_purchase(c, type='pack'):
|
|||||||
|
|
||||||
if i[3] > 0:
|
if i[3] > 0:
|
||||||
r['discount_from'] = i[3]
|
r['discount_from'] = i[3]
|
||||||
if i[4] > 0:
|
if i[4] > 0:
|
||||||
r['discount_to'] = i[4]
|
r['discount_to'] = i[4]
|
||||||
|
|
||||||
|
if i[5] == 'anni5tix' and i[3] <= int(time.time() * 1000) <= i[4]:
|
||||||
|
c.execute(
|
||||||
|
'''select amount from user_item where user_id=? and item_id="anni5tix"''', (user_id,))
|
||||||
|
z = c.fetchone()
|
||||||
|
if z and z[0] >= 1:
|
||||||
|
r['discount_reason'] = 'anni5tix'
|
||||||
|
r['price'] = 0
|
||||||
|
|
||||||
re.append(r)
|
re.append(r)
|
||||||
|
|
||||||
return re
|
return re
|
||||||
|
|
||||||
|
|
||||||
def get_single_purchase():
|
def get_single_purchase(user_id):
|
||||||
# main里面没开数据库,这里写一下代替
|
# main里面没开数据库,这里写一下代替
|
||||||
re = []
|
re = []
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
re = get_purchase(c, type='single')
|
re = get_purchase(c, user_id, 'single')
|
||||||
|
|
||||||
return re
|
return re
|
||||||
|
|
||||||
@@ -98,6 +104,25 @@ def buy_item(c, user_id, price):
|
|||||||
return True, ticket - price
|
return True, ticket - price
|
||||||
|
|
||||||
|
|
||||||
|
def buy_item_with_anni5tix(c, user_id):
|
||||||
|
# 兑换券购买接口,返回成功与否标志
|
||||||
|
c.execute('''select amount from user_item where user_id = :a and item_id = "anni5tix"''',
|
||||||
|
{'a': user_id})
|
||||||
|
amount = c.fetchone()
|
||||||
|
if amount:
|
||||||
|
amount = amount[0]
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if amount <= 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
c.execute('''update user_item set amount = :b where user_id = :a and item_id = "anni5tix"''',
|
||||||
|
{'a': user_id, 'b': amount-1})
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def buy_thing(user_id, purchase_id):
|
def buy_thing(user_id, purchase_id):
|
||||||
# 购买物品接口,返回字典
|
# 购买物品接口,返回字典
|
||||||
success_flag = False
|
success_flag = False
|
||||||
@@ -107,7 +132,7 @@ def buy_thing(user_id, purchase_id):
|
|||||||
characters = []
|
characters = []
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
c.execute('''select price, orig_price, discount_from, discount_to from purchase where purchase_name=:a''',
|
c.execute('''select price, orig_price, discount_from, discount_to, discount_reason from purchase where purchase_name=:a''',
|
||||||
{'a': purchase_id})
|
{'a': purchase_id})
|
||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
price = 0
|
price = 0
|
||||||
@@ -117,6 +142,7 @@ def buy_thing(user_id, purchase_id):
|
|||||||
orig_price = x[1]
|
orig_price = x[1]
|
||||||
discount_from = x[2]
|
discount_from = x[2]
|
||||||
discount_to = x[3]
|
discount_to = x[3]
|
||||||
|
discount_reason = x[4]
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
@@ -130,6 +156,8 @@ def buy_thing(user_id, purchase_id):
|
|||||||
now = int(time.time() * 1000)
|
now = int(time.time() * 1000)
|
||||||
if not(discount_from <= now <= discount_to):
|
if not(discount_from <= now <= discount_to):
|
||||||
price = orig_price
|
price = orig_price
|
||||||
|
elif discount_reason == 'anni5tix' and buy_item_with_anni5tix(c, user_id):
|
||||||
|
price = 0
|
||||||
|
|
||||||
flag, ticket = buy_item(c, user_id, price)
|
flag, ticket = buy_item(c, user_id, price)
|
||||||
|
|
||||||
|
|||||||
@@ -1,187 +1,43 @@
|
|||||||
import hashlib
|
from flask import Blueprint, request, jsonify
|
||||||
import time
|
|
||||||
from server.sql import Connect
|
|
||||||
import functools
|
import functools
|
||||||
|
import base64
|
||||||
|
from core.error import ArcError, NoAccess
|
||||||
|
from core.user import UserAuth, UserLogin
|
||||||
|
from core.sql import Connect
|
||||||
|
from .func import error_return
|
||||||
from setting import Config
|
from setting import Config
|
||||||
from flask import jsonify
|
|
||||||
|
|
||||||
BAN_TIME = [1, 3, 7, 15, 31]
|
|
||||||
|
|
||||||
|
|
||||||
def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
|
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
||||||
# 查询数据库中的user表,验证账号密码,返回并记录token和user_id,多返回个error code和extra
|
|
||||||
# token采用user_id和时间戳连接后hash生成(真的是瞎想的,没用bear)
|
|
||||||
# 密码和token的加密方式为 SHA-256
|
|
||||||
|
|
||||||
error_code = 108
|
|
||||||
token = None
|
@bp.route('/login', methods=['POST']) # 登录接口
|
||||||
user_id = None
|
def login():
|
||||||
|
if 'AppVersion' in request.headers: # 版本检查
|
||||||
|
if Config.ALLOW_APPVERSION:
|
||||||
|
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||||
|
return error_return(NoAccess('Wrong app version.', 1203))
|
||||||
|
|
||||||
|
headers = request.headers
|
||||||
|
request.form['grant_type']
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
try:
|
||||||
c.execute('''select user_id, password, ban_flag from user where name = :name''', {
|
id_pwd = headers['Authorization']
|
||||||
'name': name})
|
id_pwd = base64.b64decode(id_pwd[6:]).decode()
|
||||||
x = c.fetchone()
|
name, password = id_pwd.split(':', 1)
|
||||||
if x is not None:
|
if 'DeviceId' in headers:
|
||||||
now = int(time.time() * 1000)
|
device_id = headers['DeviceId']
|
||||||
if x[2] is not None and x[2] != '':
|
|
||||||
# 自动封号检查
|
|
||||||
ban_timestamp = int(x[2].split(':', 1)[1])
|
|
||||||
if ban_timestamp > now:
|
|
||||||
return None, 105, {'remaining_ts': ban_timestamp-now}
|
|
||||||
if x[1] == '':
|
|
||||||
# 账号封禁
|
|
||||||
error_code = 106
|
|
||||||
elif x[1] == hash_pwd:
|
|
||||||
user_id = str(x[0])
|
|
||||||
token = hashlib.sha256(
|
|
||||||
(user_id + str(now)).encode("utf8")).hexdigest()
|
|
||||||
c.execute(
|
|
||||||
'''select login_device from login where user_id = :user_id''', {"user_id": user_id})
|
|
||||||
y = c.fetchall()
|
|
||||||
if y:
|
|
||||||
device_list = []
|
|
||||||
for i in y:
|
|
||||||
if i[0]:
|
|
||||||
device_list.append(i[0])
|
|
||||||
else:
|
|
||||||
device_list.append('')
|
|
||||||
|
|
||||||
should_delete_num = len(
|
|
||||||
device_list) + 1 - Config.LOGIN_DEVICE_NUMBER_LIMIT
|
|
||||||
|
|
||||||
if not Config.ALLOW_LOGIN_SAME_DEVICE:
|
|
||||||
if device_id in device_list: # 对相同设备进行删除
|
|
||||||
c.execute('''delete from login where login_device=:a and user_id=:b''', {
|
|
||||||
'a': device_id, 'b': user_id})
|
|
||||||
should_delete_num = len(
|
|
||||||
device_list) + 1 - device_list.count(device_id) - Config.LOGIN_DEVICE_NUMBER_LIMIT
|
|
||||||
|
|
||||||
if should_delete_num >= 1: # 删掉多余token
|
|
||||||
if not Config.ALLOW_LOGIN_SAME_DEVICE and Config.ALLOW_BAN_MULTIDEVICE_USER_AUTO: # 自动封号检查
|
|
||||||
c.execute(
|
|
||||||
'''select count(*) from login where user_id=? and login_time>?''', (user_id, now-86400000))
|
|
||||||
if c.fetchone()[0] >= Config.LOGIN_DEVICE_NUMBER_LIMIT:
|
|
||||||
remaining_ts = arc_auto_ban(c, user_id, now)
|
|
||||||
return None, None, 105, {'remaining_ts': remaining_ts}
|
|
||||||
|
|
||||||
c.execute('''delete from login where rowid in (select rowid from login where user_id=:user_id limit :a);''',
|
|
||||||
{'user_id': user_id, 'a': int(should_delete_num)})
|
|
||||||
|
|
||||||
c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
|
|
||||||
'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
|
|
||||||
error_code = None
|
|
||||||
else:
|
else:
|
||||||
# 密码错误
|
device_id = 'low_version'
|
||||||
error_code = 104
|
|
||||||
else:
|
|
||||||
# 用户名错误
|
|
||||||
error_code = 104
|
|
||||||
|
|
||||||
return token, user_id, error_code, None
|
user = UserLogin(c)
|
||||||
|
user.login(name, password, device_id, request.remote_addr)
|
||||||
|
|
||||||
|
return jsonify({"success": True, "token_type": "Bearer", 'user_id': user.user_id, 'access_token': user.token})
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
|
||||||
def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册
|
return error_return()
|
||||||
# 账号注册,记录hash密码、用户名和邮箱,生成user_id和user_code,自动登录返回token
|
|
||||||
# token和密码的处理同登录部分
|
|
||||||
|
|
||||||
def build_user_code(c):
|
|
||||||
# 生成9位的user_code,用的自然是随机
|
|
||||||
import random
|
|
||||||
flag = True
|
|
||||||
while flag:
|
|
||||||
user_code = ''.join([str(random.randint(0, 9)) for i in range(9)])
|
|
||||||
c.execute('''select exists(select * from user where user_code = :user_code)''',
|
|
||||||
{'user_code': user_code})
|
|
||||||
if c.fetchone() == (0,):
|
|
||||||
flag = False
|
|
||||||
return user_code
|
|
||||||
|
|
||||||
def build_user_id(c):
|
|
||||||
# 生成user_id,往后加1
|
|
||||||
c.execute('''select max(user_id) from user''')
|
|
||||||
x = c.fetchone()
|
|
||||||
if x[0] is not None:
|
|
||||||
return x[0] + 1
|
|
||||||
else:
|
|
||||||
return 2000001
|
|
||||||
|
|
||||||
def insert_user_char(c, user_id):
|
|
||||||
# 为用户添加初始角色
|
|
||||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
|
||||||
(user_id, 0, 1, 0, 0, 0))
|
|
||||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
|
||||||
(user_id, 1, 1, 0, 0, 0))
|
|
||||||
c.execute('''select character_id, max_level, is_uncapped from character''')
|
|
||||||
x = c.fetchall()
|
|
||||||
if x:
|
|
||||||
for i in x:
|
|
||||||
exp = 25000 if i[1] == 30 else 10000
|
|
||||||
c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
|
|
||||||
(user_id, i[0], i[1], exp, i[2], 0))
|
|
||||||
|
|
||||||
user_id = None
|
|
||||||
token = None
|
|
||||||
error_code = 108
|
|
||||||
with Connect() as c:
|
|
||||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
|
||||||
c.execute(
|
|
||||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
|
||||||
if c.fetchone() == (0,):
|
|
||||||
c.execute(
|
|
||||||
'''select exists(select * from user where email = :email)''', {'email': email})
|
|
||||||
if c.fetchone() == (0,):
|
|
||||||
user_code = build_user_code(c)
|
|
||||||
user_id = build_user_id(c)
|
|
||||||
now = int(time.time() * 1000)
|
|
||||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
|
||||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket, prog_boost, email)
|
|
||||||
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', :memories, 0, :email)
|
|
||||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd, 'memories': Config.DEFAULT_MEMORIES, 'email': email})
|
|
||||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
|
||||||
'user_id': user_id})
|
|
||||||
|
|
||||||
token = hashlib.sha256(
|
|
||||||
(str(user_id) + str(now)).encode("utf8")).hexdigest()
|
|
||||||
c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
|
|
||||||
'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
|
|
||||||
|
|
||||||
insert_user_char(c, user_id)
|
|
||||||
error_code = 0
|
|
||||||
else:
|
|
||||||
error_code = 102
|
|
||||||
else:
|
|
||||||
error_code = 101
|
|
||||||
|
|
||||||
return user_id, token, error_code
|
|
||||||
|
|
||||||
|
|
||||||
def token_get_id(token: str):
|
|
||||||
# 用token获取id,没有考虑不同用户token相同情况,说不定会有bug
|
|
||||||
|
|
||||||
user_id = None
|
|
||||||
with Connect() as c:
|
|
||||||
c.execute('''select user_id from login where access_token = :token''', {
|
|
||||||
'token': token})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
user_id = x[0]
|
|
||||||
|
|
||||||
return user_id
|
|
||||||
|
|
||||||
|
|
||||||
def code_get_id(user_code):
|
|
||||||
# 用user_code获取id
|
|
||||||
|
|
||||||
user_id = None
|
|
||||||
|
|
||||||
with Connect() as c:
|
|
||||||
c.execute('''select user_id from user where user_code = :a''',
|
|
||||||
{'a': user_code})
|
|
||||||
x = c.fetchone()
|
|
||||||
if x is not None:
|
|
||||||
user_id = x[0]
|
|
||||||
|
|
||||||
return user_id
|
|
||||||
|
|
||||||
|
|
||||||
def auth_required(request):
|
def auth_required(request):
|
||||||
@@ -190,44 +46,22 @@ def auth_required(request):
|
|||||||
@functools.wraps(view)
|
@functools.wraps(view)
|
||||||
def wrapped_view(*args, **kwargs):
|
def wrapped_view(*args, **kwargs):
|
||||||
|
|
||||||
user_id = None
|
|
||||||
headers = request.headers
|
headers = request.headers
|
||||||
|
|
||||||
if 'AppVersion' in headers: # 版本检查
|
if 'AppVersion' in headers: # 版本检查
|
||||||
if Config.ALLOW_APPVERSION:
|
if Config.ALLOW_APPVERSION:
|
||||||
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||||
return jsonify({"success": False, "error_code": 1203})
|
return error_return(NoAccess('Wrong app version.', 1203))
|
||||||
|
|
||||||
if 'Authorization' in headers:
|
with Connect() as c:
|
||||||
token = headers['Authorization']
|
try:
|
||||||
token = token[7:]
|
user = UserAuth(c)
|
||||||
user_id = token_get_id(token)
|
user.token = headers['Authorization'][7:]
|
||||||
|
return view(user.token_get_id(), *args, **kwargs)
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
|
||||||
if user_id is not None:
|
return error_return()
|
||||||
return view(user_id, *args, **kwargs)
|
|
||||||
else:
|
|
||||||
return jsonify({"success": False, "error_code": 108})
|
|
||||||
|
|
||||||
return wrapped_view
|
return wrapped_view
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def arc_auto_ban(c, user_id, now):
|
|
||||||
# 多设备自动封号机制,返回封号时长
|
|
||||||
c.execute('''delete from login where user_id=?''', (user_id, ))
|
|
||||||
c.execute('''select ban_flag from user where user_id=?''', (user_id,))
|
|
||||||
x = c.fetchone()
|
|
||||||
if x and x[0] != '' and x[0] is not None:
|
|
||||||
last_ban_time = int(x[0].split(':', 1)[0])
|
|
||||||
i = 0
|
|
||||||
while i < len(BAN_TIME) - 1 and BAN_TIME[i] <= last_ban_time:
|
|
||||||
i += 1
|
|
||||||
ban_time = BAN_TIME[i]
|
|
||||||
else:
|
|
||||||
ban_time = BAN_TIME[0]
|
|
||||||
|
|
||||||
ban_flag = ':'.join((str(ban_time), str(now + ban_time*24*60*60*1000)))
|
|
||||||
c.execute('''update user set ban_flag=? where user_id=?''',
|
|
||||||
(ban_flag, user_id))
|
|
||||||
|
|
||||||
return ban_time*24*60*60*1000
|
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ def get_one_character(c, user_id, character_id):
|
|||||||
"name": x[7],
|
"name": x[7],
|
||||||
"character_id": x[1]
|
"character_id": x[1]
|
||||||
}
|
}
|
||||||
if x[1] == 21:
|
if x[1] == 21 or x[1] == 46:
|
||||||
r["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
|
r["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
|
||||||
|
|
||||||
return r
|
return r
|
||||||
@@ -211,7 +211,7 @@ def char_use_core(user_id, character_id, amount):
|
|||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
if x:
|
if x:
|
||||||
exp, level = calc_level_up(
|
exp, level = calc_level_up(
|
||||||
c, user_id, character_id, x[0], amount*Config.CORE_EXP)
|
c, user_id, character_id, x[0], amount*Constant.CORE_EXP)
|
||||||
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
||||||
(level, exp, user_id, character_id))
|
(level, exp, user_id, character_id))
|
||||||
server.item.claim_user_item(
|
server.item.claim_user_item(
|
||||||
|
|||||||
@@ -590,7 +590,7 @@ class Constant:
|
|||||||
"complete": 1
|
"complete": 1
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "saikyostronger|2|3|laqryma|2",
|
"unlock_key": "saikyostronger|2|3|laqryma|2",
|
||||||
"complete": 1
|
"complete": 3
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "purgatorium|1|0",
|
"unlock_key": "purgatorium|1|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
@@ -636,6 +636,9 @@ class Constant:
|
|||||||
}, {
|
}, {
|
||||||
"unlock_key": "nhelv|2|0",
|
"unlock_key": "nhelv|2|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
|
}, {
|
||||||
|
"unlock_key": "nekonote|2|0",
|
||||||
|
"complete": 1
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "memoryforest|1|0",
|
"unlock_key": "memoryforest|1|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
@@ -681,6 +684,9 @@ class Constant:
|
|||||||
}, {
|
}, {
|
||||||
"unlock_key": "lostdesire|1|0",
|
"unlock_key": "lostdesire|1|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
|
}, {
|
||||||
|
"unlock_key": "nekonote|1|0",
|
||||||
|
"complete": 1
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "lostcivilization|1|0",
|
"unlock_key": "lostcivilization|1|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
@@ -777,12 +783,6 @@ class Constant:
|
|||||||
}, {
|
}, {
|
||||||
"unlock_key": "gothiveofra|1|0",
|
"unlock_key": "gothiveofra|1|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
}, {
|
|
||||||
"unlock_key": "etherstrike|1|0",
|
|
||||||
"complete": 1
|
|
||||||
}, {
|
|
||||||
"unlock_key": "singularity|2|0",
|
|
||||||
"complete": 1
|
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "lightningscrew|1|0",
|
"unlock_key": "lightningscrew|1|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
@@ -801,6 +801,9 @@ class Constant:
|
|||||||
}, {
|
}, {
|
||||||
"unlock_key": "pragmatism|2|0",
|
"unlock_key": "pragmatism|2|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
|
}, {
|
||||||
|
"unlock_key": "stasis|2|0",
|
||||||
|
"complete": 1
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "oracle|2|0",
|
"unlock_key": "oracle|2|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
@@ -861,6 +864,12 @@ class Constant:
|
|||||||
}, {
|
}, {
|
||||||
"unlock_key": "etherstrike|2|0",
|
"unlock_key": "etherstrike|2|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
|
}, {
|
||||||
|
"unlock_key": "singularity|2|0",
|
||||||
|
"complete": 1
|
||||||
|
}, {
|
||||||
|
"unlock_key": "etherstrike|1|0",
|
||||||
|
"complete": 1
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "flyburg|1|0",
|
"unlock_key": "flyburg|1|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
@@ -1125,6 +1134,9 @@ class Constant:
|
|||||||
}, {
|
}, {
|
||||||
"unlock_key": "vector|2|0",
|
"unlock_key": "vector|2|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
|
}, {
|
||||||
|
"unlock_key": "stasis|1|0",
|
||||||
|
"complete": 1
|
||||||
}, {
|
}, {
|
||||||
"unlock_key": "loschen|2|0",
|
"unlock_key": "loschen|2|0",
|
||||||
"complete": 1
|
"complete": 1
|
||||||
|
|||||||
66
latest version/server/func.py
Normal file
66
latest version/server/func.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
from flask import jsonify
|
||||||
|
from core.error import ArcError
|
||||||
|
|
||||||
|
default_error = ArcError('Unknown Error')
|
||||||
|
|
||||||
|
|
||||||
|
def error_return(e: ArcError = default_error): # 错误返回
|
||||||
|
# -7 处理交易时发生了错误
|
||||||
|
# -5 所有的曲目都已经下载完毕
|
||||||
|
# -4 您的账号已在别处登录
|
||||||
|
# -3 无法连接至服务器
|
||||||
|
# 2 Arcaea服务器正在维护
|
||||||
|
# 9 新版本请等待几分钟
|
||||||
|
# 100 无法在此ip地址下登录游戏
|
||||||
|
# 101 用户名占用
|
||||||
|
# 102 电子邮箱已注册
|
||||||
|
# 103 已有一个账号由此设备创建
|
||||||
|
# 104 用户名密码错误
|
||||||
|
# 105 24小时内登入两台设备
|
||||||
|
# 106 121 账户冻结
|
||||||
|
# 107 你没有足够的体力
|
||||||
|
# 113 活动已结束
|
||||||
|
# 114 该活动已结束,您的成绩不会提交
|
||||||
|
# 115 请输入有效的电子邮箱地址
|
||||||
|
# 120 封号警告
|
||||||
|
# 122 账户暂时冻结
|
||||||
|
# 123 账户被限制
|
||||||
|
# 124 你今天不能再使用这个IP地址创建新的账号
|
||||||
|
# 150 非常抱歉您已被限制使用此功能
|
||||||
|
# 151 目前无法使用此功能
|
||||||
|
# 401 用户不存在
|
||||||
|
# 403 无法连接至服务器
|
||||||
|
# 501 502 -6 此物品目前无法获取
|
||||||
|
# 504 无效的序列码
|
||||||
|
# 505 此序列码已被使用
|
||||||
|
# 506 你已拥有了此物品
|
||||||
|
# 601 好友列表已满
|
||||||
|
# 602 此用户已是好友
|
||||||
|
# 604 你不能加自己为好友
|
||||||
|
# 903 下载量超过了限制,请24小时后重试
|
||||||
|
# 905 请在再次使用此功能前等待24小时
|
||||||
|
# 1001 设备数量达到上限
|
||||||
|
# 1002 此设备已使用过此功能
|
||||||
|
# 1201 房间已满
|
||||||
|
# 1202 房间号码无效
|
||||||
|
# 1203 请将Arcaea更新至最新版本
|
||||||
|
# 1205 此房间目前无法加入
|
||||||
|
# 9801 下载歌曲时发生问题,请再试一次
|
||||||
|
# 9802 保存歌曲时发生问题,请检查设备空间容量
|
||||||
|
# 9803 下载已取消
|
||||||
|
# 9905 没有在云端发现任何数据
|
||||||
|
# 9907 更新数据时发生了问题
|
||||||
|
# 9908 服务器只支持最新的版本,请更新Arcaea
|
||||||
|
# 其它 发生未知错误
|
||||||
|
r = {"success": False, "error_code": e.error_code}
|
||||||
|
if e.extra_data:
|
||||||
|
r['extra'] = e.extra_data
|
||||||
|
|
||||||
|
return jsonify(r)
|
||||||
|
|
||||||
|
|
||||||
|
def success_return(value):
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"value": value
|
||||||
|
})
|
||||||
@@ -208,7 +208,7 @@ def get_user_me_c(user_id):
|
|||||||
def get_purchase_pack(user_id):
|
def get_purchase_pack(user_id):
|
||||||
# 返回曲包数据
|
# 返回曲包数据
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
return server.arcpurchase.get_purchase(c, 'pack')
|
return server.arcpurchase.get_purchase(c, user_id)
|
||||||
|
|
||||||
|
|
||||||
def get_game_info():
|
def get_game_info():
|
||||||
@@ -261,7 +261,7 @@ def arc_aggregate_big(user_id):
|
|||||||
"value": get_user_me(c, user_id)
|
"value": get_user_me(c, user_id)
|
||||||
}, {
|
}, {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"value": server.arcpurchase.get_purchase(c, 'pack')
|
"value": server.arcpurchase.get_purchase(c, user_id)
|
||||||
}, {
|
}, {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"value": id_2
|
"value": id_2
|
||||||
|
|||||||
@@ -55,20 +55,20 @@ def claim_user_item(c, user_id, item_id, item_type, amount=1):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if item_type == 'core':
|
if item_type in ['core', 'anni5tix']:
|
||||||
c.execute(
|
c.execute(
|
||||||
'''select amount from user_item where user_id=? and item_id=? and type="core"''', (user_id, item_id))
|
'''select amount from user_item where user_id=? and item_id=? and type=?''', (user_id, item_id, item_type))
|
||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
if x:
|
if x:
|
||||||
if x[0] + amount < 0: # 数量不足
|
if x[0] + amount < 0: # 数量不足
|
||||||
return False
|
return False
|
||||||
c.execute('''update user_item set amount=? where user_id=? and item_id=? and type="core"''',
|
c.execute('''update user_item set amount=? where user_id=? and item_id=? and type=?''',
|
||||||
(x[0]+amount, user_id, item_id))
|
(x[0]+amount, user_id, item_id, item_type))
|
||||||
else:
|
else:
|
||||||
if amount < 0: # 添加数量错误
|
if amount < 0: # 添加数量错误
|
||||||
return False
|
return False
|
||||||
c.execute('''insert into user_item values(?,?,"core",?)''',
|
c.execute('''insert into user_item values(?,?,?,?)''',
|
||||||
(user_id, item_id, amount))
|
(user_id, item_id, item_type, amount))
|
||||||
|
|
||||||
elif item_type == 'memory':
|
elif item_type == 'memory':
|
||||||
c.execute('''select ticket from user where user_id=?''', (user_id,))
|
c.execute('''select ticket from user where user_id=?''', (user_id,))
|
||||||
|
|||||||
73
latest version/server/user.py
Normal file
73
latest version/server/user.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
from flask import Blueprint, request
|
||||||
|
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 .func import error_return, success_return
|
||||||
|
from .auth import auth_required
|
||||||
|
from setting import Config
|
||||||
|
|
||||||
|
bp = Blueprint('user', __name__, url_prefix='/user')
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('', methods=['POST']) # 注册接口
|
||||||
|
def register():
|
||||||
|
if 'AppVersion' in request.headers: # 版本检查
|
||||||
|
if Config.ALLOW_APPVERSION:
|
||||||
|
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||||
|
return error_return(NoAccess('Wrong app version.', 1203))
|
||||||
|
|
||||||
|
with Connect() as c:
|
||||||
|
try:
|
||||||
|
new_user = UserRegister(c)
|
||||||
|
new_user.set_name(request.form['name'])
|
||||||
|
new_user.set_password(request.form['password'])
|
||||||
|
new_user.set_email(request.form['email'])
|
||||||
|
if 'device_id' in request.form:
|
||||||
|
device_id = request.form['device_id']
|
||||||
|
else:
|
||||||
|
device_id = 'low_version'
|
||||||
|
|
||||||
|
new_user.register()
|
||||||
|
|
||||||
|
# 注册后自动登录
|
||||||
|
user = UserLogin(c)
|
||||||
|
user.login(new_user.name, new_user.password,
|
||||||
|
device_id, request.remote_addr)
|
||||||
|
return success_return({'user_id': user.user_id, 'access_token': user.token})
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/me/character', methods=['POST']) # 角色切换
|
||||||
|
@auth_required(request)
|
||||||
|
def character_change(user_id):
|
||||||
|
with Connect() as c:
|
||||||
|
try:
|
||||||
|
user = UserOnline(c, user_id)
|
||||||
|
user.change_character(
|
||||||
|
int(request.form['character']), request.form['skill_sealed'] == 'true')
|
||||||
|
|
||||||
|
return success_return({'user_id': user.user_id, 'character': user.character.character_id})
|
||||||
|
except ArcError as e:
|
||||||
|
return error_return(e)
|
||||||
|
return error_return()
|
||||||
|
|
||||||
|
|
||||||
|
# 角色觉醒切换
|
||||||
|
@bp.route('/me/character/<int:character_id>/toggle_uncap', methods=['POST'])
|
||||||
|
@auth_required(request)
|
||||||
|
def toggle_uncap(user_id, character_id):
|
||||||
|
with Connect() as c:
|
||||||
|
try:
|
||||||
|
user = User()
|
||||||
|
user.user_id = user_id
|
||||||
|
character = UserCharacter(c)
|
||||||
|
character.character_id = 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()
|
||||||
@@ -19,7 +19,7 @@ class Config():
|
|||||||
游戏API地址前缀
|
游戏API地址前缀
|
||||||
Game API's URL prefix
|
Game API's URL prefix
|
||||||
'''
|
'''
|
||||||
GAME_API_PREFIX = '/bridge/18'
|
GAME_API_PREFIX = '/years/19'
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
@@ -30,7 +30,7 @@ class Config():
|
|||||||
Allowed game versions
|
Allowed game versions
|
||||||
If it is blank, all are allowed.
|
If it is blank, all are allowed.
|
||||||
'''
|
'''
|
||||||
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.12.0', '3.12.0c']
|
ALLOW_APPVERSION = ['3.12.6', '3.12.6c', '3.12.8', '3.12.8c']
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
@@ -75,7 +75,7 @@ class Config():
|
|||||||
愚人节模式开关
|
愚人节模式开关
|
||||||
Switch of April Fool's Day
|
Switch of April Fool's Day
|
||||||
'''
|
'''
|
||||||
IS_APRILFOOLS = True
|
IS_APRILFOOLS = False
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
<span>Discount to: </span>
|
<span>Discount to: </span>
|
||||||
<span class="char-num">{{item['discount_to']}}</span>
|
<span class="char-num">{{item['discount_to']}}</span>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
<span>Whether it is allowed to purchase with exchange certificate: </span>
|
||||||
|
<span class="char-num">{{item['discount_reason']}}</span>
|
||||||
|
<br />
|
||||||
{% if item['items'] %}<br />
|
{% if item['items'] %}<br />
|
||||||
{% for x in item['items'] %}
|
{% for x in item['items'] %}
|
||||||
<span>Item id: </span>
|
<span>Item id: </span>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<option value='core'>Character core</option>
|
<option value='core'>Character core</option>
|
||||||
<option value='fragment'>Fragment</option>
|
<option value='fragment'>Fragment</option>
|
||||||
<option value='memory'>Memory</option>
|
<option value='memory'>Memory</option>
|
||||||
|
<option value='anni5tix'>Anniversary 5 ticket</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<label for="amount">Amount</label>
|
<label for="amount">Amount</label>
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
<input type="datetime-local" name="discount_from" id="discount_from">
|
<input type="datetime-local" name="discount_from" id="discount_from">
|
||||||
<label for="discount_to">Discount to</label>
|
<label for="discount_to">Discount to</label>
|
||||||
<input type="datetime-local" name="discount_to" id="discount_to">
|
<input type="datetime-local" name="discount_to" id="discount_to">
|
||||||
|
<div>是否允许使用兑换券购买 Whether it is allowed to purchase with Anniversary 5 ticket:
|
||||||
|
<label><input type="radio" name="discount_reason" value="0">No</label>
|
||||||
|
<label><input type="radio" name="discount_reason" value="anni5tix">Yes</label>
|
||||||
|
</div>
|
||||||
<div class="content">时间填写是一个HTML5控件</div>
|
<div class="content">时间填写是一个HTML5控件</div>
|
||||||
<div class="content">Time filling is an HTML5 control.</div>
|
<div class="content">Time filling is an HTML5 control.</div>
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<option value='core'>Character core</option>
|
<option value='core'>Character core</option>
|
||||||
<option value='fragment'>Fragment</option>
|
<option value='fragment'>Fragment</option>
|
||||||
<option value='memory'>Memory</option>
|
<option value='memory'>Memory</option>
|
||||||
|
<option value='anni5tix'>Anniversary 5 ticket</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<label for="amount">Amount</label>
|
<label for="amount">Amount</label>
|
||||||
|
|||||||
@@ -764,6 +764,7 @@ def change_purchase():
|
|||||||
orig_price = request.form['orig_price']
|
orig_price = request.form['orig_price']
|
||||||
discount_from = request.form['discount_from']
|
discount_from = request.form['discount_from']
|
||||||
discount_to = request.form['discount_to']
|
discount_to = request.form['discount_to']
|
||||||
|
discount_reason = request.form['discount_reason']
|
||||||
|
|
||||||
if price:
|
if price:
|
||||||
price = int(price)
|
price = int(price)
|
||||||
@@ -783,6 +784,10 @@ def change_purchase():
|
|||||||
discount_to, "%Y-%m-%dT%H:%M"))) * 1000
|
discount_to, "%Y-%m-%dT%H:%M"))) * 1000
|
||||||
else:
|
else:
|
||||||
discount_to = -1
|
discount_to = -1
|
||||||
|
|
||||||
|
if not discount_reason:
|
||||||
|
discount_reason = ''
|
||||||
|
|
||||||
except:
|
except:
|
||||||
error = '数据错误 Wrong data.'
|
error = '数据错误 Wrong data.'
|
||||||
flash(error)
|
flash(error)
|
||||||
@@ -792,8 +797,8 @@ def change_purchase():
|
|||||||
c.execute(
|
c.execute(
|
||||||
'''select exists(select * from purchase where purchase_name=:a)''', {'a': purchase_name})
|
'''select exists(select * from purchase where purchase_name=:a)''', {'a': purchase_name})
|
||||||
if c.fetchone() == (0,):
|
if c.fetchone() == (0,):
|
||||||
c.execute('''insert into purchase values(?,?,?,?,?)''',
|
c.execute('''insert into purchase values(?,?,?,?,?,?)''',
|
||||||
(purchase_name, price, orig_price, discount_from, discount_to))
|
(purchase_name, price, orig_price, discount_from, discount_to, discount_reason))
|
||||||
|
|
||||||
flash('购买项目添加成功 Successfully add the purchase.')
|
flash('购买项目添加成功 Successfully add the purchase.')
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ def get_all_purchase():
|
|||||||
|
|
||||||
discount_from = None
|
discount_from = None
|
||||||
discount_to = None
|
discount_to = None
|
||||||
|
discount_reason = 'Yes' if i[5] == 'anni5tix' else 'No'
|
||||||
|
|
||||||
if i[3] and i[3] >= 0:
|
if i[3] and i[3] >= 0:
|
||||||
discount_from = time.strftime(
|
discount_from = time.strftime(
|
||||||
@@ -302,13 +303,15 @@ def get_all_purchase():
|
|||||||
items = []
|
items = []
|
||||||
if y:
|
if y:
|
||||||
for j in y:
|
for j in y:
|
||||||
items.append({'item_id': j[1], 'type': j[2], 'amount':j[3]})
|
items.append(
|
||||||
|
{'item_id': j[1], 'type': j[2], 'amount': j[3]})
|
||||||
|
|
||||||
re.append({'purchase_name': i[0],
|
re.append({'purchase_name': i[0],
|
||||||
'price': i[1],
|
'price': i[1],
|
||||||
'orig_price': i[2],
|
'orig_price': i[2],
|
||||||
'discount_from': discount_from,
|
'discount_from': discount_from,
|
||||||
'discount_to': discount_to,
|
'discount_to': discount_to,
|
||||||
|
'discount_reason': discount_reason,
|
||||||
'items': items
|
'items': items
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user