23 Commits
v2.4 ... v2.7.0

Author SHA1 Message Date
Lost-MSth
d0be49fe20 Update to v2.7.0 2021-11-14 21:35:34 +08:00
Lost-MSth
b494b93c14 Update to v2.6.6 without release
- Fix a bug about purchase discount
- Fix a bug about database synchronization, which may make 'api_login' table empty
- For Arcaea 3.8.8
- Update a logout api
2021-10-22 20:00:22 +08:00
Lost-MSth
a523ff5aae Update README.md
Add some download links.

close #28
2021-10-01 16:24:56 +08:00
Lost-MSth
acbe9fc4a1 Merge pull request #31 from superDXZ/master
Try to add some download links.
2021-10-01 16:19:53 +08:00
superDXZ
9422bf7182 Update README.md
Found another download site
2021-10-01 02:49:06 +08:00
superDXZ
53acad4ffb Update README.md
Found another download site
2021-10-01 02:46:39 +08:00
Lost-MSth
13048e57f4 Update to v2.6.5 without release
- Change something to make that purchase things can have some cores.
- Fix the download link of Arcaea client in README

> This is a small update.
2021-09-30 19:02:10 +08:00
Lost-MSth
39d3f073ba Update to v2.6.4 without release 2021-09-08 17:35:10 +08:00
Lost-MSth
2cf6595478 Update to v2.6.3 2021-08-25 14:31:06 +08:00
Lost-MSth
7c3bc99570 Update to v2.6.2 2021-08-11 18:45:32 +08:00
Lost-MSth
168e5f0b12 Update to v2.6.1 2021-07-21 18:14:26 +08:00
Lost-MSth
9286caf900 Update to v2.6 2021-07-01 18:46:16 +08:00
Lost-MSth
b0647bedb6 Merge pull request #24 from MBRjun/patch-1
Update run.bat

To deal with the problem when you run *run.bat* with Administrator power and in another path.
2021-06-20 22:07:52 +08:00
MBRjun
f49a868707 Update run.bat 2021-06-20 22:04:41 +08:00
MBRjun
28bca25092 Update run.bat
cd %~dp0 用于cd到当前目录
使用管理员权限或Administrator账户运行脚本默认路径为%windir%\system32
2021-06-20 21:47:02 +08:00
Lost-MSth
0227d2b44a Merge pull request #22 from Menci/sort_friend_list
Sort friend list
2021-06-17 13:03:54 +08:00
Lost-MSth
0d95b886bc Merge pull request #23 from Menci/use_b10_as_r10
Add USE_B10_AS_R10 option
2021-06-17 12:53:33 +08:00
Menci
9b61acd163 Sort friend list 2021-06-17 03:04:39 +08:00
Menci
be134bbc6e Add USE_B10_AS_R10 option 2021-06-16 19:02:04 +00:00
Lost-MSth
c8d0463b9e Fix a small bug
Fix a bug about double slashes in URL, which will influence character uncap, uncap override and level up with EXP cores when you use a special game API prefix, like '/latte' or '/latte/1'.
It means that the length of game API prefix is not 2 in the end (not '/*** . . . **/**') and the bug will happen.
2021-06-14 18:04:16 +08:00
Lost-MSth
871c2082a8 Fix two small bugs
1. Rank list numbers in my world rank may disappear.
2.  Characters in rank list may be locked to uncapping state.
2021-06-10 22:27:29 +08:00
Lost-MSth
14b685850c Update README.md 2021-06-10 20:12:32 +08:00
Lost-MSth
3fbf473de2 Ready to update to v2.5
Please wait for some time. I need to change readme. I almost forgot what I changed before.
WARNING: There will be many bugs!
2021-06-10 19:01:26 +08:00
40 changed files with 4773 additions and 1872 deletions

View File

@@ -19,6 +19,7 @@ This procedure is mainly used for study and research, and shall not be used for
- 多设备登录 Multi device login - 多设备登录 Multi device login
- 成绩上传 Score upload - 成绩上传 Score upload
- PTT - PTT
- 世界排名 Global rank
- 排名 Rank - 排名 Rank
- 好友系统 Friends - 好友系统 Friends
- 数据同步 Data synchronization - 数据同步 Data synchronization
@@ -28,9 +29,7 @@ This procedure is mainly used for study and research, and shall not be used for
- 单曲和曲包购买(没啥用) Single songs and song packs purchase(useless) - 单曲和曲包购买(没啥用) Single songs and song packs purchase(useless)
- 奖励系统 Present system - 奖励系统 Present system
- 兑换码系统 Redeem code system - 兑换码系统 Redeem code system
- 角色立绘 All character drawings - 角色系统 Character system
- 角色技能 Character skills
- 自定义角色属性 Customizable characters attributes
- 全剧情解锁 Unlock all the stories - 全剧情解锁 Unlock all the stories
- 后台查询 Background search - 后台查询 Background search
- 后台自定义信息 Customize some things in the background - 后台自定义信息 Customize some things in the background
@@ -40,8 +39,6 @@ This procedure is mainly used for study and research, and shall not be used for
没有以下 We don't have 没有以下 We don't have
- 服务器安全性保证 Server security assurance - 服务器安全性保证 Server security assurance
- 世界模式下的搭档升级、觉醒,以及某些奖励的获取 Partner upgrading, uncapping, and receiving some rewards in the world mode
- 世界排名 Global rank
可能有问题 There may be problems 可能有问题 There may be problems
- Recent 30 - Recent 30
@@ -56,7 +53,9 @@ It is just so interesting. What it can do is under exploration.
## 下载 Download ## 下载 Download
[这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases) [这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases)
[Arcaea](https://konmai.cn/#arcaea) [Arcaea-CN official](https://arcaea.lowiro.com/zh)
[Arcaea-lowi.ro](https://lowi.ro)
[Arcaea-RhyDown](https://rhydown.com)
## 更新日志 Update log ## 更新日志 Update log
只保留最新版本 Only keep the latest version. 只保留最新版本 Only keep the latest version.
@@ -65,17 +64,25 @@ 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.4 ### Version 2.7.0
- 适用于Arcaea 3.6.0版本 For Arcaea 3.6.0 - 适用于Arcaea 3.9.0版本 For Arcaea 3.9.0
- 更新了歌曲数据库 Update the song database. - 更新了歌曲数据库 Update the song database.
- 新搭档 **拉格兰(拉格朗日!** 已解锁 Unlock the new character **Lagrange**. - 新搭档 **爱丽丝 & 坦尼尔(小步舞曲** 已解锁 Unlock the new character **Alice & Tenniel (Minuet)**.
-增游戏版本校验 Add game version verification. -搭档 **对立(挽歌)** 已解锁 Unlock the new character **Tairitsu (Elegy)**.
- 新增注册时邮箱记录 Add email recording while registering. - 搭档 **爱托** 已觉醒 Uncap the character **Eto**.
- 新增登录时IP地址记录 Add IP address recording at login time. - 搭档 **露娜** 已觉醒 Uncap the character **Luna**.
- 新增了一些设置选项和后台功能 Add some setting options and background functions. - 新增爱托和露娜的觉醒技能(可能有问题) Add the uncapped skills of Eto and Luna (There may be bugs.).
- 新增数据库自动更新功能 Add database automatic update function. - 新增是否同步角色表的设置选项 Add the setting option of whether updating the character table.
- 正在构建API接口 Building API interface.
- 修复了一些Bug Fix some bugs. - 以下是累积更新 The following are cumulative updates:
- 添加购买物品含有以太之滴的支持 Add support for purchasing items containing generic cores.
- 修复关于购买物品打折的问题 Fix a bug about purchase discount.
- 修复关于数据库同步的问题这可能导致api_login表为空 Fix a bug about database synchronization, which may make 'api_login' table empty.
- 新增登出API接口 Update a logout API.
> 提示:某些数据可能尚不完整,某个单曲的解锁和某两个角色的数据可能会有问题
> Tips: Some data may not be complete, so there may be problems with the unlocking of a single song and the data of two characters.
## 运行环境与依赖 Running environment and requirements ## 运行环境与依赖 Running environment and requirements
- Windows/Linux/Mac OS/Android - Windows/Linux/Mac OS/Android
@@ -87,11 +94,11 @@ It is just so interesting. What it can do is under exploration.
## 环境搭建 Environment construction ## 环境搭建 Environment construction
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA) [中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA)
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Environment-construction) [English](https://github.com/Lost-MSth/Arcaea-server/wiki/Environment-construction)
-->
## 使用说明 Instruction for use ## 使用说明 Instruction for use
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E) [中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Instruction-for-use) [English](https://github.com/Lost-MSth/Arcaea-server/wiki/Instruction-for-use)
-->
## 注意 Attentions ## 注意 Attentions
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E6%B3%A8%E6%84%8F) [中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E6%B3%A8%E6%84%8F)

View File

@@ -1,5 +1,7 @@
import hashlib import hashlib
import base64
import time import time
import random
from server.sql import Connect from server.sql import Connect
import functools import functools
from setting import Config from setting import Config
@@ -14,14 +16,50 @@ class User():
self.power = power self.power = power
def login(): def login(auth: str, ip: str):
# 登录接口 # 登录接口,返回字典和错误码
return {'token': 1}, 0
try:
auth_decode = bytes.decode(base64.b64decode(auth))
except:
return {}, -100
if not ':' in auth_decode:
return {}, -100
name, password = auth_decode.split(':', 1)
with Connect() as c:
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
c.execute('''select user_id, password from user where name = :name''', {
'name': name})
x = c.fetchone()
if x is None:
return {}, -201
if x[1] == '':
return {}, -202
if hash_pwd != x[1]:
return {}, -201
user_id = str(x[0])
now = int(time.time() * 1000)
token = hashlib.sha256(
(user_id + str(random.randint(10000, 99999)) + str(now)).encode("utf8")).hexdigest()
c.execute('''delete from api_login where user_id=?''', (user_id,))
c.execute('''insert into api_login values(?,?,?,?)''',
(user_id, token, now, ip))
return {'token': token, 'user_id': user_id}, 0
def logout(): def logout(user: User):
# 登出接口 # 登出接口,返回错误码
pass code = 999
with Connect() as c:
c.execute('''delete from api_login where user_id=?''', (user.user_id,))
code = 0
return code
def id_get_role_id(c, user_id): def id_get_role_id(c, user_id):
@@ -86,20 +124,19 @@ def role_required(request, power=[]):
return jsonify({'status': 400, 'code': -1, 'data': {}, 'msg': 'Payload must be a valid json'}) return jsonify({'status': 400, 'code': -1, 'data': {}, 'msg': 'Payload must be a valid json'})
if not 'Token' in request.headers: if not 'Token' in request.headers:
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No Token'}) return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No token'})
user = User() user = User()
if power == []: if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '': user.user_id = 0
user.user_id = 0 elif power == []:
else: return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
else: else:
with Connect() as c: with Connect() as c:
user.user_id = api_token_get_id( user.user_id = api_token_get_id(
c, request.headers['Token']) c, request.headers['Token'])
if user.user_id is None: if user.user_id is None:
return jsonify({'status': 404, 'code': -1, 'data': {}, 'msg': ''}) return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No token'})
role_id = id_get_role_id(c, user.user_id) role_id = id_get_role_id(c, user.user_id)
user.role = role_id_get_role(c, role_id) user.role = role_id_get_role(c, role_id)

View File

@@ -6,10 +6,14 @@ def code_get_msg(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',
'-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',
'-202': 'User is banned',
'-999': 'Unknown error'
} }
return msg[str(code)] return msg[str(code)]

View File

@@ -1,6 +1,7 @@
from flask import ( from flask import (
Blueprint, request, jsonify Blueprint, request, jsonify
) )
import functools
import api.api_auth import api.api_auth
import api.users import api.users
import api.songs import api.songs
@@ -10,93 +11,112 @@ from api.api_code import code_get_msg
bp = Blueprint('api', __name__, url_prefix='/api/v1') bp = Blueprint('api', __name__, url_prefix='/api/v1')
class Query():
# 查询类,当查询附加参数的数据类型用
def __init__(self, limit=-1, offset=0, query={}, sort=[]) -> None:
self.limit = limit
self.offset = offset
self.query = query # {'name': 'admin'}
self.sort = sort # [{'column': 'user_id', 'order': 'ASC'}, ...]
def get_query_parameter(request, query_able=[], sort_able=[]): def get_query_parameter(request, query_able=[], sort_able=[]):
# 提取查询请求参数,返回四个参数和code # 提取查询请求参数,返回Query类查询参数写成修饰器
limit = -1 def decorator(view):
offset = 0 @functools.wraps(view)
query = {} # {'name': 'admin'} def wrapped_view(*args, **kwargs):
sort = [] # [{'column': 'user_id', 'order': 'ASC'}, ...]
if 'limit' in request.json: re = Query()
try:
limit = int(request.json['limit'])
except:
return -1, 0, {}, {}, -101
if 'offset' in request.json:
try:
offset = int(request.json['offset'])
except:
return -1, 0, {}, {}, -101
if 'query' in request.json:
query = request.json['query']
for i in query:
if i not in query_able:
return -1, 0, {}, {}, -102
if 'sort' in request.json:
sort = request.json['sort']
for i in sort:
if 'column' not in i or i['column'] not in sort_able:
return -1, 0, {}, {}, -103
if not 'order' in i:
i['order'] = 'ASC'
else:
if i['order'] not in ['ASC', 'DESC']:
return -1, 0, {}, {}, -104
return limit, offset, query, sort, 0 if 'limit' in request.json:
try:
re.limit = int(request.json['limit'])
except:
return jsonify({'status': 200, 'code': -101, 'data': {}, 'msg': code_get_msg(-101)})
if 'offset' in request.json:
try:
re.offset = int(request.json['offset'])
except:
return jsonify({'status': 200, 'code': -101, 'data': {}, 'msg': code_get_msg(-101)})
if 'query' in request.json:
re.query = request.json['query']
for i in re.query:
if i not in query_able:
return jsonify({'status': 200, 'code': -102, 'data': {}, 'msg': code_get_msg(-102)})
if 'sort' in request.json:
re.sort = request.json['sort']
for i in re.sort:
if 'column' not in i or i['column'] not in sort_able:
return jsonify({'status': 200, 'code': -103, 'data': {}, 'msg': code_get_msg(-103)})
if not 'order' in i:
i['order'] = 'ASC'
else:
if i['order'] not in ['ASC', 'DESC']:
return jsonify({'status': 200, 'code': -104, 'data': {}, 'msg': code_get_msg(-104)})
return view(re, *args, **kwargs)
return wrapped_view
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 jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''}) return return_encode()
@bp.route('/token', methods=['POST']) @bp.route('/token', methods=['POST'])
def token_post(): def token_post():
# 登录获取token # 登录获取token
# {'auth': `base64(user_id:password)`}
if 'auth' in request.json: if 'auth' in request.json:
data, code = api.api_auth.login( data, code = api.api_auth.login(
request.json['auth'], request.remote_addr) str(request.json['auth']), request.remote_addr)
if code < 0: return return_encode(code, data)
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
else:
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
else: else:
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No authentication'}) return return_encode(-1, {}, 401, 'No authentication')
@bp.route('/token', methods=['GET']) @bp.route('/token', methods=['GET'])
@api.api_auth.role_required(request, ['select_me', 'select']) @api.api_auth.role_required(request, ['select_me', 'select'])
def token_get(user): def token_get(user):
# 判断登录有效性 # 判断登录有效性
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''}) return return_encode()
@bp.route('/token', methods=['DELETE']) @bp.route('/token', methods=['DELETE'])
@api.api_auth.role_required(request, ['change_me', 'select_me', 'select']) @api.api_auth.role_required(request, ['change_me', 'select_me', 'select'])
def token_delete(user): def token_delete(user):
# 登出 # 登出
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''}) return return_encode(api.api_auth.logout(user))
@bp.route('/users', methods=['GET']) @bp.route('/users', methods=['GET'])
@api.api_auth.role_required(request, ['select']) @api.api_auth.role_required(request, ['select'])
def users_get(user): @get_query_parameter(request, ['user_id', 'name', 'user_code'], [
'user_id', 'name', 'user_code', 'join_date', 'rating_ptt', 'time_played', 'ticket', 'world_rank_score'])
def users_get(query, user):
# 查询全用户信息 # 查询全用户信息
limit, offset, query, sort, code = get_query_parameter(request, ['user_id', 'name', 'user_code'], [ data = api.users.get_users(query)
'user_id', 'name', 'user_code', 'join_date', 'rating_ptt', 'time_played', 'ticket'])
if code < 0:
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
data = api.users.get_users(limit, offset, query, sort)
if not data: if not data:
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)}) return return_encode(-2)
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''}) return return_encode(0, data)
@bp.route('/users/<int:user_id>', methods=['GET']) @bp.route('/users/<int:user_id>', methods=['GET'])
@@ -108,17 +128,17 @@ def users_user_get(user, user_id):
user_id = user.user_id user_id = user.user_id
if user_id <= 0: if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)}) return return_encode(-4)
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 jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'}) return return_encode(-1, {}, 403, 'No permission')
data = api.users.get_user_info(user_id) data = api.users.get_user_info(user_id)
if not data: if not data:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)}) return return_encode(-3)
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''}) return return_encode(0, data)
@bp.route('/users/<int:user_id>/b30', methods=['GET']) @bp.route('/users/<int:user_id>/b30', methods=['GET'])
@@ -126,48 +146,39 @@ def users_user_get(user, user_id):
def users_user_b30_get(user, user_id): def users_user_b30_get(user, user_id):
# 查询用户b30 # 查询用户b30
if user_id == 'me':
user_id = user.user_id
if user_id <= 0: if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)}) return return_encode(-4)
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 jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'}) return return_encode(-1, {}, 403, 'No permission')
data = api.users.get_user_b30(user_id) data = api.users.get_user_b30(user_id)
if data['data'] == []: if data['data'] == []:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)}) return return_encode(-3)
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''}) return return_encode(0, data)
@bp.route('/users/<int:user_id>/best', methods=['GET']) @bp.route('/users/<int:user_id>/best', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_me']) @api.api_auth.role_required(request, ['select', 'select_me'])
def users_user_best_get(user, user_id): @get_query_parameter(request, ['song_id', 'difficulty'], [
'song_id', 'difficulty', 'score', 'time_played', 'rating'])
def users_user_best_get(query, user, user_id):
# 查询用户所有best成绩 # 查询用户所有best成绩
if user_id == 'me':
user_id = user.user_id
if user_id <= 0: if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)}) return return_encode(-4)
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 jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'}) return return_encode(-1, {}, 403, 'No permission')
limit, offset, query, sort, code = get_query_parameter(request, ['song_id', 'difficulty'], [ data = api.users.get_user_best(user_id, query)
'song_id', 'difficulty', 'score', 'time_played', 'rating'])
if code < 0:
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
data = api.users.get_user_best(user_id, limit, offset, query, sort)
if data['data'] == []: if data['data'] == []:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)}) return return_encode(-3)
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''}) return return_encode(0, data)
@bp.route('/users/<int:user_id>/r30', methods=['GET']) @bp.route('/users/<int:user_id>/r30', methods=['GET'])
@@ -175,21 +186,18 @@ def users_user_best_get(user, user_id):
def users_user_r30_get(user, user_id): def users_user_r30_get(user, user_id):
# 查询用户r30 # 查询用户r30
if user_id == 'me':
user_id = user.user_id
if user_id <= 0: if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)}) return return_encode(-4)
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 jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'}) return return_encode(-1, {}, 403, 'No permission')
data = api.users.get_user_r30(user_id) data = api.users.get_user_r30(user_id)
if data['data'] == []: if data['data'] == []:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)}) return return_encode(-3)
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''}) return return_encode(0, data)
@bp.route('/songs/<string:song_id>', methods=['GET']) @bp.route('/songs/<string:song_id>', methods=['GET'])
@@ -200,24 +208,21 @@ def songs_song_get(user, song_id):
data = api.songs.get_song_info(song_id) data = api.songs.get_song_info(song_id)
if not data: if not data:
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)}) return return_encode(-2)
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''}) return return_encode(0, data)
@bp.route('/songs', methods=['GET']) @bp.route('/songs', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_song_info']) @api.api_auth.role_required(request, ['select', 'select_song_info'])
def songs_get(user): @get_query_parameter(request, ['sid', 'name_en', 'name_jp', 'pakset', 'artist'], [
'sid', 'name_en', 'name_jp', 'pakset', 'artist', 'date', 'rating_pst', 'rating_prs', 'rating_ftr', 'rating_byn'])
def songs_get(query, user):
# 查询全歌曲信息 # 查询全歌曲信息
limit, offset, query, sort, code = get_query_parameter(request, ['sid', 'name_en', 'name_jp', 'pakset', 'artist'], [ data = api.songs.get_songs(query)
'sid', 'name_en', 'name_jp', 'pakset', 'artist', 'date', 'rating_pst', 'rating_prs', 'rating_ftr', 'rating_byn'])
if code < 0:
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
data = api.songs.get_songs(limit, offset, query, sort)
if not data: if not data:
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)}) return return_encode(-2)
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''}) return return_encode(0, data)

View File

@@ -17,25 +17,25 @@ def get_song_info(song_id):
'pakset': x[5], 'pakset': x[5],
'artist': x[6], 'artist': x[6],
'date': x[9], 'date': x[9],
'rating_pst': x[12]/10, 'rating_pst': x[13]/10,
'rating_prs': x[13]/10, 'rating_prs': x[14]/10,
'rating_ftr': x[14]/10, 'rating_ftr': x[15]/10,
'rating_byn': x[15]/10, 'rating_byn': x[16]/10,
'difficultly_pst': x[16]/2, 'difficultly_pst': x[17]/2,
'difficultly_prs': x[17]/2, 'difficultly_prs': x[18]/2,
'difficultly_ftr': x[18]/2, 'difficultly_ftr': x[19]/2,
'difficultly_byn': x[19]/2 'difficultly_byn': x[20]/2
} }
return r return r
def get_songs(limit=-1, offset=0, query={}, sort=[]): def get_songs(query=None):
# 查询全部歌曲信息,返回字典列表 # 查询全部歌曲信息,返回字典列表
r = [] r = []
with Connect('./database/arcsong.db') as c: with Connect('./database/arcsong.db') as c:
x = Sql.select(c, 'songs', [], limit, offset, query, sort) x = Sql.select(c, 'songs', [], query)
if x: if x:
for i in x: for i in x:
@@ -45,14 +45,14 @@ def get_songs(limit=-1, offset=0, query={}, sort=[]):
'pakset': i[5], 'pakset': i[5],
'artist': i[6], 'artist': i[6],
'date': i[9], 'date': i[9],
'rating_pst': i[12]/10, 'rating_pst': i[13]/10,
'rating_prs': i[13]/10, 'rating_prs': i[14]/10,
'rating_ftr': i[14]/10, 'rating_ftr': i[15]/10,
'rating_byn': i[15]/10, 'rating_byn': i[16]/10,
'difficultly_pst': i[16]/2, 'difficultly_pst': i[17]/2,
'difficultly_prs': i[17]/2, 'difficultly_prs': i[18]/2,
'difficultly_ftr': i[18]/2, 'difficultly_ftr': i[19]/2,
'difficultly_byn': i[19]/2 'difficultly_byn': i[20]/2
}) })
return r return r

View File

@@ -5,12 +5,12 @@ import web.webscore
import server.info import server.info
def get_users(limit=-1, offset=0, query={}, sort=[]): def get_users(query=None):
# 获取全用户信息,返回字典列表 # 获取全用户信息,返回字典列表
r = [] r = []
with Connect() as c: with Connect() as c:
x = Sql.select(c, 'user', [], limit, offset, query, sort) x = Sql.select(c, 'user', [], query)
if x: if x:
for i in x: for i in x:
@@ -62,12 +62,12 @@ def get_user_b30(user_id):
return {'user_id': user_id, 'b30_ptt': bestptt / 30, 'data': r} return {'user_id': user_id, 'b30_ptt': bestptt / 30, 'data': r}
def get_user_best(user_id, limit=-1, offset=0, query={}, sort=[]): def get_user_best(user_id, query=None):
# 获取用户b30信息返回字典 # 获取用户b30信息返回字典
r = [] r = []
with Connect() as c: with Connect() as c:
x = Sql.select(c, 'best_score', [], limit, offset, query, sort) x = Sql.select(c, 'best_score', [], query)
if x: if x:
for i in x: for i in x:
r.append({ r.append({

Binary file not shown.

View File

@@ -1,11 +1,10 @@
import sqlite3 import sqlite3
import hashlib
import time import time
import json import json
# 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用 # 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用
ARCAEA_SERVER_VERSION = 'v2.4.0' ARCAEA_SERVER_VERSION = 'v2.7.0'
def main(path='./'): def main(path='./'):
@@ -44,7 +43,12 @@ def main(path='./'):
current_map text, current_map text,
ticket int, ticket int,
prog_boost int, prog_boost int,
email text email text,
world_rank_score int,
ban_flag text,
next_fragstam_ts int,
max_stamina_ts int,
stamina int
);''') );''')
c.execute('''create table if not exists login(access_token text, c.execute('''create table if not exists login(access_token text,
user_id int, user_id int,
@@ -77,35 +81,42 @@ def main(path='./'):
character_id int, character_id int,
level int, level int,
exp real, exp real,
level_exp real, is_uncapped int,
frag real, is_uncapped_override int,
prog real, primary key(user_id, character_id)
overdrive real, );''')
skill_id text, c.execute('''create table if not exists user_char_full(user_id int,
skill_unlock_level int, character_id int,
skill_requires_uncap int, level int,
skill_id_uncap text, exp real,
char_type int,
is_uncapped int, is_uncapped int,
is_uncapped_override int, is_uncapped_override int,
primary key(user_id, character_id) primary key(user_id, character_id)
);''') );''')
c.execute('''create table if not exists character(character_id int primary key, c.execute('''create table if not exists character(character_id int primary key,
name text, name text,
level int, max_level int,
exp real, frag1 real,
level_exp real, prog1 real,
frag real, overdrive1 real,
prog real, frag20 real,
overdrive real, prog20 real,
overdrive20 real,
frag30 real,
prog30 real,
overdrive30 real,
skill_id text, skill_id text,
skill_unlock_level int, skill_unlock_level int,
skill_requires_uncap int, skill_requires_uncap int,
skill_id_uncap text, skill_id_uncap text,
char_type int, char_type int,
uncap_cores text, is_uncapped int
is_uncapped int, );''')
is_uncapped_override int c.execute('''create table if not exists char_item(character_id int,
item_id text,
type text,
amount int,
primary key(character_id, item_id, type)
);''') );''')
c.execute('''create table if not exists recent30(user_id int primary key, c.execute('''create table if not exists recent30(user_id int primary key,
r0 real, r0 real,
@@ -199,18 +210,27 @@ def main(path='./'):
c.execute('''create table if not exists item(item_id text, c.execute('''create table if not exists item(item_id text,
type text, type text,
is_available int, is_available int,
price int,
orig_price int,
discount_from int,
discount_to int,
_id text, _id text,
primary key(item_id, type) primary key(item_id, type)
);''') );''')
c.execute('''create table if not exists user_item(user_id int, c.execute('''create table if not exists user_item(user_id int,
item_id text, item_id text,
type text, type text,
amount int,
primary key(user_id, item_id, type) primary key(user_id, item_id, type)
);''') );''')
c.execute('''create table if not exists purchase(purchase_name text primary key,
price int,
orig_price int,
discount_from int,
discount_to int
);''')
c.execute('''create table if not exists purchase_item(purchase_name text,
item_id text,
type text,
amount int,
primary key(purchase_name, item_id, type)
);''')
c.execute('''create table if not exists user_save(user_id int primary key, c.execute('''create table if not exists user_save(user_id int primary key,
scores_data text, scores_data text,
clearlamps_data text, clearlamps_data text,
@@ -223,26 +243,36 @@ def main(path='./'):
);''') );''')
c.execute('''create table if not exists present(present_id text primary key, c.execute('''create table if not exists present(present_id text primary key,
expire_ts int, expire_ts int,
items text,
description text description text
);''') );''')
c.execute('''create table if not exists user_present(user_id int, c.execute('''create table if not exists user_present(user_id int,
present_id text, present_id text,
primary key(user_id, present_id) primary key(user_id, present_id)
);''') );''')
c.execute('''create table if not exists present_item(present_id text,
item_id text,
type text,
amount int,
primary key(present_id, item_id, type)
);''')
c.execute('''create table if not exists songfile(song_id text, c.execute('''create table if not exists songfile(song_id text,
file_type int, file_type int,
md5 text, md5 text,
primary key(song_id, file_type) primary key(song_id, file_type)
);''') );''')
c.execute('''create table if not exists redeem(code text primary key, c.execute('''create table if not exists redeem(code text primary key,
items text,
type int type int
);''') );''')
c.execute('''create table if not exists user_redeem(user_id int, c.execute('''create table if not exists user_redeem(user_id int,
code text, code text,
primary key(user_id, code) primary key(user_id, code)
);''') );''')
c.execute('''create table if not exists redeem_item(code text,
item_id text,
type text,
amount int,
primary key(code, item_id, type)
);''')
c.execute('''create table if not exists role(role_id int primary key, c.execute('''create table if not exists role(role_id int primary key,
role_name text, role_name text,
@@ -268,52 +298,103 @@ def main(path='./'):
);''') );''')
# 搭档初始化 # 搭档初始化
char = ['Hikari', 'Tairitsu', 'Kou', 'Sapphire', 'Lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'Stella', 'Hikari & Fisica', 'Ilith', 'Eto', 'Luna', 'Shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin', char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'stella', 'Hikari & Fisica', 'ilith', 'eto', 'luna', 'shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
'Ayu', 'Eto & Luna', 'Yume', 'Seine & Hikari', 'Saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'Haruna', 'Nono', 'MTA-XXX', 'MDA-21', 'Kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'Sia', 'DORO*C', 'Tairitsu(Tempest)', 'Brillante', 'Ilith(Summer)', 'Etude', 'Alice & Tenniel', 'Luna & Mia', 'Areus', 'Seele', 'Isabelle', 'Mir', 'Lagrange'] 'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)']
skill_id = ['gauge_easy', '', '', '', 'note_mirror', '', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere', skill_id = ['gauge_easy', '', '', '', 'note_mirror', '', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange'] 'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '']
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee', skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''] '', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
frag = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 80, 90, 92, 0, 61, 67, 92, 85, 50, 86, 62, skill_unlock_level = [0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0,
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] 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]
prog = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 67, 63, 78, 0, 99, 80, 66, 46, 83, 40, 83, frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80] 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, 55, 55]
overdrive = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 59, 90, 68, 0, 78, 50, 70, 62, 49, 64, prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 90, 80] 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, 55, 55]
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, 55, 55]
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, 80, 80]
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, 80, 80]
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, 80, 80]
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, 80, 80]
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 83, 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, 80, 80]
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 49, 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, 80, 80]
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, 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]
# print(len(char)) char_core = {
# print(len(skill_id)) 0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
# print(len(skill_id_uncap)) 1: [{'core_id': 'core_hollow', 'amount': 5}, {'core_id': 'core_desolate', 'amount': 25}],
# print(len(frag)) 2: [{'core_id': 'core_hollow', 'amount': 5}, {'core_id': 'core_crimson', 'amount': 25}],
# print(len(prog)) 4: [{'core_id': 'core_ambivalent', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
# print(len(overdrive)) 13: [{'core_id': 'core_scarlet', 'amount': 30}],
# print(len(char_type)) 21: [{'core_id': 'core_scarlet', 'amount': 30}],
26: [{'core_id': 'core_chunithm', 'amount': 15}],
27: [{'core_id': 'core_chunithm', 'amount': 15}],
28: [{'core_id': 'core_chunithm', 'amount': 15}],
29: [{'core_id': 'core_chunithm', 'amount': 15}],
36: [{'core_id': 'core_chunithm', 'amount': 15}],
42: [{'core_id': 'core_chunithm', 'amount': 15}],
43: [{'core_id': 'core_chunithm', 'amount': 15}],
11: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_hollow', 'amount': 5}],
12: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}]
}
for i in range(0, 46): for i in range(0, 53):
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43]: skill_requires_uncap = 1 if i == 2 else 0
sql = '''insert into character values(:a,:b,30,25000,25000,:c,:d,:e,:f,0,0,:g,:h,'',1,1)'''
c.execute(sql, {'a': i, 'b': char[i], 'c': frag[i], 'd': prog[i], if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43, 11, 12]:
'e': overdrive[i], 'f': skill_id[i], 'g': skill_id_uncap[i], 'h': char_type[i]}) sql = '''insert into character values(?,?,30,?,?,?,?,?,?,?,?,?,?,?,?,?,?,1)'''
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]))
else: else:
if i != 5: if i != 5 and i != 46:
sql = '''insert into character values(:a,:b,20,10000,10000,:c,:d,:e,:f,0,0,:g,:h,'',0,0)''' sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
c.execute(sql, {'a': i, 'b': char[i], 'c': frag[i], 'd': prog[i], c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
'e': overdrive[i], 'f': skill_id[i], 'g': skill_id_uncap[i], 'h': 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]))
def b2int(x): for i in char_core:
# int与布尔值转换 for j in char_core[i]:
if x: c.execute('''insert into char_item values(?,?,'core',?)''',
return 1 (i, j['core_id'], j['amount']))
else: cores = ['core_hollow', 'core_desolate', 'core_chunithm', 'core_crimson',
return 0 'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary']
for i in cores:
c.execute('''insert into item values(?,"core",1,'')''', (i,))
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3"]
for i in world_songs:
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
world_unlocks = ["scenery_chap1", "scenery_chap2",
"scenery_chap3", "scenery_chap4", "scenery_chap5"]
for i in world_unlocks:
c.execute('''insert into item values(?,"world_unlock",1,'')''', (i,))
c.execute('''insert into item values(?,?,?,?)''',
('fragment', 'fragment', 1, ''))
c.execute('''insert into item values(?,?,?,?)''',
('memory', 'memory', 1, ''))
def insert_items(c, items): def insert_items(c, items):
# 物品数据导入 # 物品数据导入
@@ -326,14 +407,24 @@ 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(?,?,?,?,?)''',
(i['name'], i['price'], i['orig_price'], discount_from, discount_to))
for j in i['items']: for j in i['items']:
if "_id" not in j: if "_id" not in j:
_id = '' _id = ''
else: else:
_id = j['_id'] _id = j['_id']
if j['type'] != 'character': c.execute(
c.execute('''insert into item(item_id, type, is_available, price, orig_price, discount_from, discount_to, _id) values(:a,:b,:c,:d,:e,:f,:g,:h)''', { '''select exists(select * from item where item_id=?)''', (j['id'],))
'a': j['id'], 'b': j['type'], 'c': b2int(j['is_available']), 'd': i['price'], 'e': i['orig_price'], 'f': discount_from, 'g': discount_to, 'h': _id}) if c.fetchone() == (0,):
c.execute('''insert into item values(?,?,?,?)''',
(j['id'], j['type'], j['is_available'], _id))
if 'amount' in j:
amount = j['amount']
else:
amount = 1
c.execute('''insert into purchase_item values(?,?,?,?)''',
(i['name'], j['id'], j['type'], amount))
# item初始化 # item初始化
f = open(path+'singles.json', 'r') f = open(path+'singles.json', 'r')
@@ -374,36 +465,32 @@ def main(path='./'):
conn.commit() conn.commit()
conn.close() conn.close()
def arc_register(name: str, password: str): def arc_register(name: str):
def build_user_code(c): def build_user_code(c):
return '123456789' return '123456789'
def build_user_id(c): def build_user_id(c):
return 2000000 return 2000000
# def insert_user_char(c, user_id):
# for i in range(0, 38):
# if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
# sql = 'insert into user_char values('+str(user_id)+','+str(
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)'''
# c.execute(sql)
# else:
# if i != 5:
# sql = 'insert into user_char values('+str(user_id)+','+str(
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)'''
# c.execute(sql)
def insert_user_char(c, user_id): def insert_user_char(c, user_id):
# 为用户添加所有可用角色 # 为用户添加可用角色
c.execute('''select * from character''') c.execute('''insert into user_char values(?,?,?,?,?,?)''',
(user_id, 0, 30, 25000, 1, 0))
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
(user_id, 1, 30, 25000, 1, 0))
c.execute(
'''select character_id, max_level, is_uncapped from character''')
x = c.fetchall() x = c.fetchall()
if x != []: if x:
for i in x: for i in x:
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', { exp = 25000 if i[1] == 30 else 10000
'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]}) c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
(user_id, i[0], i[1], exp, i[2], 0))
conn = sqlite3.connect(path+'arcaea_database.db') conn = sqlite3.connect(path+'arcaea_database.db')
c = conn.cursor() c = conn.cursor()
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest() hash_pwd = '41e5653fc7aeb894026d6bb7b2db7f65902b454945fa8fd65a6327047b5277fb'
c.execute( c.execute(
'''select exists(select * from user where name = :name)''', {'name': name}) '''select exists(select * from user where name = :name)''', {'name': name})
if c.fetchone() == (0,): if c.fetchone() == (0,):
@@ -427,7 +514,7 @@ def main(path='./'):
conn.close() conn.close()
return None return None
arc_register('admin', 'admin123') arc_register('admin')
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -0,0 +1,376 @@
{
"map_id": "byd_bookmaker",
"is_legacy": false,
"chapter": 1001,
"available_from": -1,
"available_to": 9999999999999,
"is_repeatable": false,
"require_id": "bookmaker2",
"require_type": "chart_unlock",
"coordinate": "-650,-650",
"is_beyond": true,
"stamina_cost": 3,
"beyond_health": 300,
"character_affinity": [
23,
34,
47
],
"affinity_multiplier": [
1.3,
5,
3.8
],
"step_count": 31,
"custom_bg": "",
"curr_position": 3,
"curr_capture": 35.81730523472207,
"is_locked": false,
"steps": [
{
"map_id": "byd_bookmaker",
"position": 0,
"capture": 10
},
{
"map_id": "byd_bookmaker",
"position": 1,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 2,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 3,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
],
"restrict_ids": [
"bookmaker",
"viciousheroism",
"battlenoone",
"galaxyfriends",
"rekkaresonance"
],
"restrict_type": "song_id"
},
{
"map_id": "byd_bookmaker",
"position": 4,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 5,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 6,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 7,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 8,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 9,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 10,
"capture": 10,
"items": [
{
"type": "core",
"id": "core_generic",
"amount": 1
}
]
},
{
"map_id": "byd_bookmaker",
"position": 11,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 12,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 13,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 14,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 15,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 16,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 17,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 18,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 19,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 20,
"capture": 10,
"items": [
{
"type": "core",
"id": "core_generic",
"amount": 1
}
]
},
{
"map_id": "byd_bookmaker",
"position": 21,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 22,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 23,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 24,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 25,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 26,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 27,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 28,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 29,
"capture": 10,
"items": [
{
"type": "fragment",
"amount": 30
}
]
},
{
"map_id": "byd_bookmaker",
"position": 30,
"capture": 0,
"items": [
{
"id": "bookmaker3",
"type": "world_song"
}
]
}
]
}

View File

@@ -0,0 +1,114 @@
{
"map_id": "test3",
"is_legacy": true,
"is_beyond": false,
"beyond_health": 100,
"character_affinity": [],
"affinity_multiplier": [],
"chapter": 0,
"available_from": -1,
"available_to": 2106124800000,
"is_repeatable": false,
"require_id": "",
"require_type": "",
"require_value": 0,
"coordinate": "300,200",
"step_count": 12,
"custom_bg": "",
"stamina_cost": 1,
"curr_position": 0,
"curr_capture": 0,
"is_locked": false,
"steps": [{
"position": 0,
"capture": 10
}, {
"position": 1,
"capture": 10,
"items": [{
"type": "fragment",
"amount": 1000
}]
}, {
"position": 2,
"capture": 10,
"restrict_id": "base",
"restrict_type": "pack_id",
"items": [{
"type": "memory",
"amount": 150
}]
}, {
"position": 3,
"capture": 10,
"restrict_id": "base",
"restrict_type": "pack_id",
"step_type": ["randomsong"],
"items": [{
"id": "supernova",
"type": "world_song"
}]
}, {
"position": 4,
"capture": 10,
"items": [{
"type": "core",
"id": "core_generic",
"amount": 1
}]
}, {
"position": 5,
"capture": 10,
"step_type": ["speedlimit"],
"speed_limit_value": 60,
"items": [{
"id": "scenery_chap4",
"type": "world_unlock"
}]
}, {
"position": 6,
"capture": 10,
"items": [{
"type": "fragment",
"amount": 1250
}]
}, {
"position": 7,
"capture": 10,
"step_type": ["plusstamina"],
"plus_stamina_value": 2,
"items": [{
"type": "fragment",
"amount": 125
}]
}, {
"position": 8,
"capture": 10,
"items": [{
"type": "fragment",
"amount": 125
}]
}, {
"position": 9,
"capture": 10,
"items": [{
"type": "memory",
"amount": 150
}]
}, {
"position": 10,
"capture": 10,
"items": [{"id": "2", "type": "character"}]
}, {
"position": 11,
"capture": 0,
"items": [{
"type": "core",
"id": "core_crimson",
"amount": 50
}, {
"type": "fragment",
"amount": 125
}]
}]
}

View File

@@ -1,250 +1,515 @@
[{ [
"name": "core", {
"items": [{ "name": "core",
"type": "pack", "items": [
"id": "core", {
"is_available": true "type": "pack",
}], "id": "core",
"price": 500, "is_available": true
"orig_price": 500, },
"discount_from": 1615248000000, {
"discount_to": 1615852799000 "type": "core",
}, { "amount": 5,
"name": "shiawase", "id": "core_generic",
"items": [{ "is_available": true
"type": "pack", }
"id": "shiawase", ],
"is_available": true "price": 500,
}, { "orig_price": 500,
"type": "character", "discount_from": 1615248000000,
"id": "kou", "discount_to": 1615852799000
"is_available": true },
}], {
"price": 500, "name": "shiawase",
"orig_price": 500, "items": [
"discount_from": 1615248000000, {
"discount_to": 1615852799000 "type": "pack",
}, { "id": "shiawase",
"name": "dynamix", "is_available": true
"items": [{ },
"type": "pack", {
"id": "dynamix", "type": "character",
"is_available": true "id": "kou",
}, { "is_available": true
"type": "character", },
"id": "sapphire", {
"is_available": true "type": "core",
}], "amount": 5,
"price": 500, "id": "core_generic",
"orig_price": 500, "is_available": true
"discount_from": 1615248000000, }
"discount_to": 1615852799000 ],
}, { "price": 500,
"name": "mirai", "orig_price": 500,
"items": [{ "discount_from": 1615248000000,
"type": "pack", "discount_to": 1615852799000
"id": "mirai", },
"is_available": true {
}, { "name": "dynamix",
"type": "character", "items": [
"id": "lethe", {
"is_available": true "type": "pack",
}], "id": "dynamix",
"price": 500, "is_available": true
"orig_price": 500, },
"discount_from": 1615248000000, {
"discount_to": 1615852799000 "type": "character",
}, { "id": "sapphire",
"name": "yugamu", "is_available": true
"items": [{ },
"type": "pack", {
"id": "yugamu", "type": "core",
"is_available": true "amount": 5,
}], "id": "core_generic",
"price": 500, "is_available": true
"orig_price": 500, }
"discount_from": 1615248000000, ],
"discount_to": 1615852799000 "price": 500,
}, { "orig_price": 500,
"name": "lanota", "discount_from": 1615248000000,
"items": [{ "discount_to": 1615852799000
"type": "pack", },
"id": "lanota", {
"is_available": true "name": "mirai",
}], "items": [
"price": 500, {
"orig_price": 500, "type": "pack",
"discount_from": 1615248000000, "id": "mirai",
"discount_to": 1615852799000 "is_available": true
}, { },
"name": "nijuusei", {
"items": [{ "type": "character",
"type": "pack", "id": "lethe",
"id": "nijuusei", "is_available": true
"is_available": true },
}], {
"price": 500, "type": "core",
"orig_price": 500, "amount": 5,
"discount_from": 1615248000000, "id": "core_generic",
"discount_to": 1615852799000 "is_available": true
}, { }
"name": "rei", ],
"items": [{ "price": 500,
"type": "pack", "orig_price": 500,
"id": "rei", "discount_from": 1615248000000,
"is_available": true "discount_to": 1615852799000
}], },
"price": 500, {
"orig_price": 500, "name": "yugamu",
"discount_from": 1615248000000, "items": [
"discount_to": 1615852799000 {
}, { "type": "pack",
"name": "tonesphere", "id": "yugamu",
"items": [{ "is_available": true
"type": "pack", },
"id": "tonesphere", {
"is_available": true "type": "core",
}], "amount": 5,
"price": 500, "id": "core_generic",
"orig_price": 500, "is_available": true
"discount_from": 1615248000000, }
"discount_to": 1615852799000 ],
}, { "price": 500,
"name": "groovecoaster", "orig_price": 500,
"items": [{ "discount_from": 1615248000000,
"type": "pack", "discount_to": 1615852799000
"id": "groovecoaster", },
"is_available": true {
}], "name": "lanota",
"price": 500, "items": [
"orig_price": 500, {
"discount_from": 1615248000000, "type": "pack",
"discount_to": 1615852799000 "id": "lanota",
}, { "is_available": true
"name": "zettai", },
"items": [{ {
"type": "pack", "type": "core",
"id": "zettai", "amount": 5,
"is_available": true "id": "core_generic",
}], "is_available": true
"price": 500, }
"orig_price": 500, ],
"discount_from": 1615248000000, "price": 500,
"discount_to": 1615852799000 "orig_price": 500,
}, { "discount_from": 1615248000000,
"name": "chunithm", "discount_to": 1615852799000
"items": [{ },
"type": "pack", {
"id": "chunithm", "name": "nijuusei",
"is_available": true "items": [
}], {
"price": 300, "type": "pack",
"orig_price": 300 "id": "nijuusei",
}, { "is_available": true
"name": "prelude", },
"items": [{ {
"type": "pack", "type": "core",
"id": "prelude", "amount": 5,
"is_available": true "id": "core_generic",
}], "is_available": true
"price": 400, }
"orig_price": 400, ],
"discount_from": 1615248000000, "price": 500,
"discount_to": 1615852799000 "orig_price": 500,
}, { "discount_from": 1615248000000,
"name": "omatsuri", "discount_to": 1615852799000
"items": [{ },
"type": "pack", {
"id": "omatsuri", "name": "rei",
"is_available": true "items": [
}], {
"price": 500, "type": "pack",
"orig_price": 500, "id": "rei",
"discount_from": 1615248000000, "is_available": true
"discount_to": 1615852799000 },
}, { {
"name": "vs", "type": "core",
"items": [{ "amount": 5,
"type": "pack", "id": "core_generic",
"id": "vs", "is_available": true
"is_available": true }
}], ],
"price": 500, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
}, { },
"name": "extend", {
"items": [{ "name": "tonesphere",
"type": "pack", "items": [
"id": "extend", {
"is_available": true "type": "pack",
}], "id": "tonesphere",
"price": 700, "is_available": true
"orig_price": 700, },
"discount_from": 1615248000000, {
"discount_to": 1615852799000 "type": "core",
}, { "amount": 5,
"name": "alice", "id": "core_generic",
"items": [{ "is_available": true
"type": "pack", }
"id": "alice", ],
"is_available": true "price": 500,
}], "orig_price": 500,
"orig_price": 500, "discount_from": 1615248000000,
"price": 500 "discount_to": 1615852799000
}, { },
"name": "alice_append_1", {
"items": [{ "name": "groovecoaster",
"type": "pack", "items": [
"id": "alice_append_1", {
"is_available": true "type": "pack",
}], "id": "groovecoaster",
"orig_price": 300, "is_available": true
"price": 300 },
}, { {
"name": "ongeki", "type": "core",
"items": [{ "amount": 5,
"type": "pack", "id": "core_generic",
"id": "ongeki", "is_available": true
"is_available": true }
}], ],
"orig_price": 400, "price": 500,
"price": 400 "orig_price": 500,
}, { "discount_from": 1615248000000,
"name": "maimai", "discount_to": 1615852799000
"items": [{ },
"type": "pack", {
"id": "maimai", "name": "zettai",
"is_available": true "items": [
}], {
"orig_price": 400, "type": "pack",
"price": 400 "id": "zettai",
}, { "is_available": true
"name": "chunithm_append_1", },
"items": [{ {
"type": "pack", "type": "core",
"id": "chunithm_append_1", "amount": 5,
"is_available": true "id": "core_generic",
}], "is_available": true
"orig_price": 300, }
"price": 300 ],
}, { "price": 500,
"name": "observer_append_1", "orig_price": 500,
"items": [{ "discount_from": 1615248000000,
"type": "pack", "discount_to": 1615852799000
"id": "observer_append_1", },
"is_available": true {
}], "name": "chunithm",
"orig_price": 300, "items": [
"price": 300 {
}, { "type": "pack",
"name": "observer", "id": "chunithm",
"items": [{ "is_available": true
"type": "pack", },
"id": "observer", {
"is_available": true "type": "core",
}], "amount": 3,
"orig_price": 500, "id": "core_generic",
"price": 500 "is_available": true
}] }
],
"price": 300,
"orig_price": 300
},
{
"name": "prelude",
"items": [
{
"type": "pack",
"id": "prelude",
"is_available": true
},
{
"type": "core",
"amount": 4,
"id": "core_generic",
"is_available": true
}
],
"price": 400,
"orig_price": 400,
"discount_from": 1615248000000,
"discount_to": 1615852799000
},
{
"name": "omatsuri",
"items": [
{
"type": "pack",
"id": "omatsuri",
"is_available": true
},
{
"type": "core",
"amount": 5,
"id": "core_generic",
"is_available": true
}
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
},
{
"name": "vs",
"items": [
{
"type": "pack",
"id": "vs",
"is_available": true
},
{
"type": "core",
"amount": 5,
"id": "core_generic",
"is_available": true
}
],
"price": 500,
"orig_price": 500,
"discount_from": 1615248000000,
"discount_to": 1615852799000
},
{
"name": "extend",
"items": [
{
"type": "pack",
"id": "extend",
"is_available": true
},
{
"type": "core",
"amount": 7,
"id": "core_generic",
"is_available": true
}
],
"price": 0,
"orig_price": 700,
"discount_from": 1615248000000,
"discount_to": 1615852799000
},
{
"name": "alice",
"items": [
{
"type": "pack",
"id": "alice",
"is_available": true
},
{
"type": "core",
"amount": 5,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 500,
"price": 500
},
{
"name": "alice_append_1",
"items": [
{
"type": "pack",
"id": "alice_append_1",
"is_available": true
},
{
"type": "core",
"amount": 3,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 300,
"price": 300
},
{
"name": "ongeki",
"items": [
{
"type": "pack",
"id": "ongeki",
"is_available": true
},
{
"type": "core",
"amount": 4,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 400,
"price": 400
},
{
"name": "maimai",
"items": [
{
"type": "pack",
"id": "maimai",
"is_available": true
},
{
"type": "core",
"amount": 4,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 400,
"price": 400
},
{
"name": "chunithm_append_1",
"items": [
{
"type": "pack",
"id": "chunithm_append_1",
"is_available": true
},
{
"type": "core",
"amount": 3,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 300,
"price": 300
},
{
"name": "observer_append_1",
"items": [
{
"type": "pack",
"id": "observer_append_1",
"is_available": true
},
{
"type": "core",
"amount": 3,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 300,
"price": 300
},
{
"name": "observer",
"items": [
{
"type": "pack",
"id": "observer",
"is_available": true
},
{
"type": "core",
"amount": 5,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 500,
"price": 500
},
{
"name": "observer_append_2",
"items": [
{
"type": "pack",
"id": "observer_append_2",
"is_available": true
},
{
"type": "core",
"amount": 3,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 300,
"price": 300
},
{
"name": "wacca",
"items": [
{
"type": "pack",
"id": "wacca",
"is_available": true
},
{
"type": "core",
"amount": 5,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 500,
"price": 500
},
{
"name": "nijuusei_append_1",
"items": [
{
"type": "pack",
"id": "nijuusei_append_1",
"is_available": true
},
{
"type": "core",
"amount": 3,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 300,
"price": 300
}
]

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ import server.arcworld
import server.arcdownload import server.arcdownload
import server.arcpurchase import server.arcpurchase
import server.init import server.init
import server.character
import os import os
import sys import sys
@@ -112,13 +113,14 @@ def add_url_prefix(url, strange_flag=False):
return prefix + r return prefix + r
def error_return(error_code): # 错误返回 def error_return(error_code, extra={}): # 错误返回
# -7 处理交易时发生了错误 # -7 处理交易时发生了错误
# -5 所有的曲目都已经下载完毕 # -5 所有的曲目都已经下载完毕
# -4 您的账号已在别处登录 # -4 您的账号已在别处登录
# -3 无法连接至服务器 # -3 无法连接至服务器
# 2 Arcaea服务器正在维护 # 2 Arcaea服务器正在维护
# 5 请更新Arcaea到最新版本 # 5 请更新Arcaea到最新版本
# 9 新版本请等待几分钟
# 100 无法在此ip地址下登录游戏 # 100 无法在此ip地址下登录游戏
# 101 用户名占用 # 101 用户名占用
# 102 电子邮箱已注册 # 102 电子邮箱已注册
@@ -155,10 +157,17 @@ def error_return(error_code): # 错误返回
# 9907 更新数据时发生了问题 # 9907 更新数据时发生了问题
# 9908 服务器只支持最新的版本请更新Arcaea # 9908 服务器只支持最新的版本请更新Arcaea
# 其它 发生未知错误 # 其它 发生未知错误
return jsonify({ if extra:
"success": False, return jsonify({
"error_code": error_code "success": False,
}) "error_code": error_code,
"extra": extra
})
else:
return jsonify({
"success": False,
"error_code": error_code
})
@app.route('/') @app.route('/')
@@ -181,7 +190,7 @@ def login():
if 'AppVersion' in request.headers: # 版本检查 if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION: if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION: if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return jsonify({"success": False, "error_code": 5}) return error_return(5)
headers = request.headers headers = request.headers
id_pwd = headers['Authorization'] id_pwd = headers['Authorization']
@@ -192,14 +201,17 @@ def login():
else: else:
device_id = 'low_version' device_id = 'low_version'
token, error_code = server.auth.arc_login( token, error_code, extra = server.auth.arc_login(
name, password, device_id, request.remote_addr) name, password, device_id, request.remote_addr)
if not error_code: if not error_code:
r = {"success": True, "token_type": "Bearer"} r = {"success": True, "token_type": "Bearer"}
r['access_token'] = token r['access_token'] = token
return jsonify(r) return jsonify(r)
else: else:
return error_return(error_code) if extra:
return error_return(error_code, extra)
else:
return error_return(error_code)
@app.route(add_url_prefix('/user/'), methods=['POST']) # 注册接口 @app.route(add_url_prefix('/user/'), methods=['POST']) # 注册接口
@@ -207,7 +219,7 @@ def register():
if 'AppVersion' in request.headers: # 版本检查 if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION: if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION: if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return jsonify({"success": False, "error_code": 5}) return error_return(5)
name = request.form['name'] name = request.form['name']
password = request.form['password'] password = request.form['password']
@@ -239,6 +251,15 @@ def aggregate(user_id):
return jsonify(r) return jsonify(r)
@app.route(add_url_prefix('/user/me'), methods=['GET']) # 用户信息给baa查分器用的
@server.auth.auth_required(request)
def user_me(user_id):
r = server.info.arc_aggregate_small(user_id)
if r['success']:
r['value'] = r['value'][0]['value']
return jsonify(r)
@app.route(add_url_prefix('/user/me/character'), methods=['POST']) # 角色切换 @app.route(add_url_prefix('/user/me/character'), methods=['POST']) # 角色切换
@server.auth.auth_required(request) @server.auth.auth_required(request)
def character_change(user_id): def character_change(user_id):
@@ -262,9 +283,7 @@ def character_change(user_id):
@app.route(add_url_prefix('/<path:path>/toggle_uncap', True), methods=['POST']) @app.route(add_url_prefix('/<path:path>/toggle_uncap', True), methods=['POST'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
def character_uncap(user_id, path): def character_uncap(user_id, path):
while '//' in path: character_id = int(path[path.find('character')+10:])
path = path.replace('//', '/')
character_id = int(path[21:])
r = server.setme.change_char_uncap(user_id, character_id) r = server.setme.change_char_uncap(user_id, character_id)
if r is not None: if r is not None:
return jsonify({ return jsonify({
@@ -278,6 +297,37 @@ def character_uncap(user_id, path):
return error_return(108) return error_return(108)
# 角色觉醒
@app.route(add_url_prefix('/<path:path>/uncap', True), methods=['POST'])
@server.auth.auth_required(request)
def character_first_uncap(user_id, path):
character_id = int(path[path.find('character')+10:])
r = server.character.char_uncap(user_id, character_id)
if r is not None:
return jsonify({
"success": True,
"value": r
})
else:
return error_return(108)
# 角色使用以太之滴
@app.route(add_url_prefix('/<path:path>/exp', True), methods=['POST'])
@server.auth.auth_required(request)
def character_exp(user_id, path):
character_id = int(path[path.find('character')+10:])
amount = int(request.form['amount'])
r = server.character.char_use_core(user_id, character_id, amount)
if r is not None:
return jsonify({
"success": True,
"value": r
})
else:
return error_return(108)
@app.route(add_url_prefix('/friend/me/add'), methods=['POST']) # 加好友 @app.route(add_url_prefix('/friend/me/add'), methods=['POST']) # 加好友
@server.auth.auth_required(request) @server.auth.auth_required(request)
def add_friend(user_id): def add_friend(user_id):
@@ -396,17 +446,11 @@ def song_score_post(user_id):
r, re = server.arcscore.arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count, r, re = server.arcscore.arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count,
perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type) perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type)
if r is not None: if re:
if re: return jsonify({
return jsonify({ "success": True,
"success": True, "value": re
"value": re })
})
else:
return jsonify({
"success": True,
"value": {"user_rating": r}
})
else: else:
return error_return(108) return error_return(108)
@@ -427,15 +471,14 @@ def score_token():
@server.auth.auth_required(request) @server.auth.auth_required(request)
def score_token_world(user_id): def score_token_world(user_id):
args = request.args args = request.args
server.arcworld.play_world_song(user_id, args) r = server.arcworld.play_world_song(user_id, args)
return jsonify({ if r:
"success": True, return jsonify({
"value": { "success": True,
"stamina": 12, "value": r
"max_stamina_ts": 1599547603825, })
"token": "13145201919810" else:
} return error_return(108)
})
@app.route(add_url_prefix('/user/me/save'), methods=['GET']) # 从云端同步 @app.route(add_url_prefix('/user/me/save'), methods=['GET']) # 从云端同步
@@ -506,6 +549,28 @@ def claim_present(user_id, present_id):
return error_return(108) return error_return(108)
# 购买体力
@app.route(add_url_prefix('/purchase/me/stamina/<buy_stamina_type>'), methods=['POST'])
@server.auth.auth_required(request)
def purchase_stamina(user_id, buy_stamina_type):
if buy_stamina_type == 'fragment':
r, error_code = server.arcworld.buy_stamina_by_fragment(user_id)
else:
return error_return(108)
if error_code:
return error_return(error_code)
else:
if r:
return jsonify({
"success": True,
"value": r
})
else:
return error_return(108)
# 购买world模式boost # 购买world模式boost
@app.route(add_url_prefix('/purchase/me/item'), methods=['POST']) @app.route(add_url_prefix('/purchase/me/item'), methods=['POST'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
@@ -521,6 +586,16 @@ def prog_boost(user_id):
"success": True, "success": True,
"value": {'ticket': ticket} "value": {'ticket': ticket}
} }
elif request.form['item_id'] == 'stamina6':
r, error_code = server.arcworld.buy_stamina_by_ticket(user_id)
if error_code:
return error_return(error_code)
re = {
"success": True,
"value": r
}
return jsonify(re) return jsonify(re)
@@ -528,9 +603,9 @@ def prog_boost(user_id):
@server.auth.auth_required(request) @server.auth.auth_required(request)
def pack(user_id): def pack(user_id):
if 'pack_id' in request.form: if 'pack_id' in request.form:
return jsonify(server.arcpurchase.buy_thing(user_id, request.form['pack_id'], 'pack')) return jsonify(server.arcpurchase.buy_thing(user_id, request.form['pack_id']))
if 'single_id' in request.form: if 'single_id' in request.form:
return jsonify(server.arcpurchase.buy_thing(user_id, request.form['single_id'], 'single')) return jsonify(server.arcpurchase.buy_thing(user_id, request.form['single_id']))
return jsonify({"success": True}) return jsonify({"success": True})
@@ -557,12 +632,13 @@ def world_all(user_id):
}) })
@app.route(add_url_prefix('/world/map/me/'), methods=['POST']) # 进入地图 @app.route(add_url_prefix('/world/map/me'), methods=['POST']) # 进入地图
@server.auth.auth_required(request) @server.auth.auth_required(request)
def world_in(user_id): def world_in(user_id):
map_id = request.form['map_id'] map_id = request.form['map_id']
flag = server.arcworld.unlock_user_world(user_id, map_id)
return jsonify({ return jsonify({
"success": True, "success": flag,
"value": server.arcworld.get_user_world(user_id, map_id) "value": server.arcworld.get_user_world(user_id, map_id)
}) })
@@ -622,7 +698,7 @@ def download(file_path):
@app.route(add_url_prefix('/<path:path>', True), methods=['POST']) @app.route(add_url_prefix('/<path:path>', True), methods=['POST'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
def sys_set(user_id, path): def sys_set(user_id, path):
set_arg = path[10:] set_arg = path[5:]
value = request.form['value'] value = request.form['value']
server.setme.arc_sys_set(user_id, value, set_arg) server.setme.arc_sys_set(user_id, value, set_arg)
r = server.info.arc_aggregate_small(user_id) r = server.info.arc_aggregate_small(user_id)

View File

@@ -1 +1,2 @@
cd /d %~dp0
python -B main.py python -B main.py

View File

@@ -1,5 +1,6 @@
from server.sql import Connect from server.sql import Connect
import server.info import server.item
import server.character
import time import time
import json import json
@@ -12,28 +13,57 @@ def int2b(x):
return True return True
def get_item(c, type='pack'): def get_purchase(c, type='pack'):
# 读取packs内容返回字典列表 # 读取packs内容返回字典列表
c.execute('''select * from item where type = :a''', {'a': type}) c.execute(
'''select * from purchase where purchase_name in (select purchase_name from purchase_item where type = :a)''', {'a': type})
x = c.fetchall() x = c.fetchall()
if not x: if not x:
return [] return []
re = [] re = []
for i in x: for i in x:
r = {"name": i[0], items = []
"items": [{ c.execute(
"type": i[1], '''select a.*, b.amount from item a, purchase_item b where a.item_id=b.item_id and a.type=b.type and b.purchase_name=:name''', {'name': i[0]})
"id": i[0], y = c.fetchall()
"is_available": int2b(i[2]) t = None
}], if y:
"price": i[3], for j in y:
"orig_price": i[4]} if j[3]:
amount = j[3]
else:
amount = 1
if i[0] == j[0]:
# 物品排序,否则客户端报错
t = {
"type": j[1],
"id": j[0],
"is_available": int2b(j[2]),
'amount': amount
}
else:
items.append({
"type": j[1],
"id": j[0],
"is_available": int2b(j[2]),
"amount": amount
})
if i[5] > 0:
r['discount_from'] = i[5] if t is not None:
if i[6] > 0: # 放到列表头
r['discount_to'] = i[6] items = [t, items]
r = {"name": i[0],
"items": items,
"price": i[1],
"orig_price": i[2]}
if i[3] > 0:
r['discount_from'] = i[3]
if i[4] > 0:
r['discount_to'] = i[4]
re.append(r) re.append(r)
@@ -44,7 +74,7 @@ def get_single_purchase():
# main里面没开数据库这里写一下代替 # main里面没开数据库这里写一下代替
re = [] re = []
with Connect() as c: with Connect() as c:
re = get_item(c, type='single') re = get_purchase(c, type='single')
return re return re
@@ -68,7 +98,7 @@ def buy_item(c, user_id, price):
return True, ticket - price return True, ticket - price
def buy_thing(user_id, item_id, item_type): def buy_thing(user_id, purchase_id):
# 购买物品接口,返回字典 # 购买物品接口,返回字典
success_flag = False success_flag = False
ticket = 0 ticket = 0
@@ -77,36 +107,50 @@ def buy_thing(user_id, item_id, item_type):
characters = [] characters = []
with Connect() as c: with Connect() as c:
c.execute('''select is_available, price, orig_price, discount_from, discount_to from item where item_id=:a and type=:b''', c.execute('''select price, orig_price, discount_from, discount_to from purchase where purchase_name=:a''',
{'a': item_id, 'b': item_type}) {'a': purchase_id})
x = c.fetchone() x = c.fetchone()
price = 0 price = 0
flag = False flag = False
if x: if x:
is_available = x[0] price = x[0]
price = x[1] orig_price = x[1]
orig_price = x[2] discount_from = x[2]
discount_from = x[3] discount_to = x[3]
discount_to = x[4] else:
return {
if not is_available: "success": False,
return False "error_code": 501
}
c.execute(
'''select item_id, type, amount from purchase_item where purchase_name=:a''', {'a': purchase_id})
x = c.fetchall()
if x:
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
flag, ticket = buy_item(c, user_id, price) flag, ticket = buy_item(c, user_id, price)
if flag: if flag:
c.execute('''insert into user_item values(:a,:b,:c)''', for i in x:
{'a': user_id, 'b': item_id, 'c': item_type}) if i[2]:
amount = i[2]
else:
amount = 1
server.item.claim_user_item(c, user_id, i[0], i[1], amount)
success_flag = True success_flag = True
else:
return {
"success": False,
"error_code": 501
}
packs = server.info.get_user_packs(c, user_id) packs = server.item.get_user_items(c, user_id, 'pack')
singles = server.info.get_user_singles(c, user_id) singles = server.item.get_user_items(c, user_id, 'single')
characters = server.info.get_user_characters(c, user_id) characters = server.character.get_user_characters(c, user_id)
return { return {
"success": success_flag, "success": success_flag,
@@ -145,10 +189,22 @@ def get_user_present(c, user_id):
if x: if x:
for i in x: for i in x:
if now <= int(i[1]): if now <= int(i[1]):
c.execute(
'''select * from present_item where present_id=?''', (i[0],))
y = c.fetchall()
items = []
if y:
for j in y:
if j is not None:
items.append({
"type": j[2],
"id": j[1],
"amount": j[3]
})
re.append({'expire_ts': i[1], re.append({'expire_ts': i[1],
'description': i[3], 'description': i[2],
'present_id': i[0], 'present_id': i[0],
'items': json.loads(i[2]) 'items': items
}) })
return re return re
@@ -169,16 +225,16 @@ def claim_user_present(user_id, present_id):
now = int(time.time() * 1000) now = int(time.time() * 1000)
if now <= int(x[1]): if now <= int(x[1]):
# 处理memory # 处理memory
items = json.loads(x[2]) c.execute(
for i in items: '''select * from present_item where present_id=?''', (x[0],))
if i['id'] == 'memory': y = c.fetchall()
c.execute('''select ticket from user where user_id=:a''', {
'a': user_id})
ticket = int(c.fetchone()[0])
ticket += int(i['amount'])
c.execute('''update user set ticket=:b where user_id=:a''', {
'a': user_id, 'b': ticket})
flag = True flag = True
if y:
for j in y:
if j is not None:
flag = flag and server.item.claim_user_item(
c, user_id, j[1], j[2], j[3])
else: else:
# 过期 # 过期
flag = False flag = False
@@ -196,12 +252,12 @@ def claim_user_redeem(user_id, code):
if not x: if not x:
return 0, 504 return 0, 504
if x[2] == 0: # 一次性 if x[1] == 0: # 一次性
c.execute( c.execute(
'''select exists(select * from user_redeem where code=:a)''', {'a': code}) '''select exists(select * from user_redeem where code=:a)''', {'a': code})
if c.fetchone() == (1,): if c.fetchone() == (1,):
return 0, 505 return 0, 505
elif x[2] == 1: # 每个玩家一次 elif x[1] == 1: # 每个玩家一次
c.execute('''select exists(select * from user_redeem where code=:a and user_id=:b)''', c.execute('''select exists(select * from user_redeem where code=:a and user_id=:b)''',
{'a': code, 'b': user_id}) {'a': code, 'b': user_id})
if c.fetchone() == (1,): if c.fetchone() == (1,):
@@ -210,17 +266,17 @@ def claim_user_redeem(user_id, code):
c.execute('''insert into user_redeem values(:b,:a)''', c.execute('''insert into user_redeem values(:b,:a)''',
{'a': code, 'b': user_id}) {'a': code, 'b': user_id})
items = json.loads(x[1]) c.execute('''select * from redeem_item where code=?''', (code,))
for i in items: x = c.fetchall()
if i['type'] == 'fragment': flag = True
fragment = i['amount'] if x:
if i['type'] == 'memory': for i in x:
c.execute('''select ticket from user where user_id=:a''', { if i[2] == 'fragment':
'a': user_id}) fragment += i[3]
ticket = int(c.fetchone()[0]) else:
ticket += int(i['amount']) flag = flag and server.item.claim_user_item(
c.execute('''update user set ticket=:b where user_id=:a''', { c, user_id, i[1], i[2], i[3])
'a': user_id, 'b': ticket}) if flag:
error_code = None error_code = None
return fragment, error_code return fragment, error_code

View File

@@ -38,22 +38,30 @@ def get_score(c, user_id, song_id, difficulty):
{'a': user_id, 'b': song_id, 'c': difficulty}) {'a': user_id, 'b': song_id, 'c': difficulty})
x = c.fetchone() x = c.fetchone()
if x is not None: if x is not None:
c.execute('''select name, character_id, is_skill_sealed, is_char_uncapped, favorite_character from user where user_id = :a''', { c.execute('''select name, character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, favorite_character from user where user_id = :a''', {
'a': user_id}) 'a': user_id})
y = c.fetchone() y = c.fetchone()
if y is not None: if y is not None:
character = y[1] character = y[1]
is_char_uncapped = int2b(y[3]) is_char_uncapped = int2b(y[3])
if y[4] != -1: if y[5] != -1:
character = y[4] character = y[5]
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id=:a and character_id=:b''', { if not Config.CHARACTER_FULL_UNLOCK:
'a': user_id, 'b': character}) c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id=:a and character_id=:b''', {
'a': user_id, 'b': character})
else:
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id=:a and character_id=:b''', {
'a': user_id, 'b': character})
z = c.fetchone() z = c.fetchone()
if z: if z:
if z[1] == 0: if z[1] == 0:
is_char_uncapped = int2b(z[0]) is_char_uncapped = int2b(z[0])
else: else:
is_char_uncapped = False is_char_uncapped = False
else:
if y[4] == 1:
is_char_uncapped = False
return { return {
"user_id": x[0], "user_id": x[0],
"song_id": x[1], "song_id": x[1],
@@ -168,6 +176,8 @@ def arc_score_me(user_id, song_id, difficulty, limit=20):
x = c.fetchall() x = c.fetchall()
if x != []: if x != []:
rank = amount - limit rank = amount - limit
if rank < 0:
rank = 0
for i in x: for i in x:
rank += 1 rank += 1
y = get_score(c, i[0], song_id, difficulty) y = get_score(c, i[0], song_id, difficulty)
@@ -272,30 +282,39 @@ def get_user_ptt_float(c, user_id) -> float:
c.execute('''select rating from best_score where user_id = :a order by rating DESC limit 30''', { c.execute('''select rating from best_score where user_id = :a order by rating DESC limit 30''', {
'a': user_id}) 'a': user_id})
x = c.fetchall() x = c.fetchall()
if x != []: if not Config.USE_B10_AS_R10:
n = len(x) if x != []:
for i in x: for i in x:
sumr += float(i[0]) sumr += float(i[0])
c.execute('''select * from recent30 where user_id = :a''', {'a': user_id}) c.execute('''select * from recent30 where user_id = :a''',
x = c.fetchone() {'a': user_id})
if x is not None: x = c.fetchone()
r30 = [] if x is not None:
s30 = [] r30 = []
for i in range(1, 61, 2): s30 = []
if x[i] is not None: for i in range(1, 61, 2):
r30.append(float(x[i])) if x[i] is not None:
s30.append(x[i+1]) r30.append(float(x[i]))
else: s30.append(x[i+1])
r30.append(0) else:
s30.append('') r30.append(0)
r30, s30 = (list(t) for t in zip(*sorted(zip(r30, s30), reverse=True))) s30.append('')
songs = [] r30, s30 = (list(t)
i = 0 for t in zip(*sorted(zip(r30, s30), reverse=True)))
while len(songs) < 10 and i <= 29 and s30[i] != '' and s30[i] is not None: songs = []
if s30[i] not in songs: i = 0
sumr += r30[i] while len(songs) < 10 and i <= 29 and s30[i] != '' and s30[i] is not None:
songs.append(s30[i]) if s30[i] not in songs:
i += 1 sumr += r30[i]
songs.append(s30[i])
i += 1
else:
if x != []:
for i in range(len(x)):
t = float(x[i][0])
sumr += t
if i < 10:
sumr += t
return sumr/40 return sumr/40
@@ -305,6 +324,54 @@ def get_user_ptt(c, user_id) -> int:
return int(get_user_ptt_float(c, user_id)*100) return int(get_user_ptt_float(c, user_id)*100)
def get_user_world_rank(c, user_id) -> int:
# 用户世界排名计算同时返回排名值如果超过设定最大值返回0
with Connect('./database/arcsong.db') as c2:
c2.execute(
'''select sid, rating_ftr, rating_byn from songs''')
x = c2.fetchall()
if x:
song_list_ftr = [user_id]
song_list_byn = [user_id]
for i in x:
if i[1] > 0:
song_list_ftr.append(i[0])
if i[2] > 0:
song_list_byn.append(i[0])
if len(song_list_ftr) >= 2:
c.execute('''select sum(score) from best_score where user_id=? and difficulty=2 and song_id in ({0})'''.format(
','.join(['?']*(len(song_list_ftr)-1))), tuple(song_list_ftr))
x = c.fetchone()
if x[0] is not None:
score_sum = x[0]
else:
score_sum = 0
if len(song_list_byn) >= 2:
c.execute('''select sum(score) from best_score where user_id=? and difficulty=3 and song_id in ({0})'''.format(
','.join(['?']*(len(song_list_byn)-1))), tuple(song_list_byn))
x = c.fetchone()
if x[0] is not None:
score_sum += x[0]
else:
score_sum += 0
c.execute('''update user set world_rank_score = :b where user_id = :a''', {
'a': user_id, 'b': score_sum})
c.execute(
'''select count(*) from user where world_rank_score > ?''', (score_sum,))
x = c.fetchone()
if x and x[0] + 1 <= Config.WORLD_RANK_MAX:
return x[0] + 1
else:
return 0
def update_recent30(c, user_id, song_id, rating, is_protected): def update_recent30(c, user_id, song_id, rating, is_protected):
# 刷新r30这里的判断方法存疑这里的song_id结尾包含难度数字 # 刷新r30这里的判断方法存疑这里的song_id结尾包含难度数字
def insert_r30table(c, user_id, a, b): def insert_r30table(c, user_id, a, b):
@@ -433,133 +500,20 @@ def arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count, per
ptt = get_user_ptt(c, user_id) ptt = get_user_ptt(c, user_id)
c.execute('''update user set rating_ptt = :a where user_id = :b''', { c.execute('''update user set rating_ptt = :a where user_id = :b''', {
'a': ptt, 'b': user_id}) 'a': ptt, 'b': user_id})
# 世界模式判断 # 世界模式判断
c.execute('''select stamina_multiply,fragment_multiply,prog_boost_multiply from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', { c.execute('''select stamina_multiply,fragment_multiply,prog_boost_multiply from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
'a': user_id, 'b': song_id, 'c': difficulty}) 'a': user_id, 'b': song_id, 'c': difficulty})
x = c.fetchone() x = c.fetchone()
re = None
if x: if x:
stamina_multiply = x[0] re = server.arcworld.world_update(
fragment_multiply = x[1] c, user_id, song_id, difficulty, rating, clear_type, beyond_gauge, x[0], x[1], x[2])
prog_boost_multiply = x[2] re['global_rank'] = get_user_world_rank(c, user_id) # 更新世界排名
step_times = stamina_multiply * fragment_multiply / \ re["user_rating"] = ptt
100 * (prog_boost_multiply+100)/100 else:
exp_times = stamina_multiply * (prog_boost_multiply+100)/100 re = {'global_rank': get_user_world_rank(
if prog_boost_multiply != 0: c, user_id), 'user_rating': ptt}
c.execute('''update user set prog_boost = 0 where user_id = :a''', {
'a': user_id})
c.execute('''delete from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
'a': user_id, 'b': song_id, 'c': difficulty})
c.execute('''select character_id,frag,prog,overdrive from user_char where user_id = :a and character_id = (select character_id from user where user_id=:a)''', {
'a': user_id})
y = c.fetchone()
if y:
character_id = y[0]
flag = float(y[1])
prog = float(y[2])
overdrive = float(y[3])
else:
character_id = 0
flag = 0
prog = 0
overdrive = 0
c.execute('''select current_map from user where user_id = :a''', {
'a': user_id})
map_id = c.fetchone()[0]
if beyond_gauge == 0: # 是否是beyond挑战
base_step = 2.5 + 2.45*rating**0.5
step = base_step * (prog/50) * step_times
else:
info = server.arcworld.get_world_info(map_id)
if clear_type == 0:
base_step = 8/9 + (rating/1.3)**0.5
else:
base_step = 8/3 + (rating/1.3)**0.5
if character_id in info['character_affinity']:
affinity_multiplier = info['affinity_multiplier'][info['character_affinity'].index(
character_id)]
else:
affinity_multiplier = 1
step = base_step * (prog/50) * step_times * affinity_multiplier
c.execute('''select * from user_world where user_id = :a and map_id =:b''',
{'a': user_id, 'b': map_id})
y = c.fetchone()
rewards, steps, curr_position, curr_capture, info = server.arcworld.climb_step(
user_id, map_id, step, y[3], y[2])
if beyond_gauge == 0:
re = {
"rewards": rewards,
"exp": 25000,
"level": 30,
"base_progress": base_step,
"progress": step,
"user_map": {
"user_id": user_id,
"curr_position": curr_position,
"curr_capture": curr_capture,
"is_locked": int2b(y[4]),
"map_id": map_id,
"prev_capture": y[3],
"prev_position": y[2],
"beyond_health": info['beyond_health'],
"steps": steps
},
"char_stats": {
"character_id": character_id,
"frag": flag,
"prog": prog,
"overdrive": overdrive
},
"current_stamina": 12,
"max_stamina_ts": 1586274871917,
"user_rating": ptt
}
else:
re = {
"rewards": rewards,
"exp": 25000,
"level": 30,
"base_progress": base_step,
"progress": step,
"user_map": {
"user_id": user_id,
"curr_position": curr_position,
"curr_capture": curr_capture,
"is_locked": int2b(y[4]),
"map_id": map_id,
"prev_capture": y[3],
"prev_position": y[2],
"beyond_health": info['beyond_health'],
"step_count": len(steps)
},
"char_stats": {
"character_id": character_id,
"frag": flag,
"prog": prog,
"overdrive": overdrive
},
"current_stamina": 12,
"max_stamina_ts": 1586274871917,
"user_rating": ptt
}
if stamina_multiply != 1:
re['stamina_multiply'] = stamina_multiply
if fragment_multiply != 100:
re['fragment_multiply'] = fragment_multiply
if prog_boost_multiply != 0:
re['prog_boost_multiply'] = prog_boost_multiply
if curr_position == info['step_count']-1 and info['is_repeatable']: # 循环图判断
curr_position = 0
c.execute('''update user_world set curr_position=:a, curr_capture=:b where user_id=:c and map_id=:d''', {
'a': curr_position, 'b': curr_capture, 'c': user_id, 'd': map_id})
return ptt, re return ptt, re
@@ -597,16 +551,14 @@ def arc_score_check(user_id, song_id, difficulty, score, shiny_perfect_count, pe
def refresh_all_score_rating(): def refresh_all_score_rating():
# 刷新所有best成绩的rating # 刷新所有best成绩的rating
error = 'Unknown error.' error = 'Unknown error.'
song_data = []
with Connect('./database/arcsong.db') as c: with Connect('./database/arcsong.db') as c:
with Connect() as c2: c.execute(
c.execute( '''select sid, rating_pst, rating_prs, rating_ftr, rating_byn from songs''')
'''select sid, rating_pst, rating_prs, rating_ftr, rating_byn from songs''') x = c.fetchall()
x = c.fetchall()
if x: if x:
song_list = [i[0] for i in song_data] song_list = [i[0] for i in x]
with Connect() as c: with Connect() as c:
c.execute('''update best_score set rating=0 where song_id not in ({0})'''.format( c.execute('''update best_score set rating=0 where song_id not in ({0})'''.format(
','.join(['?']*len(song_list))), tuple(song_list)) ','.join(['?']*len(song_list))), tuple(song_list))
@@ -932,6 +884,36 @@ def arc_all_get(user_id):
"mi": 6, "mi": 6,
"c": True, "c": True,
"r": True "r": True
}, {
"ma": 7,
"mi": 1,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 2,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 3,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 4,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 5,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 6,
"c": True,
"r": True
}, { }, {
"ma": 9, "ma": 9,
"mi": 1, "mi": 1,
@@ -963,50 +945,50 @@ def arc_all_get(user_id):
"c": True, "c": True,
"r": True "r": True
}, { }, {
"ma": 6, "ma": 10,
"mi": 1, "mi": 1,
"c": True, "c": True,
"r": True "r": True
}, { }, {
"ma": 6, "ma": 10,
"mi": 2, "mi": 2,
"c": True, "c": True,
"r": True "r": True
}, { }, {
"ma": 6, "ma": 10,
"mi": 3, "mi": 3,
"c": True, "c": True,
"r": True "r": True
}, { }, {
"ma": 7, "ma": 10,
"mi": 1,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 2,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 3,
"c": True,
"r": True
}, {
"ma": 7,
"mi": 4, "mi": 4,
"c": True, "c": True,
"r": True "r": True
}, { }, {
"ma": 7, "ma": 10,
"mi": 5, "mi": 5,
"c": True, "c": True,
"r": True "r": True
}, { }, {
"ma": 7, "ma": 10,
"mi": 6, "mi": 6,
"c": True, "c": True,
"r": True "r": True
}, {
"ma": 6,
"mi": 1,
"c": True,
"r": True
}, {
"ma": 6,
"mi": 2,
"c": True,
"r": True
}, {
"ma": 6,
"mi": 3,
"c": True,
"r": True
}, { }, {
"ma": 8, "ma": 8,
"mi": 1, "mi": 1,
@@ -1029,42 +1011,30 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "worldexecuteme|2|0", "unlock_key": "worldexecuteme|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "viyellastears|2|0",
"complete": 1
}, { }, {
"unlock_key": "viciousheroism|2|0", "unlock_key": "viciousheroism|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "loschen|2|0", "unlock_key": "vector|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "vector|2|0", "unlock_key": "valhallazero|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "corpssansorganes|2|0", "unlock_key": "tiferet|2|0",
"complete": 1
}, {
"unlock_key": "valhallazero|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "tiferet|1|0", "unlock_key": "tiferet|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "ouroboros|2|0", "unlock_key": "tiemedowngently|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "tiemedowngently|1|0", "unlock_key": "tempestissimo|2|101",
"complete": 1
}, {
"unlock_key": "aegleseeker|2|3|crystalgravity|2",
"complete": 10
}, {
"unlock_key": "tempestissimo|0|101",
"complete": 100 "complete": 100
}, { }, {
"unlock_key": "syro|2|0", "unlock_key": "tempestissimo|1|101",
"complete": 1 "complete": 100
}, { }, {
"unlock_key": "ringedgenesis|2|0", "unlock_key": "ringedgenesis|2|0",
"complete": 1 "complete": 1
@@ -1074,11 +1044,14 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "snowwhite|2|0", "unlock_key": "snowwhite|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "senkyou|1|0",
"complete": 1
}, { }, {
"unlock_key": "senkyou|2|0", "unlock_key": "senkyou|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "senkyou|1|0", "unlock_key": "seclusion|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "scarletlance|2|0", "unlock_key": "scarletlance|2|0",
@@ -1104,6 +1077,12 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "reinvent|2|0", "unlock_key": "reinvent|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "felis|1|0",
"complete": 1
}, {
"unlock_key": "rekkaresonance|2|0",
"complete": 1
}, { }, {
"unlock_key": "reinvent|1|0", "unlock_key": "reinvent|1|0",
"complete": 1 "complete": 1
@@ -1151,7 +1130,7 @@ def arc_all_get(user_id):
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "saikyostronger|2|3|einherjar|2", "unlock_key": "saikyostronger|2|3|einherjar|2",
"complete": 3 "complete": 6
}, { }, {
"unlock_key": "cyanine|2|0", "unlock_key": "cyanine|2|0",
"complete": 1 "complete": 1
@@ -1162,7 +1141,10 @@ def arc_all_get(user_id):
"unlock_key": "purgatorium|1|0", "unlock_key": "purgatorium|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "pragmatism|2|0", "unlock_key": "tiemedowngently|1|0",
"complete": 1
}, {
"unlock_key": "ouroboros|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "ringedgenesis|1|0", "unlock_key": "ringedgenesis|1|0",
@@ -1212,6 +1194,12 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "melodyoflove|1|0", "unlock_key": "melodyoflove|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "mazymetroplex|2|0",
"complete": 1
}, {
"unlock_key": "seclusion|2|0",
"complete": 1
}, { }, {
"unlock_key": "lucifer|2|0", "unlock_key": "lucifer|2|0",
"complete": 1 "complete": 1
@@ -1227,9 +1215,6 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "lostdesire|2|0", "unlock_key": "lostdesire|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "tiemedowngently|2|0",
"complete": 1
}, { }, {
"unlock_key": "solitarydream|1|0", "unlock_key": "solitarydream|1|0",
"complete": 1 "complete": 1
@@ -1242,12 +1227,30 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "loschen|1|0", "unlock_key": "loschen|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "syro|2|0",
"complete": 1
}, {
"unlock_key": "aegleseeker|2|3|paperwitch|2",
"complete": 10
}, {
"unlock_key": "livefastdieyoung|1|0",
"complete": 1
}, { }, {
"unlock_key": "shadesoflight|1|0", "unlock_key": "shadesoflight|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "kanagawa|2|0", "unlock_key": "kanagawa|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "ifi|2|0",
"complete": 1
}, {
"unlock_key": "kanagawa|1|0",
"complete": 1
}, {
"unlock_key": "jump|1|0",
"complete": 1
}, { }, {
"unlock_key": "grievouslady|1|101", "unlock_key": "grievouslady|1|101",
"complete": 100 "complete": 100
@@ -1264,28 +1267,7 @@ def arc_all_get(user_id):
"unlock_key": "ignotus|2|0", "unlock_key": "ignotus|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "snowwhite|1|0", "unlock_key": "ignotus|1|0",
"complete": 1
}, {
"unlock_key": "partyvinyl|1|0",
"complete": 1
}, {
"unlock_key": "viciousheroism|1|0",
"complete": 1
}, {
"unlock_key": "gloryroad|0|0",
"complete": 1
}, {
"unlock_key": "axiumcrisis|1|0",
"complete": 1
}, {
"unlock_key": "kanagawa|1|0",
"complete": 1
}, {
"unlock_key": "ifi|2|0",
"complete": 1
}, {
"unlock_key": "jump|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "nhelv|1|0", "unlock_key": "nhelv|1|0",
@@ -1296,9 +1278,6 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "guardina|2|0", "unlock_key": "guardina|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "guardina|1|0",
"complete": 1
}, { }, {
"unlock_key": "lethaeus|1|0", "unlock_key": "lethaeus|1|0",
"complete": 1 "complete": 1
@@ -1308,9 +1287,6 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "guardina|0|0", "unlock_key": "guardina|0|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "jump|1|0",
"complete": 1
}, { }, {
"unlock_key": "oshamascramble|2|0", "unlock_key": "oshamascramble|2|0",
"complete": 1 "complete": 1
@@ -1326,9 +1302,6 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "darakunosono|1|0", "unlock_key": "darakunosono|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "valhallazero|1|0",
"complete": 1
}, { }, {
"unlock_key": "grimheart|1|0", "unlock_key": "grimheart|1|0",
"complete": 1 "complete": 1
@@ -1338,27 +1311,33 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "gothiveofra|1|0", "unlock_key": "gothiveofra|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "cyanine|1|0",
"complete": 1
}, {
"unlock_key": "tempestissimo|3|101",
"complete": 100
}, {
"unlock_key": "overwhelm|2|0",
"complete": 1
}, {
"unlock_key": "chronostasis|2|0",
"complete": 1
}, {
"unlock_key": "gloryroad|2|0",
"complete": 1
}, { }, {
"unlock_key": "etherstrike|1|0", "unlock_key": "etherstrike|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "singularity|2|0", "unlock_key": "singularity|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "viciousheroism|1|0",
"complete": 1
}, {
"unlock_key": "axiumcrisis|1|0",
"complete": 1
}, {
"unlock_key": "gloryroad|0|0",
"complete": 1
}, {
"unlock_key": "guardina|1|0",
"complete": 1
}, {
"unlock_key": "pragmatism|2|0",
"complete": 1
}, {
"unlock_key": "oracle|2|0",
"complete": 1
}, {
"unlock_key": "genocider|2|0",
"complete": 1
}, { }, {
"unlock_key": "buchigireberserker|2|3|gothiveofra|2", "unlock_key": "buchigireberserker|2|3|gothiveofra|2",
"complete": 1 "complete": 1
@@ -1389,9 +1368,6 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "flyburg|2|0", "unlock_key": "flyburg|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "oracle|2|0",
"complete": 1
}, { }, {
"unlock_key": "clotho|2|0", "unlock_key": "clotho|2|0",
"complete": 1 "complete": 1
@@ -1425,12 +1401,39 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "espebranch|2|0", "unlock_key": "espebranch|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "syro|1|0",
"complete": 1
}, {
"unlock_key": "anokumene|2|0",
"complete": 1
}, {
"unlock_key": "equilibrium_challenge|2|102",
"complete": 1
}, {
"unlock_key": "overwhelm|2|0",
"complete": 1
}, {
"unlock_key": "chronostasis|2|0",
"complete": 1
}, {
"unlock_key": "gloryroad|2|0",
"complete": 1
}, {
"unlock_key": "equilibrium_challenge|1|102",
"complete": 1
}, { }, {
"unlock_key": "aterlbus|2|0", "unlock_key": "aterlbus|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "dreaminattraction|2|0", "unlock_key": "dreaminattraction|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "dreaminattraction|1|0",
"complete": 1
}, {
"unlock_key": "divinelight|2|0",
"complete": 1
}, { }, {
"unlock_key": "goodtek|2|0", "unlock_key": "goodtek|2|0",
"complete": 1 "complete": 1
@@ -1449,24 +1452,27 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "dandelion|1|0", "unlock_key": "dandelion|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "aegleseeker_challenge|0|102",
"complete": 1
}, { }, {
"unlock_key": "oblivia|1|0", "unlock_key": "oblivia|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "cyberneciacatharsis|1|0", "unlock_key": "cyberneciacatharsis|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "blrink|1|0",
"complete": 1
}, {
"unlock_key": "livefastdieyoung|2|0",
"complete": 1
}, { }, {
"unlock_key": "corpssansorganes|0|0", "unlock_key": "corpssansorganes|0|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "vector|1|0",
"complete": 1
}, { }, {
"unlock_key": "conflict|2|0", "unlock_key": "conflict|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "ignotus|1|0",
"complete": 1
}, { }, {
"unlock_key": "monochromeprincess|1|0", "unlock_key": "monochromeprincess|1|0",
"complete": 1 "complete": 1
@@ -1518,21 +1524,33 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "blaster|1|0", "unlock_key": "blaster|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "genocider|1|0",
"complete": 1
}, { }, {
"unlock_key": "rise|1|0", "unlock_key": "rise|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "tempestissimo|0|101",
"complete": 100
}, {
"unlock_key": "aegleseeker|2|3|crystalgravity|2",
"complete": 10
}, {
"unlock_key": "tempestissimo|3|101",
"complete": 100
}, {
"unlock_key": "cyanine|1|0",
"complete": 1
}, { }, {
"unlock_key": "axiumcrisis|2|0", "unlock_key": "axiumcrisis|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "tempestissimo|2|101",
"complete": 100
}, { }, {
"unlock_key": "solitarydream|2|0", "unlock_key": "solitarydream|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "aegleseeker|1|103", "unlock_key": "aegleseeker|1|103",
"complete": 103 "complete": 1
}, { }, {
"unlock_key": "amazingmightyyyy|2|0", "unlock_key": "amazingmightyyyy|2|0",
"complete": 1 "complete": 1
@@ -1543,16 +1561,34 @@ def arc_all_get(user_id):
"unlock_key": "linearaccelerator|1|0", "unlock_key": "linearaccelerator|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "tempestissimo|1|101", "unlock_key": "valhallazero|2|0",
"complete": 100 "complete": 1
}, {
"unlock_key": "corpssansorganes|2|0",
"complete": 1
}, { }, {
"unlock_key": "conflict|1|0", "unlock_key": "conflict|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "mazymetroplex|1|0",
"complete": 1
}, {
"unlock_key": "viyellastears|2|0",
"complete": 1
}, {
"unlock_key": "equilibrium_challenge|0|102",
"complete": 1
}, { }, {
"unlock_key": "grievouslady|0|101", "unlock_key": "grievouslady|0|101",
"complete": 100 "complete": 100
}, { }, {
"unlock_key": "dreaminattraction|1|0", "unlock_key": "snowwhite|1|0",
"complete": 1
}, {
"unlock_key": "partyvinyl|1|0",
"complete": 1
}, {
"unlock_key": "jump|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "saikyostronger|2|3|izana|2", "unlock_key": "saikyostronger|2|3|izana|2",
@@ -1563,32 +1599,26 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "halcyon|1|0", "unlock_key": "halcyon|1|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "felis|1|0",
"complete": 1
}, {
"unlock_key": "overwhelm|1|0",
"complete": 1
}, {
"unlock_key": "toaliceliddell|2|0",
"complete": 1
}, {
"unlock_key": "blrink|1|0",
"complete": 1
}, { }, {
"unlock_key": "heavensdoor|2|0", "unlock_key": "heavensdoor|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "genesis|2|0", "unlock_key": "genesis|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "vector|2|0",
"complete": 1
}, {
"unlock_key": "loschen|2|0",
"complete": 1
}, {
"unlock_key": "divinelight|1|0",
"complete": 1
}, { }, {
"unlock_key": "viyellastears|1|0", "unlock_key": "viyellastears|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "aegleseeker|2|103", "unlock_key": "aegleseeker|2|103",
"complete": 103
}, {
"unlock_key": "tiferet|2|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "grimheart|2|0", "unlock_key": "grimheart|2|0",
@@ -1605,36 +1635,33 @@ def arc_all_get(user_id):
}, { }, {
"unlock_key": "sheriruth|2|0", "unlock_key": "sheriruth|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "aegleseeker|2|3|paperwitch|2",
"complete": 10
}, { }, {
"unlock_key": "sheriruth|1|0", "unlock_key": "sheriruth|1|0",
"complete": 1 "complete": 1
}, { }, {
"unlock_key": "aegleseeker|2|3|loschen|2", "unlock_key": "aegleseeker|2|3|loschen|2",
"complete": 10 "complete": 10
}, {
"unlock_key": "cyberneciacatharsis|2|0",
"complete": 1
}, { }, {
"unlock_key": "essenceoftwilight|2|0", "unlock_key": "essenceoftwilight|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "syro|1|0",
"complete": 1
}, {
"unlock_key": "anokumene|2|0",
"complete": 1
}, {
"unlock_key": "freefall|1|0",
"complete": 1
}, { }, {
"unlock_key": "darakunosono|2|0", "unlock_key": "darakunosono|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "freefall|1|0",
"complete": 1
}, {
"unlock_key": "toaliceliddell|2|0",
"complete": 1
}, {
"unlock_key": "overwhelm|1|0",
"complete": 1
}, { }, {
"unlock_key": "nirvluce|2|0", "unlock_key": "nirvluce|2|0",
"complete": 1 "complete": 1
}, {
"unlock_key": "cyberneciacatharsis|2|0",
"complete": 1
}] }]
return { return {

View File

@@ -1,6 +1,16 @@
import json import json
from server.sql import Connect from server.sql import Connect
from setting import Config
import server.item
import server.character
import server.info
import server.arcpurchase
import os import os
import time
ETO_UNCAP_BONUS_PROGRESS = 7
LUNA_UNCAP_BONUS_PROGRESS = 7
def int2b(x): def int2b(x):
@@ -11,6 +21,23 @@ def int2b(x):
return True return True
def calc_stamina(max_stamina_ts, curr_stamina):
# 计算体力,返回剩余体力数值
stamina = int(server.info.MAX_STAMINA - (max_stamina_ts -
int(time.time()*1000)) / server.info.STAMINA_RECOVER_TICK)
if stamina >= server.info.MAX_STAMINA:
if curr_stamina >= server.info.MAX_STAMINA:
stamina = curr_stamina
else:
stamina = server.info.MAX_STAMINA
if stamina < 0:
stamina = 0
return stamina
def get_world_name(file_dir='./database/map'): def get_world_name(file_dir='./database/map'):
# 获取所有地图名称,返回列表 # 获取所有地图名称,返回列表
L = [] L = []
@@ -42,7 +69,10 @@ def get_user_world_info(user_id, map_id):
info['curr_capture'] = x[3] info['curr_capture'] = x[3]
info['is_locked'] = int2b(x[4]) info['is_locked'] = int2b(x[4])
else: else:
c.execute('''insert into user_world values(:a,:b,0,0,0)''', { info['curr_position'] = 0
info['curr_capture'] = 0
info['is_locked'] = True
c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
'a': user_id, 'b': map_id}) 'a': user_id, 'b': map_id})
return info return info
@@ -64,8 +94,8 @@ def get_current_map(user_id):
def get_world_all(user_id): def get_world_all(user_id):
# 读取所有地图信息并处理,返回字典列表 # 读取所有地图信息并处理,返回字典列表
re = [] re = []
worlds = get_world_name()
with Connect() as c: with Connect() as c:
worlds = get_world_name()
for map_id in worlds: for map_id in worlds:
info = get_world_info(map_id) info = get_world_info(map_id)
steps = info['steps'] steps = info['steps']
@@ -84,7 +114,10 @@ def get_world_all(user_id):
info['curr_capture'] = x[3] info['curr_capture'] = x[3]
info['is_locked'] = int2b(x[4]) info['is_locked'] = int2b(x[4])
else: else:
c.execute('''insert into user_world values(:a,:b,0,0,0)''', { info['curr_position'] = 0
info['curr_capture'] = 0
info['is_locked'] = True
c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
'a': user_id, 'b': map_id}) 'a': user_id, 'b': map_id})
re.append(info) re.append(info)
@@ -92,6 +125,23 @@ def get_world_all(user_id):
return re return re
def get_available_maps():
# 获取当前可用图(用户设定的),返回字典列表
re = []
for i in Config.AVAILABLE_MAP:
info = get_world_info(i)
del info['steps']
try:
del info['is_locked']
del info['curr_position']
del info['curr_capture']
except:
pass
re.append(info)
return re
def get_user_world(user_id, map_id): def get_user_world(user_id, map_id):
# 获取用户图信息,返回字典 # 获取用户图信息,返回字典
re = {} re = {}
@@ -103,7 +153,7 @@ def get_user_world(user_id, map_id):
"user_id": user_id, "user_id": user_id,
"curr_position": 0, "curr_position": 0,
"curr_capture": 0, "curr_capture": 0,
"is_locked": False, "is_locked": True,
"map_id": map_id "map_id": map_id
} }
if x: if x:
@@ -111,12 +161,40 @@ def get_user_world(user_id, map_id):
re['curr_capture'] = x[3] re['curr_capture'] = x[3]
re['is_locked'] = int2b(x[4]) re['is_locked'] = int2b(x[4])
else: else:
c.execute('''insert into user_world values(:a,:b,0,0,0)''', { c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
'a': user_id, 'b': map_id}) 'a': user_id, 'b': map_id})
return re return re
def unlock_user_world(user_id, map_id):
# 解锁用户的图,返回成功与否布尔值
with Connect() as c:
c.execute(
'''select is_locked from user_world where map_id=? and user_id=?''', (map_id, user_id))
x = c.fetchone()
if x:
is_locked = x[0]
else:
is_locked = 1
c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
'a': user_id, 'b': map_id})
if is_locked == 1:
map_info = get_world_info(map_id)
if 'require_type' in map_info and map_info['require_type'] != '':
if map_info['require_type'] in ['pack', 'single']:
c.execute('''select exists(select * from user_item where user_id=? and item_id=? and type=?)''',
(user_id, map_info['require_id'], map_info['require_type']))
if c.fetchone() == (0,):
return False
c.execute(
'''update user_world set is_locked=0 where user_id=? and map_id=?''', (user_id, map_id))
return True
def change_user_current_map(user_id, map_id): def change_user_current_map(user_id, map_id):
# 改变用户当前图 # 改变用户当前图
with Connect() as c: with Connect() as c:
@@ -126,7 +204,8 @@ def change_user_current_map(user_id, map_id):
def play_world_song(user_id, args): def play_world_song(user_id, args):
# 声明是世界模式的打歌,并且记录加成信息 # 声明是世界模式的打歌,并且记录加成信息,返回字典
r = {}
with Connect() as c: with Connect() as c:
stamina_multiply = 1 stamina_multiply = 1
fragment_multiply = 100 fragment_multiply = 100
@@ -147,11 +226,39 @@ def play_world_song(user_id, args):
c.execute('''insert into world_songplay values(:a,:b,:c,:d,:e,:f)''', { c.execute('''insert into world_songplay values(:a,:b,:c,:d,:e,:f)''', {
'a': user_id, 'b': args['song_id'], 'c': args['difficulty'], 'd': stamina_multiply, 'e': fragment_multiply, 'f': prog_boost_multiply}) 'a': user_id, 'b': args['song_id'], 'c': args['difficulty'], 'd': stamina_multiply, 'e': fragment_multiply, 'f': prog_boost_multiply})
return None c.execute('''select current_map from user where user_id = :a''', {
'a': user_id})
map_id = c.fetchone()[0]
info = get_world_info(map_id)
# 体力计算
c.execute(
'''select max_stamina_ts, stamina from user where user_id=?''', (user_id,))
x = c.fetchone()
max_stamina_ts = x[0] if x and x[0] is not None else 0
stamina = x[1] if x and x[1] is not None else 12
now = int(time.time() * 1000)
# 体力不足
if calc_stamina(max_stamina_ts, stamina) < info['stamina_cost']:
return {}
stamina = calc_stamina(max_stamina_ts, stamina) - \
info['stamina_cost'] * stamina_multiply
max_stamina_ts = now + server.info.STAMINA_RECOVER_TICK * \
(server.info.MAX_STAMINA - stamina)
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
(max_stamina_ts, stamina, user_id))
r = {
"stamina": stamina,
"max_stamina_ts": max_stamina_ts,
"token": "13145201919810"
}
return r
def climb_step(user_id, map_id, step, prev_capture, prev_position): def climb_step(user_id, map_id, step, prev_capture, prev_position):
# 爬梯子,返回奖励列表,台阶列表,当前的位置和坐标,图信息 # 爬梯子,返回奖励列表,台阶列表,当前的位置和坐标,图信息
info = get_world_info(map_id) info = get_world_info(map_id)
step_count = int(info['step_count']) step_count = int(info['step_count'])
@@ -245,3 +352,296 @@ def climb_step(user_id, map_id, step, prev_capture, prev_position):
steps.append(x) steps.append(x)
return rewards, steps, curr_position, curr_capture, info return rewards, steps, curr_position, curr_capture, info
def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gauge, stamina_multiply=1, fragment_multiply=100, prog_boost_multiply=0):
# 成绩上传后世界模式更新,返回字典
step_times = stamina_multiply * fragment_multiply / \
100 * (prog_boost_multiply+100)/100
exp_times = stamina_multiply * (prog_boost_multiply+100)/100
if prog_boost_multiply != 0:
c.execute('''update user set prog_boost = 0 where user_id = :a''', {
'a': user_id})
c.execute('''delete from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
'a': user_id, 'b': song_id, 'c': difficulty})
c.execute(
'''select character_id, max_stamina_ts, stamina, is_skill_sealed, is_char_uncapped, is_char_uncapped_override from user where user_id=?''', (user_id,))
x = c.fetchone()
character_id = x[0] if x and x[0] is not None else 0
max_stamina_ts = x[1] if x and x[1] is not None else 0
stamina = x[2] if x and x[2] is not None else 12
is_skill_sealed = x[3] if x and x[3] is not None else 1
skill = False
skill_uncap = False
if not is_skill_sealed:
if x:
skill = True
if x[4] is not None and x[4] == 1:
skill_uncap = True
if x[5] is not None and x[5] == 1:
skill_uncap = False
c.execute('''select frag1,prog1,overdrive1,frag20,prog20,overdrive20,frag30,prog30,overdrive30,skill_id,skill_id_uncap from character where character_id=?''', (character_id,))
x = c.fetchone()
if Config.CHARACTER_FULL_UNLOCK:
c.execute('''select level, exp from user_char_full where user_id = :a and character_id = :b''', {
'a': user_id, 'b': character_id})
else:
c.execute('''select level, exp from user_char where user_id = :a and character_id = :b''', {
'a': user_id, 'b': character_id})
y = c.fetchone()
if y:
level = y[0]
exp = y[1]
else:
level = 1
exp = 0
if x:
flag = server.character.calc_char_value(level, x[0], x[3], x[6])
prog = server.character.calc_char_value(level, x[1], x[4], x[7])
overdrive = server.character.calc_char_value(level, x[2], x[5], x[8])
if x[9] is not None and x[9] != '' and skill:
skill = x[9]
else:
skill = None
if x[10] is not None and x[9] != '' and skill_uncap:
skill_uncap = x[10]
else:
skill_uncap = None
else:
flag = 0
prog = 0
overdrive = 0
skill = None
skill_uncap = None
c.execute('''select current_map from user where user_id = :a''', {
'a': user_id})
map_id = c.fetchone()[0]
if beyond_gauge == 0: # 是否是beyond挑战
base_step = 2.5 + 2.45*rating**0.5
step = base_step * (prog/50) * step_times
else:
info = get_world_info(map_id)
if clear_type == 0:
base_step = 8/9 + (rating/1.3)**0.5
else:
base_step = 8/3 + (rating/1.3)**0.5
if character_id in info['character_affinity']:
affinity_multiplier = info['affinity_multiplier'][info['character_affinity'].index(
character_id)]
else:
affinity_multiplier = 1
step = base_step * (prog/50) * step_times * affinity_multiplier
c.execute('''select * from user_world where user_id = :a and map_id =:b''',
{'a': user_id, 'b': map_id})
y = c.fetchone()
if y[4] == 1: # 图不可用
return {}
rewards, steps, curr_position, curr_capture, info = climb_step(
user_id, map_id, step, y[3], y[2])
# Eto和Luna的技能
character_bonus_progress = None
skill_special = ''
if skill_uncap is not None and skill_uncap and skill_uncap in ['eto_uncap', 'luna_uncap']:
skill_special = skill_uncap
elif skill is not None and skill and skill in ['eto_uncap', 'luna_uncap']:
skill_special = skill
if skill_special == 'eto_uncap':
# eto觉醒技能获得残片奖励时世界模式进度加7
fragment_flag = False
for i in rewards:
for j in i['items']:
if j['type'] == 'fragment':
fragment_flag = True
break
if fragment_flag:
break
if fragment_flag:
character_bonus_progress = ETO_UNCAP_BONUS_PROGRESS
step += character_bonus_progress * step_times
rewards, steps, curr_position, curr_capture, info = climb_step(
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
elif skill_special == 'luna_uncap':
# luna觉醒技能限制格开始时世界模式进度加7
if 'restrict_id' in steps[0] and 'restrict_type' in steps[0] and steps[0]['restrict_type'] != '' and steps[0]['restrict_id'] != '':
character_bonus_progress = LUNA_UNCAP_BONUS_PROGRESS
step += character_bonus_progress * step_times
rewards, steps, curr_position, curr_capture, info = climb_step(
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
for i in rewards: # 物品分发
for j in i['items']:
amount = j['amount'] if 'amount' in j else 1
item_id = j['id'] if 'id' in j else ''
server.item.claim_user_item(c, user_id, item_id, j['type'], amount)
if 'step_type' in steps[-1]:
if 'plusstamina' in steps[-1]['step_type'] and 'plus_stamina_value' in steps[-1]:
# 体力格子
max_stamina_ts, stamina = add_stamina(
c, user_id, int(steps[-1]['plus_stamina_value']))
# 角色升级
if not Config.CHARACTER_FULL_UNLOCK:
exp, level = server.character.calc_level_up(
c, user_id, character_id, exp, exp_times*rating*6)
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
(level, exp, user_id, character_id))
else:
exp = server.character.LEVEL_STEPS[level]
if beyond_gauge == 0:
re = {
"rewards": rewards,
"exp": exp,
"level": level,
"base_progress": base_step,
"progress": step,
"user_map": {
"user_id": user_id,
"curr_position": curr_position,
"curr_capture": curr_capture,
"is_locked": int2b(y[4]),
"map_id": map_id,
"prev_capture": y[3],
"prev_position": y[2],
"beyond_health": info['beyond_health'],
"steps": steps
},
"char_stats": {
"character_id": character_id,
"frag": flag,
"prog": prog,
"overdrive": overdrive
},
"current_stamina": calc_stamina(max_stamina_ts, stamina),
"max_stamina_ts": max_stamina_ts
}
else:
re = {
"rewards": rewards,
"exp": exp,
"level": level,
"base_progress": base_step,
"progress": step,
"user_map": {
"user_id": user_id,
"curr_position": curr_position,
"curr_capture": curr_capture,
"is_locked": int2b(y[4]),
"map_id": map_id,
"prev_capture": y[3],
"prev_position": y[2],
"beyond_health": info['beyond_health'],
"step_count": len(steps)
},
"char_stats": {
"character_id": character_id,
"frag": flag,
"prog": prog,
"overdrive": overdrive
},
"current_stamina": calc_stamina(max_stamina_ts, stamina),
"max_stamina_ts": max_stamina_ts
}
if character_bonus_progress is not None:
re['character_bonus_progress'] = character_bonus_progress
if stamina_multiply != 1:
re['stamina_multiply'] = stamina_multiply
if fragment_multiply != 100:
re['fragment_multiply'] = fragment_multiply
if prog_boost_multiply != 0:
re['prog_boost_multiply'] = prog_boost_multiply
if curr_position == info['step_count']-1 and info['is_repeatable']: # 循环图判断
curr_position = 0
c.execute('''update user_world set curr_position=:a, curr_capture=:b where user_id=:c and map_id=:d''', {
'a': curr_position, 'b': curr_capture, 'c': user_id, 'd': map_id})
return re
def add_stamina(c, user_id, add_stamina):
# 增添体力返回max_stamina_ts和stamina
now = int(time.time() * 1000)
c.execute(
'''select max_stamina_ts, stamina from user where user_id=?''', (user_id,))
x = c.fetchone()
if x and x[0] is not None and x[1] is not None:
stamina = calc_stamina(x[0], x[1]) + add_stamina
max_stamina_ts = now - \
(stamina-server.info.MAX_STAMINA) * \
server.info.STAMINA_RECOVER_TICK
else:
max_stamina_ts = now
stamina = server.info.MAX_STAMINA
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
(max_stamina_ts, stamina, user_id))
return max_stamina_ts, stamina
def buy_stamina_by_fragment(user_id):
# 残片买体力,返回字典和错误码
r = {}
with Connect() as c:
c.execute(
'''select next_fragstam_ts from user where user_id=?''', (user_id,))
x = c.fetchone()
if x:
now = int(time.time() * 1000)
next_fragstam_ts = x[0] if x[0] else 0
if now < next_fragstam_ts:
return {}, 905
next_fragstam_ts = now + 24*3600*1000
max_stamina_ts, stamina = add_stamina(c, user_id, 6)
c.execute('''update user set next_fragstam_ts=?, max_stamina_ts=?, stamina=? where user_id=?''',
(next_fragstam_ts, max_stamina_ts, stamina, user_id))
r = {
"user_id": user_id,
"stamina": stamina,
"max_stamina_ts": max_stamina_ts,
"next_fragstam_ts": next_fragstam_ts
}
return r, None
def buy_stamina_by_ticket(user_id):
# 源点买体力,返回字典和错误码
r = {}
with Connect() as c:
flag, ticket = server.arcpurchase.buy_item(c, user_id, 50)
if flag:
max_stamina_ts, stamina = add_stamina(c, user_id, 6)
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
(max_stamina_ts, stamina, user_id))
r = {
"user_id": user_id,
"stamina": stamina,
"max_stamina_ts": max_stamina_ts,
"ticket": ticket
}
else:
return None, 501
return r, None

View File

@@ -5,9 +5,11 @@ import functools
from setting import Config from setting import Config
from flask import jsonify from flask import jsonify
BAN_TIME = [1, 3, 7, 15, 31]
def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断 def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
# 查询数据库中的user表验证账号密码返回并记录token多返回个error code # 查询数据库中的user表验证账号密码返回并记录token多返回个error code和extra
# token采用user_id和时间戳连接后hash生成真的是瞎想的没用bear # token采用user_id和时间戳连接后hash生成真的是瞎想的没用bear
# 密码和token的加密方式为 SHA-256 # 密码和token的加密方式为 SHA-256
@@ -15,16 +17,21 @@ def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判
token = None token = None
with Connect() as c: with Connect() as c:
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest() hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
c.execute('''select user_id, password from user where name = :name''', { c.execute('''select user_id, password, ban_flag from user where name = :name''', {
'name': name}) 'name': name})
x = c.fetchone() x = c.fetchone()
if x is not None: if x is not None:
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 > now:
return None, 105, {'remaining_ts': ban_timestamp-now}
if x[1] == '': if x[1] == '':
# 账号封禁 # 账号封禁
error_code = 106 error_code = 106
elif x[1] == hash_pwd: elif x[1] == hash_pwd:
user_id = str(x[0]) user_id = str(x[0])
now = int(time.time() * 1000)
token = hashlib.sha256( token = hashlib.sha256(
(user_id + str(now)).encode("utf8")).hexdigest() (user_id + str(now)).encode("utf8")).hexdigest()
c.execute( c.execute(
@@ -49,6 +56,13 @@ def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判
device_list) + 1 - device_list.count(device_id) - Config.LOGIN_DEVICE_NUMBER_LIMIT device_list) + 1 - device_list.count(device_id) - Config.LOGIN_DEVICE_NUMBER_LIMIT
if should_delete_num >= 1: # 删掉多余token 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, 105, {'remaining_ts': remaining_ts}
c.execute('''delete from login where rowid in (select rowid from login where user_id=:user_id limit :a);''', 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)}) {'user_id': user_id, 'a': int(should_delete_num)})
@@ -62,7 +76,7 @@ def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判
# 用户名错误 # 用户名错误
error_code = 104 error_code = 104
return token, error_code return token, error_code, None
def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册 def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册
@@ -91,13 +105,18 @@ def arc_register(name: str, password: str, device_id: str, email: str, ip: str):
return 2000001 return 2000001
def insert_user_char(c, user_id): def insert_user_char(c, user_id):
# 为用户添加所有可用角色 # 为用户添加初始角色
c.execute('''select * from character''') 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() x = c.fetchall()
if x != []: if x:
for i in x: for i in x:
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', { exp = 25000 if i[1] == 30 else 10000
'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]}) c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
(user_id, i[0], i[1], exp, i[2], 0))
user_id = None user_id = None
token = None token = None
@@ -190,3 +209,24 @@ def auth_required(request):
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

View File

@@ -0,0 +1,256 @@
from setting import Config
from server.sql import Connect
import server.info
import server.item
import server.setme
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}
def int2b(x):
# int与布尔值转换
if x is None or x == 0:
return False
else:
return True
def get_level_steps():
# 返回level_steps字典数组
return [{'level': i, 'level_exp': LEVEL_STEPS[i]} for i in LEVEL_STEPS]
def calc_char_value(level, value1, value20, value30):
# 计算搭档数值的核心函数,返回浮点数
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]
def calc_char_value_30(level, stata, statb, lva=20, lvb=30):
# 计算21~30级搭档数值返回浮点数
return (level - lva) * (statb - stata) / (lvb - lva) + stata
if level < 1 or level > 30:
return 0
elif 1 <= level <= 20:
return calc_char_value_20(level, value1, value20)
else:
return calc_char_value_30(level, value20, value30)
def get_char_core(c, character_id):
# 得到对应角色觉醒所需的核心,返回字典列表
r = []
c.execute(
'''select item_id, amount from char_item where character_id = ? and type="core"''', (character_id,))
x = c.fetchall()
if x:
for i in x:
r.append({'core_type': i[0], 'amount': i[1]})
return r
def get_user_characters(c, user_id):
# 获取用户所拥有角色,返回列表
c.execute('''select character_id from user_char where user_id = :user_id''',
{'user_id': user_id})
x = c.fetchall()
characters = []
if x:
for i in x:
characters.append(i[0])
return characters
def get_user_character(c, user_id):
# 得到用户拥有的角色列表,返回列表
if Config.CHARACTER_FULL_UNLOCK:
c.execute('''select * from user_char_full a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
{'user_id': user_id})
else:
c.execute('''select * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
{'user_id': user_id})
x = c.fetchall()
if not x and not Config.CHARACTER_FULL_UNLOCK:
# 添加初始角色
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 * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
{'user_id': user_id})
x = c.fetchall()
if not x:
return []
r = []
for i in x:
char = {
"is_uncapped_override": int2b(i[5]),
"is_uncapped": int2b(i[4]),
"uncap_cores": get_char_core(c, i[1]),
"char_type": i[22],
"skill_id_uncap": i[21],
"skill_requires_uncap": int2b(i[20]),
"skill_unlock_level": i[19],
"skill_id": i[18],
"overdrive": calc_char_value(i[2], i[11], i[14], i[17]),
"prog": calc_char_value(i[2], i[10], i[13], i[16]),
"frag": calc_char_value(i[2], i[9], i[12], i[15]),
"level_exp": LEVEL_STEPS[i[2]],
"exp": i[3],
"level": i[2],
"name": i[7],
"character_id": i[1]
}
if i[1] == 21:
char["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
r.append(char)
return r
def get_one_character(c, user_id, character_id):
# 得到用户某个拥有的角色列表,返回字典
if Config.CHARACTER_FULL_UNLOCK:
c.execute('''select * from user_char_full a,character b where a.user_id = :user_id and a.character_id=b.character_id and a.character_id=:a''',
{'user_id': user_id, 'a': character_id})
else:
c.execute('''select * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id and a.character_id=:a''',
{'user_id': user_id, 'a': character_id})
x = c.fetchone()
if not x:
return {}
r = {
"is_uncapped_override": int2b(x[5]),
"is_uncapped": int2b(x[4]),
"uncap_cores": get_char_core(c, x[1]),
"char_type": x[22],
"skill_id_uncap": x[21],
"skill_requires_uncap": int2b(x[20]),
"skill_unlock_level": x[19],
"skill_id": x[18],
"overdrive": calc_char_value(x[2], x[11], x[14], x[17]),
"prog": calc_char_value(x[2], x[10], x[13], x[16]),
"frag": calc_char_value(x[2], x[9], x[12], x[15]),
"level_exp": LEVEL_STEPS[x[2]],
"exp": x[3],
"level": x[2],
"name": x[7],
"character_id": x[1]
}
if x[1] == 21:
r["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
return r
def calc_level_up(c, user_id, character_id, exp, exp_addition):
# 计算角色升级,返回当前经验和等级
exp += exp_addition
if exp >= LEVEL_STEPS[20]: # 未觉醒溢出
c.execute('''select is_uncapped from user_char where user_id=? and character_id=?''',
(user_id, character_id))
x = c.fetchone()
if x and x[0] == 0:
return LEVEL_STEPS[20], 20
a = []
b = []
for i in LEVEL_STEPS:
a.append(i)
b.append(LEVEL_STEPS[i])
if exp >= b[-1]: # 溢出
return b[-1], a[-1]
if exp < b[0]: # 向下溢出,是异常状态
return 0, 1
i = len(a) - 1
while exp < b[i]:
i -= 1
return exp, a[i]
def char_use_core(user_id, character_id, amount):
# 以太之滴升级返回user_idcore状态角色状态的字典
r = None
with Connect() as c:
c.execute(
'''select amount from user_item where user_id=? and item_id="core_generic" and type="core"''', (user_id,))
x = c.fetchone()
if x:
pre_amount = x[0]
else:
pre_amount = 0
if amount <= pre_amount:
c.execute(
'''select exp from user_char where user_id=? and character_id=?''', (user_id, character_id))
x = c.fetchone()
if x:
exp, level = calc_level_up(
c, user_id, character_id, x[0], amount*server.info.CORE_EXP)
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
(level, exp, user_id, character_id))
server.item.claim_user_item(
c, user_id, 'core_generic', 'core', -amount)
r = {'character': [get_one_character(c, user_id, character_id)]}
r['cores'] = server.item.get_user_cores(c, user_id)
r['user_id'] = user_id
return r
def char_uncap(user_id, character_id):
# 角色觉醒返回user_idcore状态角色状态的字典
r = None
with Connect() as c:
c.execute('''select * from char_item where character_id=?''',
(character_id,))
x = c.fetchall()
if not x:
return None
success = True
for i in x:
c.execute(
'''select amount from user_item where user_id=? and item_id=? and type=?''', (user_id, i[1], i[2]))
y = c.fetchone()
if not y or i[3] > y[0]:
success = False
break
if success:
c.execute('''update user_char set is_uncapped=1, is_uncapped_override=0 where user_id=? and character_id=?''',
(user_id, character_id))
for i in x:
server.item.claim_user_item(c, user_id, i[1], i[2], -i[3])
r = {'character': [get_one_character(c, user_id, character_id)]}
r['cores'] = server.item.get_user_cores(c, user_id)
r['user_id'] = user_id
return r

View File

@@ -2,9 +2,15 @@ from server.sql import Connect
import server.arcworld import server.arcworld
import server.arcpurchase import server.arcpurchase
import server.arcdownload import server.arcdownload
import server.character
import server.item
import time import time
from setting import Config from setting import Config
MAX_STAMINA = 12
STAMINA_RECOVER_TICK = 1800000
CORE_EXP = 250
def int2b(x): def int2b(x):
# int与布尔值转换 # int与布尔值转换
@@ -46,47 +52,6 @@ def get_recent_score(c, user_id):
return [] return []
def get_user_character(c, user_id):
# 得到用户拥有的角色列表,返回列表
c.execute('''select * from user_char where user_id = :user_id''',
{'user_id': user_id})
x = c.fetchall()
if x != []:
s = []
for i in x:
char_name = ''
c.execute(
'''select name from character where character_id = :x''', {'x': i[1]})
y = c.fetchone()
if y is not None:
char_name = y[0]
char = {
"is_uncapped_override": int2b(i[14]),
"is_uncapped": int2b(i[13]),
"uncap_cores": [],
"char_type": i[12],
"skill_id_uncap": i[11],
"skill_requires_uncap": int2b(i[10]),
"skill_unlock_level": i[9],
"skill_id": i[8],
"overdrive": i[7],
"prog": i[6],
"frag": i[5],
"level_exp": i[4],
"exp": i[3],
"level": i[2],
"name": char_name,
"character_id": i[1]
}
if i[1] == 21:
char["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
s.append(char)
return s
else:
return []
def get_user_friend(c, user_id): def get_user_friend(c, user_id):
# 得到用户的朋友列表,返回列表 # 得到用户的朋友列表,返回列表
c.execute('''select user_id_other from friend where user_id_me = :user_id''', { c.execute('''select user_id_other from friend where user_id_me = :user_id''', {
@@ -111,8 +76,12 @@ def get_user_friend(c, user_id):
is_char_uncapped_override = int2b(y[9]) is_char_uncapped_override = int2b(y[9])
if y[23] != -1: if y[23] != -1:
character = y[23] character = y[23]
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id=:a and character_id=:b''', { if not Config.CHARACTER_FULL_UNLOCK:
'a': i[0], 'b': character}) c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id=:a and character_id=:b''', {
'a': i[0], 'b': character})
else:
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id=:a and character_id=:b''', {
'a': i[0], 'b': character})
z = c.fetchone() z = c.fetchone()
if z: if z:
is_char_uncapped = int2b(z[0]) is_char_uncapped = int2b(z[0])
@@ -135,75 +104,72 @@ def get_user_friend(c, user_id):
"user_id": i[0] "user_id": i[0]
}) })
s.sort(key=lambda item: item["recent_score"][0]["time_played"] if len(
item["recent_score"]) > 0 else 0, reverse=True)
return s return s
def get_user_singles(c, user_id):
# 得到用户的单曲,返回列表
c.execute('''select * from user_item where user_id = :user_id and type = "single"''',
{'user_id': user_id})
x = c.fetchall()
if not x:
return []
re = []
for i in x:
re.append(i[1])
return re
def get_user_packs(c, user_id):
# 得到用户的曲包,返回列表
c.execute('''select * from user_item where user_id = :user_id and type = "pack"''',
{'user_id': user_id})
x = c.fetchall()
if not x:
return []
re = []
for i in x:
re.append(i[1])
return re
def get_user_characters(c, user_id):
# 获取用户所拥有角色,返回列表
c.execute('''select character_id from user_char where user_id = :user_id''',
{'user_id': user_id})
x = c.fetchall()
characters = []
if x:
for i in x:
characters.append(i[0])
return characters
def get_user_me(c, user_id): def get_user_me(c, user_id):
# 构造user/me的数据返回字典 # 构造user/me的数据返回字典
c.execute('''select * from user where user_id = :x''', {'x': user_id}) c.execute('''select * from user where user_id = :x''', {'x': user_id})
x = c.fetchone() x = c.fetchone()
r = {} r = {}
if x is not None: if x is not None:
user_character = get_user_character(c, user_id) user_character = server.character.get_user_character(c, user_id)
# 下面没有使用get_user_characters函数是为了节省一次查询 # 下面没有使用get_user_characters函数是为了节省一次查询
characters = [] characters = []
for i in user_character: for i in user_character:
characters.append(i['character_id']) characters.append(i['character_id'])
character_id = x[6]
if character_id not in characters:
character_id = 0
c.execute(
'''update user set character_id=0 where user_id=?''', (user_id,))
favorite_character_id = x[23]
if favorite_character_id not in characters: # 这是考虑有可能favourite_character设置了用户未拥有的角色
favorite_character_id = -1
c.execute(
'''update user set favorite_character=-1 where user_id=?''', (user_id,))
# 计算世界排名
c.execute(
'''select world_rank_score from user where user_id=?''', (user_id,))
y = c.fetchone()
if y and y[0]:
c.execute(
'''select count(*) from user where world_rank_score > ?''', (y[0],))
y = c.fetchone()
if y and y[0] + 1 <= Config.WORLD_RANK_MAX:
world_rank = y[0] + 1
else:
world_rank = 0
else:
world_rank = 0
# 源点强化
prog_boost = 0 prog_boost = 0
if x[27] and x[27] != 0: if x[27] and x[27] != 0:
prog_boost = 300 prog_boost = 300
# 体力计算
next_fragstam_ts = -1
max_stamina_ts = 1586274871917
stamina = 12
if x[31]:
next_fragstam_ts = x[31]
if x[32]:
max_stamina_ts = x[32]
if x[33]:
stamina = x[33]
r = {"is_aprilfools": Config.IS_APRILFOOLS, r = {"is_aprilfools": Config.IS_APRILFOOLS,
"curr_available_maps": [], "curr_available_maps": server.arcworld.get_available_maps(),
"character_stats": user_character, "character_stats": user_character,
"friends": get_user_friend(c, user_id), "friends": get_user_friend(c, user_id),
"settings": { "settings": {
"favorite_character": x[23], "favorite_character": favorite_character_id,
"is_hide_rating": int2b(x[10]), "is_hide_rating": int2b(x[10]),
"max_stamina_notification_enabled": int2b(x[24]) "max_stamina_notification_enabled": int2b(x[24])
}, },
@@ -212,25 +178,25 @@ def get_user_me(c, user_id):
"user_code": x[4], "user_code": x[4],
"display_name": x[1], "display_name": x[1],
"ticket": x[26], "ticket": x[26],
"character": x[6], "character": character_id,
"is_locked_name_duplicate": False, "is_locked_name_duplicate": False,
"is_skill_sealed": int2b(x[7]), "is_skill_sealed": int2b(x[7]),
"current_map": x[25], "current_map": x[25],
"prog_boost": prog_boost, "prog_boost": prog_boost,
"next_fragstam_ts": -1, "next_fragstam_ts": next_fragstam_ts,
"max_stamina_ts": 1586274871917, "max_stamina_ts": max_stamina_ts,
"stamina": 12, "stamina": server.arcworld.calc_stamina(max_stamina_ts, stamina),
"world_unlocks": ["scenery_chap1", "scenery_chap2", "scenery_chap3", "scenery_chap4", "scenery_chap5"], "world_unlocks": server.item.get_user_items(c, user_id, 'world_unlock'),
"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"], "world_songs": server.item.get_user_items(c, user_id, 'world_song'),
"singles": get_user_singles(c, user_id), "singles": server.item.get_user_items(c, user_id, 'single'),
"packs": get_user_packs(c, user_id), "packs": server.item.get_user_items(c, user_id, 'pack'),
"characters": characters, "characters": characters,
"cores": [], "cores": server.item.get_user_cores(c, user_id),
"recent_score": get_recent_score(c, user_id), "recent_score": get_recent_score(c, user_id),
"max_friend": 50, "max_friend": 50,
"rating": x[5], "rating": x[5],
"join_date": int(x[3]), "join_date": int(x[3]),
"global_rank": 114514 "global_rank": world_rank
} }
return r return r
@@ -253,115 +219,32 @@ def arc_aggregate_big(user_id):
# 返回比较全的用户数据 # 返回比较全的用户数据
r = {"success": False} r = {"success": False}
with Connect() as c: with Connect() as c:
# 防止数据库锁
id_2 = server.arcdownload.get_all_songs(user_id, url_flag=False)
id_5 = {
"current_map": server.arcworld.get_current_map(user_id),
"user_id": user_id,
"maps": server.arcworld.get_world_all(user_id)
}
r = {"success": True, r = {"success": True,
"value": [{ "value": [{
"id": 0, "id": 0,
"value": get_user_me(c, user_id) "value": get_user_me(c, user_id)
}, { }, {
"id": 1, "id": 1,
"value": server.arcpurchase.get_item(c, 'pack') "value": server.arcpurchase.get_purchase(c, 'pack')
}, { }, {
"id": 2, "id": 2,
"value": server.arcdownload.get_all_songs(user_id, url_flag=False) "value": id_2
}, { }, {
"id": 3, "id": 3,
"value": { "value": {
"max_stamina": 12, "max_stamina": MAX_STAMINA,
"stamina_recover_tick": 1800000, "stamina_recover_tick": STAMINA_RECOVER_TICK,
"core_exp": 250, "core_exp": CORE_EXP,
"curr_ts": int(time.time())*1000, "curr_ts": int(time.time()*1000),
"level_steps": [{ "level_steps": server.character.get_level_steps(),
"level": 1, "world_ranking_enabled": True,
"level_exp": 0
}, {
"level": 2,
"level_exp": 50
}, {
"level": 3,
"level_exp": 100
}, {
"level": 4,
"level_exp": 150
}, {
"level": 5,
"level_exp": 200
}, {
"level": 6,
"level_exp": 300
}, {
"level": 7,
"level_exp": 450
}, {
"level": 8,
"level_exp": 650
}, {
"level": 9,
"level_exp": 900
}, {
"level": 10,
"level_exp": 1200
}, {
"level": 11,
"level_exp": 1600
}, {
"level": 12,
"level_exp": 2100
}, {
"level": 13,
"level_exp": 2700
}, {
"level": 14,
"level_exp": 3400
}, {
"level": 15,
"level_exp": 4200
}, {
"level": 16,
"level_exp": 5100
}, {
"level": 17,
"level_exp": 6100
}, {
"level": 18,
"level_exp": 7200
}, {
"level": 19,
"level_exp": 8500
}, {
"level": 20,
"level_exp": 10000
}, {
"level": 21,
"level_exp": 11500
}, {
"level": 22,
"level_exp": 13000
}, {
"level": 23,
"level_exp": 14500
}, {
"level": 24,
"level_exp": 16000
}, {
"level": 25,
"level_exp": 17500
}, {
"level": 26,
"level_exp": 19000
}, {
"level": 27,
"level_exp": 20500
}, {
"level": 28,
"level_exp": 22000
}, {
"level": 29,
"level_exp": 23500
}, {
"level": 30,
"level_exp": 25000
}],
"world_ranking_enabled": False,
"is_byd_chapter_unlocked": True "is_byd_chapter_unlocked": True
} }
}, { }, {
@@ -369,11 +252,7 @@ def arc_aggregate_big(user_id):
"value": server.arcpurchase.get_user_present(c, user_id) "value": server.arcpurchase.get_user_present(c, user_id)
}, { }, {
"id": 5, "id": 5,
"value": { "value": id_5
"current_map": server.arcworld.get_current_map(user_id),
"user_id": user_id,
"maps": server.arcworld.get_world_all(user_id)
}
} }
]} ]}

View File

@@ -0,0 +1,106 @@
from setting import Config
def get_user_items(c, user_id, item_type):
# 得到用户的物品,返回列表,不包含数量信息
if item_type == 'world_song' and Config.WORLD_SONG_FULL_UNLOCK or item_type == 'world_unlock' and Config.WORLD_SCENERY_FULL_UNLOCK:
c.execute('''select item_id from item where type=?''', (item_type,))
else:
c.execute('''select item_id from user_item where user_id = :user_id and type = :item_type''',
{'user_id': user_id, 'item_type': item_type})
x = c.fetchall()
if not x:
return []
re = []
for i in x:
re.append(i[0])
return re
def get_user_cores(c, user_id):
# 得到用户的core返回字典列表
r = []
c.execute(
'''select item_id, amount from user_item where user_id = ? and type="core"''', (user_id,))
x = c.fetchall()
if x:
for i in x:
if i[1]:
amount = i[1]
else:
amount = 0
r.append({'core_type': i[0], 'amount': amount})
return r
def claim_user_item(c, user_id, item_id, item_type, amount=1):
# 处理用户物品,包括添加和删除操作,返回成功与否布尔值
# 这个接口允许memory变成负数所以不能用来购买
try:
amount = int(amount)
except:
return False
if item_type not in ['memory', 'character']:
c.execute('''select is_available from item where item_id=? and type=?''',
(item_id, item_type))
x = c.fetchone()
if x:
if x[0] == 0:
return False
else:
return False
if item_type == 'core':
c.execute(
'''select amount from user_item where user_id=? and item_id=? and type="core"''', (user_id, item_id))
x = c.fetchone()
if x:
if x[0] + amount < 0: # 数量不足
return False
c.execute('''update user_item set amount=? where user_id=? and item_id=? and type="core"''',
(x[0]+amount, user_id, item_id))
else:
if amount < 0: # 添加数量错误
return False
c.execute('''insert into user_item values(?,?,"core",?)''',
(user_id, item_id, amount))
elif item_type == 'memory':
c.execute('''select ticket from user where user_id=?''', (user_id,))
x = c.fetchone()
if x is not None:
c.execute('''update user set ticket=? where user_id=?''',
(x[0]+amount, user_id))
else:
return False
elif item_type == 'character':
if not item_id.isdigit():
c.execute(
'''select character_id from character where name=?''', (item_id,))
x = c.fetchone()
if x:
character_id = x[0]
else:
return False
else:
character_id = int(item_id)
c.execute(
'''select exists(select * from user_char where user_id=? and character_id=?)''', (user_id, character_id))
if c.fetchone() == (0,):
c.execute('''insert into user_char values(?,?,1,0,0,0)''',
(user_id, character_id))
elif item_type in ['world_song', 'world_unlock', 'single', 'pack']:
c.execute('''select exists(select * from user_item where user_id=? and item_id=? and type=?)''',
(user_id, item_id, item_type))
if c.fetchone() == (0,):
c.execute('''insert into user_item values(:a,:b,:c,1)''',
{'a': user_id, 'b': item_id, 'c': item_type})
return True

View File

@@ -1,5 +1,7 @@
from server.sql import Connect from server.sql import Connect
from setting import Config
import server.info import server.info
import server.character
def b2int(x): def b2int(x):
@@ -23,18 +25,27 @@ def change_char(user_id, character_id, skill_sealed):
re = False re = False
with Connect() as c: with Connect() as c:
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''', if Config.CHARACTER_FULL_UNLOCK:
{'a': user_id, 'b': character_id}) c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
{'a': user_id, 'b': character_id})
else:
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
{'a': user_id, 'b': character_id})
x = c.fetchone() x = c.fetchone()
if x is not None: if x:
if skill_sealed == 'false': is_uncapped = x[0]
skill_sealed = False is_uncapped_override = x[1]
else: else:
skill_sealed = True return False
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': b2int(skill_sealed), 'b': character_id, 'c': x[0], 'd': x[1], 'e': user_id})
re = True if skill_sealed == 'false':
skill_sealed = False
else:
skill_sealed = True
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': b2int(skill_sealed), 'b': character_id, 'c': is_uncapped, 'd': is_uncapped_override, 'e': user_id})
re = True
return re return re
@@ -43,40 +54,46 @@ def change_char_uncap(user_id, character_id):
# 角色觉醒改变,返回字典 # 角色觉醒改变,返回字典
r = None r = None
with Connect() as c: with Connect() as c:
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''', if not Config.CHARACTER_FULL_UNLOCK:
{'a': user_id, 'b': character_id}) c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
{'a': user_id, 'b': character_id})
else:
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
{'a': user_id, 'b': character_id})
x = c.fetchone() x = c.fetchone()
if x is not None and x[0] == 1: if x is not None and x[0] == 1:
c.execute('''update user set is_char_uncapped_override = :a where user_id = :b''', { c.execute('''update user set is_char_uncapped_override = :a where user_id = :b''', {
'a': b2int(x[1] == 0), 'b': user_id}) 'a': b2int(x[1] == 0), 'b': user_id})
c.execute('''update user_char set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id}) if not Config.CHARACTER_FULL_UNLOCK:
c.execute('''select * from user_char where user_id = :a and character_id = :b''', c.execute('''update user_char set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
{'a': user_id, 'b': character_id}) 'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id})
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_id, character_id))
else:
c.execute('''update user_char_full set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id})
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_id, character_id))
y = c.fetchone() y = c.fetchone()
c.execute(
'''select name from character where character_id = :x''', {'x': y[1]})
z = c.fetchone()
if z is not None:
char_name = z[0]
if y is not None: if y is not None:
r = { r = {
"is_uncapped_override": int2b(y[14]), "is_uncapped_override": int2b(y[5]),
"is_uncapped": int2b(y[13]), "is_uncapped": int2b(y[4]),
"uncap_cores": [], "uncap_cores": server.character.get_char_core(c, y[1]),
"char_type": y[12], "char_type": y[22],
"skill_id_uncap": y[11], "skill_id_uncap": y[21],
"skill_requires_uncap": int2b(y[10]), "skill_requires_uncap": int2b(y[20]),
"skill_unlock_level": y[9], "skill_unlock_level": y[19],
"skill_id": y[8], "skill_id": y[18],
"overdrive": y[7], "overdrive": server.character.calc_char_value(y[2], y[11], y[14], y[17]),
"prog": y[6], "prog": server.character.calc_char_value(y[2], y[10], y[13], y[16]),
"frag": y[5], "frag": server.character.calc_char_value(y[2], y[9], y[12], y[15]),
"level_exp": y[4], "level_exp": server.character.LEVEL_STEPS[y[2]],
"exp": y[3], "exp": y[3],
"level": y[2], "level": y[2],
"name": char_name, "name": y[7],
"character_id": y[1] "character_id": y[1]
} }

View File

@@ -30,10 +30,11 @@ class Connect():
return True return True
class Sql(): class Sql():
@staticmethod @staticmethod
def select(c, table_name, target_column=[], limit=-1, offset=0, query={}, sort=[]): def select(c, table_name, target_column=[], query=None):
# 执行查询单句sql语句返回fetchall数据 # 执行查询单句sql语句返回fetchall数据
# 使用准确查询,且在单表内 # 使用准确查询,且在单表内
@@ -51,9 +52,10 @@ class Sql():
where_field = [] where_field = []
where_value = [] where_value = []
for i in query: if query:
where_field.append(i) for i in query.query:
where_value.append(query[i]) where_field.append(i)
where_value.append(query.query[i])
if where_field and where_value: if where_field and where_value:
sql += ' where ' sql += ' where '
@@ -64,16 +66,17 @@ class Sql():
sql_dict[where_field[i]] = where_value[i] sql_dict[where_field[i]] = where_value[i]
sql += ' and ' + where_field[i] + '=:' + where_field[i] sql += ' and ' + where_field[i] + '=:' + where_field[i]
if sort: if query and query.sort:
sql += ' order by ' + sort[0]['column'] + ' ' + sort[0]['order'] sql += ' order by ' + \
if len(sort) >= 2: query.sort[0]['column'] + ' ' + query.sort[0]['order']
for i in range(1, len(sort)): for i in range(1, len(query.sort)):
sql += ', ' + sort[i]['column'] + ' ' + sort[i]['order'] sql += ', ' + query.sort[i]['column'] + \
' ' + query.sort[i]['order']
if limit >= 0: if query and query.limit >= 0:
sql += ' limit :limit offset :offset' sql += ' limit :limit offset :offset'
sql_dict['limit'] = limit sql_dict['limit'] = query.limit
sql_dict['offset'] = offset sql_dict['offset'] = query.offset
c.execute(sql, sql_dict) c.execute(sql, sql_dict)

View File

@@ -8,7 +8,7 @@ class Config():
主机的地址和端口号 主机的地址和端口号
Host and port of your server Host and port of your server
''' '''
HOST = '192.168.1.101' HOST = '0.0.0.0'
PORT = '80' PORT = '80'
''' '''
-------------------- --------------------
@@ -19,7 +19,7 @@ class Config():
游戏API地址前缀 游戏API地址前缀
Game API's URL prefix Game API's URL prefix
''' '''
GAME_API_PREFIX = '/blockchain/14' GAME_API_PREFIX = '/earlgrey/16'
''' '''
-------------------- --------------------
''' '''
@@ -31,7 +31,7 @@ class Config():
If it is blank, all are allowed. If it is blank, all are allowed.
''' '''
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', ALLOW_APPVERSION = ['3.5.3', '3.5.3c',
'3.6.0', '3.6.0c', '3.6.1', '3.6.1c'] '3.9.0', '3.9.0c', '3.9.1', '3.9.1c']
''' '''
-------------------- --------------------
''' '''
@@ -59,6 +59,26 @@ class Config():
-------------------- --------------------
''' '''
'''
--------------------
世界排名的最大显示数量
The largest number of global rank
'''
WORLD_RANK_MAX = 200
'''
--------------------
'''
'''
--------------------
世界模式当前活动图设置
Current available maps in world mode
'''
AVAILABLE_MAP = [] # Ex. ['test', 'test2']
'''
--------------------
'''
''' '''
-------------------- --------------------
Web后台管理页面的用户名和密码 Web后台管理页面的用户名和密码
@@ -126,10 +146,17 @@ class Config():
LOGIN_DEVICE_NUMBER_LIMIT = 1 LOGIN_DEVICE_NUMBER_LIMIT = 1
''' '''
是否允许同设备多应用共存登录 是否允许同设备多应用共存登录
请注意这个选项设置为True时下一个选项将自动变为False
If logging in from multiple applications on the same device is allowed If logging in from multiple applications on the same device is allowed
Note that when this option is set to True, the next option automatically becomes False
''' '''
ALLOW_LOGIN_SAME_DEVICE = False ALLOW_LOGIN_SAME_DEVICE = False
''' '''
24小时内登陆设备数超过最大允许设备数量时是否自动封号1天、3天、7天、15天、31天
When the number of login devices exceeds the maximum number of devices allowed to log in Arcaea within 24 hours, whether the account will be automatically banned (1 day, 3 days, 7 days, 15 days, 31 days)
'''
ALLOW_BAN_MULTIDEVICE_USER_AUTO = True
'''
-------------------- --------------------
''' '''
@@ -153,6 +180,48 @@ class Config():
-------------------- --------------------
''' '''
'''
--------------------
数据库更新时,是否采用最新的角色数据,如果你想采用最新的官方角色数据
注意:如果是,旧的数据将丢失;如果否,某些角色的数据变动将无法同步
If using the latest character data when updating database. If you want to only keep newest official character data, please set it `True`.
Note: If `True`, the old data will be lost; If `False`, the data changes of some characters will not be synchronized.
'''
UPDATE_WITH_NEW_CHARACTER_DATA = True
'''
--------------------
'''
'''
--------------------
是否全解锁搭档
If unlocking all partners is enabled
'''
CHARACTER_FULL_UNLOCK = True
'''
--------------------
'''
'''
--------------------
是否全解锁世界歌曲
If unlocking all world songs is enabled
'''
WORLD_SONG_FULL_UNLOCK = True
'''
--------------------
'''
'''
--------------------
是否全解锁世界场景
If unlocking all world scenerys is enabled
'''
WORLD_SCENERY_FULL_UNLOCK = True
'''
--------------------
'''
''' '''
-------------------- --------------------
是否强制使用全解锁云端存档 是否强制使用全解锁云端存档
@@ -162,3 +231,13 @@ class Config():
''' '''
-------------------- --------------------
''' '''
'''
--------------------
是否使用最好的 10 条记录(而不是最近的 30 条记录中较好的 10 条)来计算 PTT
Calculate PTT with best 10 instead of recent best 10
'''
USE_B10_AS_R10 = False
'''
--------------------
'''

View File

@@ -16,26 +16,54 @@
<span class="char-name">{{char['name']}}</span> <span class="char-name">{{char['name']}}</span>
<br /> <br />
<span>Level: </span> <span>Max level: </span>
<span class="char-num">{{char['level']}}</span> <span class="char-num">{{char['max_level']}}</span>
<br /> <br />
<span>Frag: </span> <span>Frag-level 1: </span>
<span class="char-num">{{char['frag']}}</span> <span class="char-num">{{char['frag1']}}</span>
<br /> <br />
<span>Prog: </span> <span>Prog-level 1: </span>
<span class="char-num">{{char['prog']}}</span> <span class="char-num">{{char['prog1']}}</span>
<br /> <br />
<span>Overdrive: </span> <span>Overdrive-level 1: </span>
<span class="char-num">{{char['overdrive']}}</span> <span class="char-num">{{char['overdrive1']}}</span>
<br />
<span>Frag-level 20: </span>
<span class="char-num">{{char['frag20']}}</span>
<br />
<span>Prog-level 20: </span>
<span class="char-num">{{char['prog20']}}</span>
<br />
<span>Overdrive-level 20: </span>
<span class="char-num">{{char['overdrive20']}}</span>
<br />
<span>Frag-level 30: </span>
<span class="char-num">{{char['frag30']}}</span>
<br />
<span>Prog-level 30: </span>
<span class="char-num">{{char['prog30']}}</span>
<br />
<span>Overdrive-level 30: </span>
<span class="char-num">{{char['overdrive30']}}</span>
<br /> <br />
<span>Skill id: </span> <span>Skill id: </span>
<span class="char-num">{{char['skill_id']}}</span> <span class="char-num">{{char['skill_id']}}</span>
<br /> <br />
<span>Skill unlock level: </span>
<span class="char-num">{{char['skill_unlock_level']}}</span>
<br />
<span>Uncapped skill id: </span> <span>Uncapped skill id: </span>
<span class="char-num">{{char['skill_id_uncap']}}</span> <span class="char-num">{{char['skill_id_uncap']}}</span>
<br /> <br />

View File

@@ -20,24 +20,6 @@
<span class="char-num">{{item['is_available']}}</span> <span class="char-num">{{item['is_available']}}</span>
<br /> <br />
<span>Price: </span>
<span class="char-num">{{item['price']}}</span>
<br />
<span>Original price: </span>
<span class="char-num">{{item['orig_price']}}</span>
<br />
<span>Discount from: </span>
<span class="char-num">{{item['discount_from']}}</span>
<br />
<span>Discount to: </span>
<span class="char-num">{{item['discount_to']}}</span>
<br />
</div> </div>
{% if not loop.last %} {% if not loop.last %}

View File

@@ -20,10 +20,28 @@
<span class="char-num">{{present['description']}}</span> <span class="char-num">{{present['description']}}</span>
<br /> <br />
<span>Items: </span> {% if present['items'] %}<br />
<span class="char-num" style='white-space:pre'>{{present['items']}}</span> {% for x in present['items'] %}
<span>Item id: </span>
<span class="char-num">{{x['item_id']}}</span>
<br /> <br />
<span>Item type: </span>
<span class="char-num">{{x['type']}}</span>
<br />
<span>Amount: </span>
<span class="char-num">{{x['amount']}}</span>
<br />
{% if not loop.last %}
<br />
{% endif %}
{% endfor %}
{% endif %}
</div> </div>
{% if not loop.last %} {% if not loop.last %}

View File

@@ -0,0 +1,61 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}All purchases{% endblock %}</h1>
{% endblock %}
{% block content %}
{% if posts %}<br />
{% for item in posts %}
<div class="char-item">
<span>Name: </span>
<span class="name">{{item['purchase_name']}}</span>
<br />
<span>Price: </span>
<span class="char-num">{{item['price']}}</span>
<br />
<span>Original price: </span>
<span class="char-num">{{item['orig_price']}}</span>
<br />
<span>Discount from: </span>
<span class="char-num">{{item['discount_from']}}</span>
<br />
<span>Discount to: </span>
<span class="char-num">{{item['discount_to']}}</span>
<br />
{% if item['items'] %}<br />
{% for x in item['items'] %}
<span>Item id: </span>
<span class="char-num">{{x['item_id']}}</span>
<br />
<span>Item type: </span>
<span class="char-num">{{x['type']}}</span>
<br />
<span>Amount: </span>
<span class="char-num">{{x['amount']}}</span>
<br />
{% if not loop.last %}
<br />
{% endif %}
{% endfor %}
{% endif %}
</div>
{% if not loop.last %}
<br />
<hr />
<br />
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}

View File

@@ -22,9 +22,28 @@
</span> </span>
<br /> <br />
<span>Items: </span> {% if redeem['items'] %}<br />
<span class="char-num" style='white-space:pre'>{{redeem['items']}}</span> {% for x in redeem['items'] %}
<span>Item id: </span>
<span class="char-num">{{x['item_id']}}</span>
<br /> <br />
<span>Item type: </span>
<span class="char-num">{{x['type']}}</span>
<br />
<span>Amount: </span>
<span class="char-num">{{x['amount']}}</span>
<br />
{% if not loop.last %}
<br />
{% endif %}
{% endfor %}
{% endif %}
<div><a href="/web/redeem/{{redeem['code']}}">使用者 Players who have used it.</a></div> <div><a href="/web/redeem/{{redeem['code']}}">使用者 Players who have used it.</a></div>
</div> </div>

View File

@@ -12,15 +12,6 @@
<label for="level">Level</label> <label for="level">Level</label>
<input name="level" id="level"> <input name="level" id="level">
<label for="frag">Frag</label>
<input name="frag" id="frag">
<label for="prog">Prog</label>
<input name="prog" id="prog">
<label for="overdrive">Overdrive</label>
<input name="overdrive" id="overdrive">
<div> <div>
<select name="skill_id"> <select name="skill_id">
<option value="">Skill id</option> <option value="">Skill id</option>

View File

@@ -5,37 +5,43 @@
{% block content %} {% block content %}
<form action="/web/changeitem" method="post"> <form action="/web/changeitem" method="post">
<div class="title">Change the item</div> <div class="title">Add the item</div>
<label for="item_id">ID of the item</label> <label for="item_id">ID of the item</label>
<input name="item_id" id="item_id" required> <input name="item_id" id="item_id" required>
<label for="type">Type</label> <label for="type">Type</label>
<div> <div>
<select name="type"> <select name="type">
<option value='single'>Single</option> <option value='single' selected>Single</option>
<option value='pack'>Pack</option> <option value='pack'>Pack</option>
<option value='world_song'>World song</option>
<option value='character'>Character</option>
</select> </select>
</div> </div>
<br /> <br />
<div class="content">可修改项 Modifiable items:</div>
<div><label for="is_available">是否可用 Is available:</label> <div><label for="is_available">是否可用 Is available:</label>
<br /> <br />
<br /> <br />
<label><input type="radio" name="is_available" value=1>True</label> <label><input type="radio" name="is_available" checked value=1>True</label>
<br /> <br />
<label><input type="radio" name="is_available" value=0>False</label> <label><input type="radio" name="is_available" value=0>False</label>
</div> </div>
<label for="price">Price</label> <br />
<input name="price" id="price"> <input type="submit" value="Add">
<label for="orig_price">Original price</label> </form>
<input name="orig_price" id="orig_price"> <form action="/web/changeitem/delete" method="post">
<label for="discount_from">Discount from</label> <div class="title">Delete the item</div>
<input type="datetime-local" name="discount_from" id="discount_from"> <label for="item_id">ID of the item</label>
<label for="discount_to">Discount to</label> <input name="item_id" id="item_id" required>
<input type="datetime-local" name="discount_to" id="discount_to"> <label for="type">Type</label>
<div class="content">如果某些不需要修改,留空即可</div> <div>
<div class="content">If some things do not need to be modified, leave it blank.</div> <select name="type">
<div class="content">时间填写是一个HTML5控件</div> <option value='single' selected>Single</option>
<div class="content">Time filling is an HTML5 control.</div> <option value='pack'>Pack</option>
<input type="submit" value="Edit"> <option value='world_song'>World song</option>
<option value='character'>Character</option>
</select>
</div>
<br />
<input type="submit" value="Delete">
</form> </form>
{% endblock %} {% endblock %}

View File

@@ -14,15 +14,25 @@
<label for="expire_ts">Expire time</label> <label for="expire_ts">Expire time</label>
<input type="datetime-local" name="expire_ts" id="expire_ts" required> <input type="datetime-local" name="expire_ts" id="expire_ts" required>
<br /> <br />
<div>Items:</div>
<label for="fragment">Fragments</label>
<input name="fragment" id="fragment">
<label for="ticket">Memories</label>
<input name="ticket" id="ticket">
<div class="content">两种奖励只能有一个,另一个请留空。如果都填写,以记忆源点优先。</div> <label for="item_id">ID of the item</label>
<div class="content">There can only be one of the two kinds of present. Please leave the other one blank. If you <input name="item_id" id="item_id" required>
fill both, the memories will be given priority.</div> <label for="type">Type</label>
<div>
<select name="type">
<option value='single' selected>Single song</option>
<option value='world_song'>World song</option>
<option value='world_unlock'>World unlock scene</option>
<option value='character'>Character</option>
<option value='core'>Character core</option>
<option value='fragment'>Fragment</option>
<option value='memory'>Memory</option>
</select>
</div>
<label for="amount">Amount</label>
<input name="amount" id="amount" value="1" required>
<br />
<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>
<input type="submit" value="Add"> <input type="submit" value="Add">

View File

@@ -0,0 +1,32 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Change the purchases{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="/web/changepurchase" method="post">
<div class="title">Add the purchase</div>
<label for="purchase_name">Purchase name</label>
<input name="purchase_name" id="purchase_name" required>
<label for="price">Price</label>
<input name="price" id="price">
<label for="orig_price">Original price</label>
<input name="orig_price" id="orig_price">
<label for="discount_from">Discount from</label>
<input type="datetime-local" name="discount_from" id="discount_from">
<label for="discount_to">Discount to</label>
<input type="datetime-local" name="discount_to" id="discount_to">
<div class="content">时间填写是一个HTML5控件</div>
<div class="content">Time filling is an HTML5 control.</div>
<br />
<input type="submit" value="Add">
</form>
<form action="/web/changepurchase/delete" method="post">
<div class="title">Delete the purchase</div>
<label for="purchase_name">Purchase name</label>
<input name="purchase_name" id="purchase_name" required>
<br />
<input type="submit" value="Delete">
</form>
{% endblock %}

View File

@@ -0,0 +1,48 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Change the purchase's items{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="/web/changepurchaseitem" method="post">
<div class="title">Add the purchase's item</div>
<label for="purchase_name">Purchase name</label>
<input name="purchase_name" id="purchase_name" required>
<label for="item_id">ID of the item</label>
<input name="item_id" id="item_id" required>
<label for="type">Type</label>
<div>
<select name="type">
<option value='single' selected>Single song</option>
<option value='pack'>Song pack</option>
<option value='world_song'>World song</option>
<option value='character'>Character</option>
<option value='core'>Core</option>
</select>
</div>
<label for="amount">Amount</label>
<input name="amount" id="amount" value="1" required>
<br />
<input type="submit" value="Add">
</form>
<form action="/web/changepurchaseitem/delete" method="post">
<div class="title">Delete the purchase's item</div>
<label for="purchase_name">Purchase name</label>
<input name="purchase_name" id="purchase_name" required>
<label for="item_id">ID of the item</label>
<input name="item_id" id="item_id" required>
<label for="type">Type</label>
<div>
<select name="type">
<option value='single' selected>Single</option>
<option value='pack'>Pack</option>
<option value='world_song'>World song</option>
<option value='character'>Character</option>
<option value='core'>Core</option>
</select>
</div>
<br />
<input type="submit" value="Delete">
</form>
{% endblock %}

View File

@@ -11,27 +11,40 @@
<input name="code" id="code"> <input name="code" id="code">
<div>Or</div> <div>Or</div>
<div>随机批量生成 Batch generation at random</div> <div>随机批量生成 Batch generation at random</div>
<label for="amount">Amount</label> <label for="redeem_amount">Redeem code amount</label>
<input name="amount" id="amount"> <input name="redeem_amount" id="redeem_amount">
<br /> <br />
<div>Type: <div>Redeem code type:
<br /> <br />
<br /> <br />
<label><input type="radio" name="type" value="0" checked>仅可使用一次 It can only be used once.</label> <label><input type="radio" name="redeem_type" value="0" checked>仅可使用一次 It can only be used once.</label>
<br /> <br />
<label><input type="radio" name="type" value="1">每个用户仅可以使用一次 Each user can only use it once.</label> <label><input type="radio" name="redeem_type" value="1">每个用户仅可以使用一次 Each user can only use it once.</label>
</div> </div>
<br /> <br />
<div>Items:</div>
<label for="fragment">Fragments</label>
<input name="fragment" id="fragment">
<label for="ticket">Memories</label>
<input name="ticket" id="ticket">
<div class="content">两种奖励可以都有,不需要请留空。</div> <label for="item_id">ID of the item</label>
<div class="content">You can have both kinds of items. Please leave it blank if you don't need it.</div> <input name="item_id" id="item_id" required>
<label for="type">Type</label>
<div>
<select name="type">
<option value='single' selected>Single song</option>
<option value='pack'>Song pack</option>
<option value='world_song'>World song</option>
<option value='world_unlock'>World unlock scene</option>
<option value='character'>Character</option>
<option value='core'>Character core</option>
<option value='fragment'>Fragment</option>
<option value='memory'>Memory</option>
</select>
</div>
<label for="amount">Amount</label>
<input name="amount" id="amount" value="1" required>
<br />
<div class="content">兑换码长度为10~20。</div> <div class="content">兑换码长度为10~20。</div>
<div class="content">The length of redeem code is 10 ~ 20.</div> <div class="content">The length of redeem code is 10 ~ 20.</div>
<div class="content">随机生成数量不得超过1000。</div> <div class="content">随机生成数量不得超过1000。</div>

View File

@@ -0,0 +1,38 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Change the scores{% endblock %}</h1>
{% endblock %}
{% block content %}
<form action="/web/changescore/delete" method="post">
<div class="title">Delete the scores</div>
<label for="sid">SID of the song</label>
<input name="sid" id="sid">
<label for="difficulty">Difficulty</label>
<select name='difficulty' id='difficulty'>
<option value="-1" selected>All</option>
<option value="0">Past</option>
<option value="1">Present</option>
<option value="2">Future</option>
<option value="3">Beyond</option>
</select>
<br />
<label for="name">Arcaea Username</label>
<input name="name" id="name">
or<br />
<label for="user_code">Arcaea User Code</label>
<input name="user_code" id="user_code">
<br />
<br />
<div class="content">上述三项是查询参数,如果留空,则被认为是全部,都不填则认为是错误的。</div>
<div class="content">The above three are query parameters. If they are left blank, they are considered all. However,
if you don't fill in all of them, it's wrong.</div>
<br />
<br />
<input type="submit" value="Delete">
</form>
{% endblock %}

View File

@@ -15,7 +15,8 @@
<a href="{{ url_for('index.all_player') }}">所有玩家信息查询 All players</a></br></br> <a href="{{ url_for('index.all_player') }}">所有玩家信息查询 All players</a></br></br>
<a href="{{ url_for('index.all_song') }}">谱面信息查询 All songs</a></br></br> <a href="{{ url_for('index.all_song') }}">谱面信息查询 All songs</a></br></br>
<a href="{{ url_for('index.all_character') }}">角色信息查询 All characters</a></br></br> <a href="{{ url_for('index.all_character') }}">角色信息查询 All characters</a></br></br>
<a href="{{ url_for('index.all_item') }}">购买信息查询 All items</a></br></br> <a href="{{ url_for('index.all_item') }}">物品信息查询 All items</a></br></br>
<a href="{{ url_for('index.all_purchase') }}">购买信息查询 All purchases</a></br></br>
<a href="{{ url_for('index.all_present') }}">奖励信息查询 All presents</a></br></br> <a href="{{ url_for('index.all_present') }}">奖励信息查询 All presents</a></br></br>
<a href="{{ url_for('index.all_redeem') }}">兑换码信息查询 All redeem codes</a></br></br> <a href="{{ url_for('index.all_redeem') }}">兑换码信息查询 All redeem codes</a></br></br>
<a href="{{ url_for('index.single_chart_top') }}">单个铺面排行榜查询 Single song chart tops</a> <a href="{{ url_for('index.single_chart_top') }}">单个铺面排行榜查询 Single song chart tops</a>
@@ -24,10 +25,13 @@
<a href="{{ url_for('index.update_database') }}">数据库更新 Update databases</a></br></br> <a href="{{ url_for('index.update_database') }}">数据库更新 Update databases</a></br></br>
<a href="{{ url_for('index.edit_userpwd') }}">用户密码修改 Change user password</a></br></br> <a href="{{ url_for('index.edit_userpwd') }}">用户密码修改 Change user password</a></br></br>
<a href="{{ url_for('index.ban_user') }}">封禁用户 Ban the user</a></br></br> <a href="{{ url_for('index.ban_user') }}">封禁用户 Ban the user</a></br></br>
<a href="{{ url_for('index.change_score') }}">成绩删除 Delete the scores</a></br></br>
<a href="{{ url_for('index.update_user_save') }}">用户存档更新 Update user saves</a></br></br> <a href="{{ url_for('index.update_user_save') }}">用户存档更新 Update user saves</a></br></br>
<a href="{{ url_for('index.change_song') }}">歌曲修改 Change the songs</a></br></br> <a href="{{ url_for('index.change_song') }}">歌曲修改 Change the songs</a></br></br>
<a href="{{ url_for('index.change_character') }}">角色修改 Change the characters</a></br></br> <a href="{{ url_for('index.change_character') }}">角色修改 Change the characters</a></br></br>
<a href="{{ url_for('index.change_item') }}">购买信息修改 Change the items</a></br></br> <a href="{{ url_for('index.change_item') }}">物品信息修改 Change the items</a></br></br>
<a href="{{ url_for('index.change_purchase') }}">购买信息修改 Change the purchases</a></br></br>
<a href="{{ url_for('index.change_purchase_item') }}">购买信息的物品修改 Change the purchase items</a></br></br>
<a href="{{ url_for('index.change_user') }}">用户信息修改 Change user information</a></br></br> <a href="{{ url_for('index.change_user') }}">用户信息修改 Change user information</a></br></br>
<a href="{{ url_for('index.change_user_purchase') }}">用户购买信息修改 Change user purchase information</a></br></br> <a href="{{ url_for('index.change_user_purchase') }}">用户购买信息修改 Change user purchase information</a></br></br>
<a href="{{ url_for('index.change_present') }}">奖励修改 Change the presents</a></br></br> <a href="{{ url_for('index.change_present') }}">奖励修改 Change the presents</a></br></br>

View File

@@ -10,10 +10,10 @@
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
<div class="content"> <div class="content">
这里可以将旧版本的数据库同步到新版本的数据库,并刷新用户拥有的角色列表。<br /> 这里可以将旧版本的数据库同步到新版本的数据库,并刷新用户拥有的角色列表。<br />
可上传文件: arcaea_database.db和arcsong.db<br /> 可上传文件: arcaea_database.db和arcsong.db<br />
新数据库不存在的数据会被添加,存在的重复数据也会被改变。<br /><br /> 新数据库不存在的数据会被添加,存在的重复数据也会被改变。<br /><br />
Here you can synchronize the old version of the database to the new version of the database and refresh the list of Here you can synchronize the old version of the database to the new version of the database and refresh the list of full
characters owned by players.<br /> characters owned by players.<br />
Uploadable files: arcaea_database.db & arcsong.db<br /> Uploadable files: arcaea_database.db & arcsong.db<br />
Data that does not exist in the new database will be added and the existing duplicate data will also be changed. Data that does not exist in the new database will be added and the existing duplicate data will also be changed.

View File

@@ -1,5 +1,5 @@
from flask import ( from flask import (
Blueprint, flash, g, redirect, render_template, request, url_for Blueprint, flash, redirect, render_template, request, url_for
) )
from web.login import login_required from web.login import login_required
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
@@ -192,10 +192,10 @@ def all_song():
for i in x: for i in x:
posts.append({'song_id': i[0], posts.append({'song_id': i[0],
'name_en': i[1], 'name_en': i[1],
'rating_pst': defnum(i[12]), 'rating_pst': defnum(i[13]),
'rating_prs': defnum(i[13]), 'rating_prs': defnum(i[14]),
'rating_ftr': defnum(i[14]), 'rating_ftr': defnum(i[15]),
'rating_byn': defnum(i[15]) 'rating_byn': defnum(i[16])
}) })
else: else:
error = '没有铺面数据 No song data.' error = '没有铺面数据 No song data.'
@@ -387,14 +387,21 @@ def all_character():
for i in x: for i in x:
posts.append({'character_id': i[0], posts.append({'character_id': i[0],
'name': i[1], 'name': i[1],
'level': i[2], 'max_level': i[2],
'frag': i[5], 'frag1': i[3],
'prog': i[6], 'prog1': i[4],
'overdrive': i[7], 'overdrive1': i[5],
'skill_id': i[8], 'frag20': i[6],
'skill_id_uncap': i[11], 'prog20': i[7],
'char_type': i[12], 'overdrive20': i[8],
'is_uncapped': i[14] == 1 'frag30': i[9],
'prog30': i[10],
'overdrive30': i[11],
'skill_id': i[12],
'skill_unlock_level': i[13],
'skill_id_uncap': i[15],
'char_type': i[16],
'is_uncapped': i[17] == 1
}) })
else: else:
error = '没有角色数据 No character data.' error = '没有角色数据 No character data.'
@@ -411,7 +418,7 @@ def all_character():
def change_character(): def change_character():
# 修改角色数据 # 修改角色数据
skill_ids = ['No_skill', 'gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere', skill_ids = ['No_skill', 'gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange'] 'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap']
return render_template('web/changechar.html', skill_ids=skill_ids) return render_template('web/changechar.html', skill_ids=skill_ids)
@@ -425,27 +432,12 @@ def edit_char():
try: try:
character_id = int(request.form['id']) character_id = int(request.form['id'])
level = request.form['level'] level = request.form['level']
frag = request.form['frag']
prog = request.form['prog']
overdrive = request.form['overdrive']
skill_id = request.form['skill_id'] skill_id = request.form['skill_id']
skill_id_uncap = request.form['skill_id_uncap'] skill_id_uncap = request.form['skill_id_uncap']
if level: if level:
level = int(level) level = int(level)
else: else:
level = None level = None
if frag:
frag = float(frag)
else:
frag = None
if prog:
prog = float(prog)
else:
prog = None
if overdrive:
overdrive = float(overdrive)
else:
overdrive = None
except: except:
error = '数据错误 Wrong data.' error = '数据错误 Wrong data.'
flash(error) flash(error)
@@ -455,36 +447,28 @@ def edit_char():
c.execute( c.execute(
'''select exists(select * from character where character_id=:a)''', {'a': character_id}) '''select exists(select * from character where character_id=:a)''', {'a': character_id})
if c.fetchone() == (1,): if c.fetchone() == (1,):
if level is None and frag is None and prog is None and overdrive is None and skill_id is None and skill_id_uncap is None: if level is None and skill_id is None and skill_id_uncap is None:
error = '无修改 No change.' error = '无修改 No change.'
else: else:
sql = '''update character set '''
sql = '''update character set level_exp=25000'''
sql_dict = {'character_id': character_id} sql_dict = {'character_id': character_id}
if level is not None: if level is not None:
sql += ', level = :level' sql += 'max_level = :level, '
sql_dict['level'] = level sql_dict['level'] = level
if frag is not None:
sql += ', frag = :frag'
sql_dict['frag'] = frag
if prog is not None:
sql += ', prog = :prog'
sql_dict['prog'] = prog
if overdrive is not None:
sql += ', overdrive = :overdrive'
sql_dict['overdrive'] = overdrive
if skill_id is not None: if skill_id is not None:
sql += ', skill_id = :skill_id' sql += 'skill_id = :skill_id, '
if skill_id == 'No_skill': if skill_id == 'No_skill':
sql_dict['skill_id'] = '' sql_dict['skill_id'] = ''
else: else:
sql_dict['skill_id'] = skill_id sql_dict['skill_id'] = skill_id
if skill_id_uncap is not None: if skill_id_uncap is not None:
sql += ', skill_id_uncap = :skill_id_uncap' sql += 'skill_id_uncap = :skill_id_uncap, '
if skill_id_uncap == 'No_skill': if skill_id_uncap == 'No_skill':
sql_dict['skill_id_uncap'] = '' sql_dict['skill_id_uncap'] = ''
else: else:
sql_dict['skill_id_uncap'] = skill_id_uncap sql_dict['skill_id_uncap'] = skill_id_uncap
sql = sql[:-2]
sql += ' where character_id = :character_id' sql += ' where character_id = :character_id'
c.execute(sql, sql_dict) c.execute(sql, sql_dict)
flash('角色修改成功 Successfully edit the character.') flash('角色修改成功 Successfully edit the character.')
@@ -620,7 +604,8 @@ def edit_user_purchase():
if method == '0': if method == '0':
web.system.unlock_all_user_item(c) web.system.unlock_all_user_item(c)
else: else:
c.execute('''delete from user_item''') c.execute(
'''delete from user_item where type in ('pack', 'single')''')
flash("全部用户购买信息修改成功 Successfully edit all the users' purchase information.") flash("全部用户购买信息修改成功 Successfully edit all the users' purchase information.")
@@ -645,8 +630,8 @@ def edit_user_purchase():
if method == '0': if method == '0':
web.system.unlock_user_item(c, user_id) web.system.unlock_user_item(c, user_id)
else: else:
c.execute('''delete from user_item where user_id=:a''', { c.execute('''delete from user_item where type in ('pack', 'single') and user_id = :user_id''', {
'a': user_id}) 'user_id': user_id})
flash('用户购买信息修改成功 Successfully edit the user purchase information.') flash('用户购买信息修改成功 Successfully edit the user purchase information.')
else: else:
@@ -665,16 +650,16 @@ def edit_user_purchase():
@bp.route('/allitem', methods=['GET']) @bp.route('/allitem', methods=['GET'])
@login_required @login_required
def all_item(): def all_item():
# 所有购买数据 # 所有物品数据
error = None error = None
posts = web.system.get_all_item() posts = web.system.get_all_item()
if not posts: if not posts:
error = '没有购买数据 No item data.' error = '没有物品数据 No item data.'
if error: if error:
flash(error) flash(error)
return redirect(url_for('index.all_item')) return render_template('web/allitem.html')
else: else:
return render_template('web/allitem.html', posts=posts) return render_template('web/allitem.html', posts=posts)
@@ -682,25 +667,103 @@ def all_item():
@bp.route('/changeitem', methods=['GET', 'POST']) @bp.route('/changeitem', methods=['GET', 'POST'])
@login_required @login_required
def change_item(): def change_item():
# 修改购买信息 # 添加物品信息
error = None error = None
if request.method == 'POST': if request.method == 'POST':
try: try:
item_id = request.form['item_id'] item_id = request.form['item_id']
item_type = request.form['type'] item_type = request.form['type']
price = request.form['price']
orig_price = request.form['orig_price']
discount_from = request.form['discount_from']
discount_to = request.form['discount_to']
try: try:
is_available = request.form['is_available'] is_available = request.form['is_available']
if is_available: if is_available:
is_available = int(is_available) is_available = int(is_available)
else: else:
is_available = None is_available = 0
except: except:
is_available = None is_available = 0
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_item'))
with Connect() as c:
c.execute(
'''select exists(select * from item where item_id=:a and type=:b)''', {'a': item_id, 'b': item_type})
if c.fetchone() == (0,):
c.execute('''insert into item values(?,?,?,'')''',
(item_id, item_type, is_available))
flash('物品添加成功 Successfully add the item.')
else:
error = '物品已存在 The item exists.'
if error:
flash(error)
return render_template('web/changeitem.html')
@bp.route('/changeitem/delete', methods=['POST'])
@login_required
def change_item_delete():
# 删除物品信息
error = None
try:
item_id = request.form['item_id']
item_type = request.form['type']
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_item'))
with Connect() as c:
c.execute(
'''select exists(select * from item where item_id=:a and type=:b)''', {'a': item_id, 'b': item_type})
if c.fetchone() == (1,):
c.execute('''delete from item where item_id=? and type=?''',
(item_id, item_type))
flash('物品删除成功 Successfully delete the item.')
else:
error = '物品不存在 The item does not exist.'
if error:
flash(error)
return render_template('web/changeitem.html')
@bp.route('/allpurchase', methods=['GET'])
@login_required
def all_purchase():
# 所有购买数据
error = None
posts = web.system.get_all_purchase()
if not posts:
error = '没有购买数据 No purchase data.'
if error:
flash(error)
return render_template('web/allpurchase.html')
else:
return render_template('web/allpurchase.html', posts=posts)
@bp.route('/changepurchase', methods=['GET', 'POST'])
@login_required
def change_purchase():
# 添加购买信息
error = None
if request.method == 'POST':
try:
purchase_name = request.form['purchase_name']
price = request.form['price']
orig_price = request.form['orig_price']
discount_from = request.form['discount_from']
discount_to = request.form['discount_to']
if price: if price:
price = int(price) price = int(price)
@@ -714,52 +777,138 @@ def change_item():
discount_from = int(time.mktime(time.strptime( discount_from = int(time.mktime(time.strptime(
discount_from, "%Y-%m-%dT%H:%M"))) * 1000 discount_from, "%Y-%m-%dT%H:%M"))) * 1000
else: else:
discount_from = None discount_from = -1
if discount_to: if discount_to:
discount_to = int(time.mktime(time.strptime( discount_to = int(time.mktime(time.strptime(
discount_to, "%Y-%m-%dT%H:%M"))) * 1000 discount_to, "%Y-%m-%dT%H:%M"))) * 1000
else: else:
discount_to = None discount_to = -1
except: except:
error = '数据错误 Wrong data.' error = '数据错误 Wrong data.'
flash(error) flash(error)
return redirect(url_for('index.change_item')) return redirect(url_for('index.change_purchase'))
with Connect() as c: with Connect() as c:
c.execute( c.execute(
'''select exists(select * from item where item_id=:a and type=:b)''', {'a': item_id, 'b': item_type}) '''select exists(select * from purchase where purchase_name=:a)''', {'a': purchase_name})
if c.fetchone() == (1,): if c.fetchone() == (0,):
if is_available is None and price is None and orig_price is None and not discount_from and not discount_to: c.execute('''insert into purchase values(?,?,?,?,?)''',
error = '无修改 No change.' (purchase_name, price, orig_price, discount_from, discount_to))
else:
sql = '''update item set type=:type'''
sql_dict = {'item_id': item_id, 'type': item_type}
if price is not None:
sql += ', price = :price'
sql_dict['price'] = price
if orig_price is not None:
sql += ', orig_price = :orig_price'
sql_dict['orig_price'] = orig_price
if discount_from is not None:
sql += ', discount_from = :discount_from'
sql_dict['discount_from'] = discount_from
if discount_to is not None:
sql += ', discount_to = :discount_to'
sql_dict['discount_to'] = discount_to
if is_available is not None:
sql += ', is_available = :is_available'
sql_dict['is_available'] = is_available
sql += ' where item_id = :item_id and type = :type' flash('购买项目添加成功 Successfully add the purchase.')
c.execute(sql, sql_dict)
flash('购买项目修改成功 Successfully edit the item.')
else: else:
error = '购买项目存在 The item does not exist.' error = '购买项目存在 The purchase exists.'
if error: if error:
flash(error) flash(error)
return render_template('web/changeitem.html') return render_template('web/changepurchase.html')
@bp.route('/changepurchase/delete', methods=['POST'])
@login_required
def change_purchase_delete():
# 删除购买信息
error = None
try:
purchase_name = request.form['purchase_name']
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_purchase'))
with Connect() as c:
c.execute(
'''select exists(select * from purchase where purchase_name=:a)''', {'a': purchase_name})
if c.fetchone() == (1,):
c.execute('''delete from purchase where purchase_name=?''',
(purchase_name,))
c.execute('''delete from purchase_item where purchase_name=?''',
(purchase_name,))
flash('购买信息删除成功 Successfully delete the purchase.')
else:
error = '购买信息不存在 The purchase does not exist.'
if error:
flash(error)
return render_template('web/changepurchase.html')
@bp.route('/changepurchaseitem', methods=['GET', 'POST'])
@login_required
def change_purchase_item():
# 添加购买的物品信息
error = None
if request.method == 'POST':
try:
purchase_name = request.form['purchase_name']
item_id = request.form['item_id']
item_type = request.form['type']
amount = int(request.form['amount'])
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_purchase_item'))
with Connect() as c:
c.execute(
'''select exists(select * from purchase_item where purchase_name=? and item_id=? and type=?)''', (purchase_name, item_id, item_type))
if c.fetchone() == (0,):
c.execute(
'''select exists(select * from purchase where purchase_name=?)''', (purchase_name,))
if c.fetchone() == (1,):
c.execute(
'''select exists(select * from item where item_id=? and type=?)''', (item_id, item_type))
if c.fetchone() == (1,):
c.execute('''insert into purchase_item values(?,?,?,?)''',
(purchase_name, item_id, item_type, amount))
flash('''购买项目的物品添加成功 Successfully add the purchase's item.''')
else:
error = '''物品不存在 The item does not exist.'''
else:
error = '''购买项目不存在 The purchase does not exist.'''
else:
error = '''购买项目的物品已存在 The purchase's item exists.'''
if error:
flash(error)
return render_template('web/changepurchaseitem.html')
@bp.route('/changepurchaseitem/delete', methods=['POST'])
@login_required
def change_purchase_item_delete():
# 删除购买的物品信息
error = None
try:
purchase_name = request.form['purchase_name']
item_id = request.form['item_id']
item_type = request.form['type']
except:
error = '数据错误 Wrong data.'
flash(error)
return redirect(url_for('index.change_purchase_item'))
with Connect() as c:
c.execute(
'''select exists(select * from purchase_item where purchase_name=? and item_id=? and type=?)''', (purchase_name, item_id, item_type))
if c.fetchone() == (1,):
c.execute('''delete from purchase_item where purchase_name=? and item_id=? and type=?''',
(purchase_name, item_id, item_type))
flash('''购买项目的物品删除成功 Successfully delete the purchase's item.''')
else:
error = '''购买项目的物品不存在 The purchase's item does not exist.'''
if error:
flash(error)
return render_template('web/changepurchaseitem.html')
@bp.route('/updateusersave', methods=['POST', 'GET']) @bp.route('/updateusersave', methods=['POST', 'GET'])
@@ -828,16 +977,19 @@ def all_present():
if x: if x:
posts = [] posts = []
for i in x: for i in x:
items = json.loads(i[2]) items = []
items_string = '' c.execute(
for j in items: '''select * from present_item where present_id=?''', (i[0],))
items_string = items_string + '\n' + \ y = c.fetchall()
str(j['id']) + ': ' + str(j['amount']) for j in y:
if j is not None:
items.append(
{'item_id': j[1], 'type': j[2], 'amount': j[3]})
posts.append({'present_id': i[0], posts.append({'present_id': i[0],
'expire_ts': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(i[1])//1000)), 'expire_ts': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(i[1])//1000)),
'items': items_string, 'items': items,
'description': i[3] 'description': i[2]
}) })
else: else:
error = '没有奖励数据 No present data.' error = '没有奖励数据 No present data.'
@@ -863,13 +1015,15 @@ def add_present():
present_id = request.form['present_id'] present_id = request.form['present_id']
expire_ts = request.form['expire_ts'] expire_ts = request.form['expire_ts']
description = request.form['description'] description = request.form['description']
fragment = request.form['fragment'] item_id = request.form['item_id']
ticket = request.form['ticket'] item_type = request.form['type']
item_amount = request.form['amount']
try: try:
if ticket: if item_amount:
ticket = int(ticket) item_amount = int(item_amount)
if fragment: else:
fragment = int(fragment) item_amount = 1
expire_ts = int(time.mktime(time.strptime( expire_ts = int(time.mktime(time.strptime(
expire_ts, "%Y-%m-%dT%H:%M"))) * 1000 expire_ts, "%Y-%m-%dT%H:%M"))) * 1000
except: except:
@@ -881,18 +1035,8 @@ def add_present():
if len(description) >= 256: if len(description) >= 256:
description = description[:200] description = description[:200]
items = []
if ticket:
items.append({'type': 'memory', 'id': 'memory', 'amount': ticket})
if fragment:
items.append(
{'type': 'fragment', 'id': 'fragment', 'amount': fragment})
if items == []:
flash('奖励为空 No items.')
return redirect(url_for('index.change_present'))
message = web.system.add_one_present( message = web.system.add_one_present(
present_id, expire_ts, description, json.dumps(items)) present_id, expire_ts, description, item_id, item_type, item_amount)
if message: if message:
flash(message) flash(message)
@@ -981,15 +1125,18 @@ def all_redeem():
if x: if x:
posts = [] posts = []
for i in x: for i in x:
items = json.loads(i[1]) items = []
items_string = '' c.execute(
for j in items: '''select * from redeem_item where code=?''', (i[0],))
items_string = items_string + '\n' + \ y = c.fetchall()
str(j['id']) + ': ' + str(j['amount']) for j in y:
if j is not None:
items.append(
{'item_id': j[1], 'type': j[2], 'amount': j[3]})
posts.append({'code': i[0], posts.append({'code': i[0],
'items': items_string, 'items': items,
'type': i[2] 'type': i[1]
}) })
else: else:
error = '没有兑换码数据 No redeem code data.' error = '没有兑换码数据 No redeem code data.'
@@ -1012,48 +1159,39 @@ def change_redeem():
@login_required @login_required
def add_redeem(): def add_redeem():
# 添加兑换码数据 # 添加兑换码数据
print(request.form)
code = request.form['code'] code = request.form['code']
amount = request.form['amount'] redeem_amount = request.form['redeem_amount']
redeem_type = request.form['type'] redeem_type = request.form['redeem_type']
fragment = request.form['fragment'] item_id = request.form['item_id']
ticket = request.form['ticket'] item_type = request.form['type']
item_amount = request.form['amount']
try: try:
if amount: if item_amount:
amount = int(amount) item_amount = int(item_amount)
if ticket: else:
ticket = int(ticket) item_amount = 1
if fragment: if redeem_amount:
fragment = int(fragment) redeem_amount = int(redeem_amount)
except: except:
flash('数据错误 Wrong data.') flash('数据错误 Wrong data.')
return redirect(url_for('index.change_redeem')) return redirect(url_for('index.change_redeem'))
items = [] if code and not redeem_amount:
if ticket:
items.append({'type': 'memory', 'id': 'memory', 'amount': ticket})
if fragment:
items.append(
{'type': 'fragment', 'id': 'fragment', 'amount': fragment})
if items == []:
flash('奖励为空 No items.')
return redirect(url_for('index.change_redeem'))
if code and not amount:
if len(code) > 20 or len(code) < 10: if len(code) > 20 or len(code) < 10:
flash('兑换码长度不合适 Inappropriate length of redeem code.') flash('兑换码长度不合适 Inappropriate length of redeem code.')
return redirect(url_for('index.change_redeem')) return redirect(url_for('index.change_redeem'))
message = web.system.add_one_redeem( message = web.system.add_one_redeem(
code, redeem_type, json.dumps(items)) code, redeem_type, item_id, item_type, item_amount)
elif amount and not code: elif redeem_amount and not code:
if amount <= 0 or amount > 1000: if redeem_amount <= 0 or redeem_amount > 1000:
flash('数量错误 Wrong amount.') flash('数量错误 Wrong amount.')
return redirect(url_for('index.change_redeem')) return redirect(url_for('index.change_redeem'))
message = web.system.add_some_random_redeem( message = web.system.add_some_random_redeem(
amount, redeem_type, json.dumps(items)) redeem_amount, redeem_type, item_id, item_type, item_amount)
elif amount and code: elif redeem_amount and code:
flash('只能使用一种添加方式 Only one add method can be used.') flash('只能使用一种添加方式 Only one add method can be used.')
return redirect(url_for('index.change_redeem')) return redirect(url_for('index.change_redeem'))
else: else:
@@ -1228,3 +1366,73 @@ def delete_user_score():
flash(error) flash(error)
return redirect(url_for('index.ban_user')) return redirect(url_for('index.ban_user'))
@bp.route('/changescore', methods=['GET'])
@login_required
def change_score():
# 修改成绩页面
return render_template('web/changescore.html')
@bp.route('/changescore/delete', methods=['POST'])
@login_required
def delete_score():
# 删除成绩
song_id = request.form['sid']
difficulty = request.form['difficulty']
if difficulty.isdigit() or difficulty == '-1':
difficulty = int(difficulty)
name = request.form['name']
user_code = request.form['user_code']
error = 'Unknown error.'
flag = [False, False, False]
if song_id:
flag[0] = True
if difficulty != -1:
flag[1] = True
with Connect() as c:
if name or user_code:
if user_code:
c.execute('''select user_id from user where user_code=:a''', {
'a': user_code})
else:
c.execute(
'''select user_id from user where name=:a''', {'a': name})
user_id = c.fetchone()
if user_id:
user_id = user_id[0]
flag[2] = True
else:
error = '玩家不存在 The player does not exist.'
if flag[0] or flag[1] or flag[2]:
sql = '''delete from best_score where '''
sql_dict = {}
if flag[0]:
sql += 'song_id=:song_id and '
sql_dict['song_id'] = song_id
if flag[1]:
sql += 'difficulty=:difficulty and '
sql_dict['difficulty'] = difficulty
if flag[2]:
sql += 'user_id=:user_id '
sql_dict['user_id'] = user_id
if sql[-4:-1] == 'and':
sql = sql[:-4]
c.execute(sql, sql_dict)
flash('成功删除成绩 Successfully delete the scores.')
error = None
else:
error = '输入为空 Null Input.'
if error:
flash(error)
return redirect(url_for('index.change_score'))

View File

@@ -5,6 +5,7 @@ import json
import server.arcscore import server.arcscore
import hashlib import hashlib
from random import Random from random import Random
from setting import Config
def int2b(x): def int2b(x):
@@ -161,22 +162,23 @@ def update_one_table(c1, c2, table_name):
def update_user_char(c): def update_user_char(c):
# 用character数据更新user_char # 用character数据更新user_char
c.execute('''select * from character''') c.execute('''select character_id, max_level, is_uncapped from character''')
x = c.fetchall() x = c.fetchall()
c.execute('''select user_id from user''') c.execute('''select user_id from user''')
y = c.fetchall() y = c.fetchall()
if x and y: if x and y:
for j in y: for j in y:
for i in x: for i in x:
c.execute('''delete from user_char where user_id=:a and character_id=:b''', { c.execute('''delete from user_char_full where user_id=:a and character_id=:b''', {
'a': j[0], 'b': i[0]}) 'a': j[0], 'b': i[0]})
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', { exp = 25000 if i[1] == 30 else 10000
'a': j[0], 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]}) c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
(j[0], i[0], i[1], exp, i[2], 0))
def update_database(): def update_database():
# 将old数据库不存在数据加入到新数据库上并删除old数据库 # 将old数据库不存在数据加入到新数据库上并删除old数据库
# 对于arcaea_datebase.db更新一些表并用character数据更新user_char # 对于arcaea_datebase.db更新一些表并用character数据更新user_char_full
# 对于arcsong.db更新songs # 对于arcsong.db更新songs
if os.path.isfile("database/old_arcaea_database.db") and os.path.isfile("database/arcaea_database.db"): if os.path.isfile("database/old_arcaea_database.db") and os.path.isfile("database/arcaea_database.db"):
with Connect('./database/old_arcaea_database.db') as c1: with Connect('./database/old_arcaea_database.db') as c1:
@@ -189,19 +191,28 @@ def update_database():
update_one_table(c1, c2, 'user_world') update_one_table(c1, c2, 'user_world')
update_one_table(c1, c2, 'item') update_one_table(c1, c2, 'item')
update_one_table(c1, c2, 'user_item') update_one_table(c1, c2, 'user_item')
update_one_table(c1, c2, 'purchase')
update_one_table(c1, c2, 'purchase_item')
update_one_table(c1, c2, 'user_save') update_one_table(c1, c2, 'user_save')
update_one_table(c1, c2, 'login') update_one_table(c1, c2, 'login')
update_one_table(c1, c2, 'present') update_one_table(c1, c2, 'present')
update_one_table(c1, c2, 'user_present') update_one_table(c1, c2, 'user_present')
update_one_table(c1, c2, 'present_item')
update_one_table(c1, c2, 'redeem') update_one_table(c1, c2, 'redeem')
update_one_table(c1, c2, 'user_redeem') update_one_table(c1, c2, 'user_redeem')
update_one_table(c1, c2, 'redeem_item')
update_one_table(c1, c2, 'role') update_one_table(c1, c2, 'role')
update_one_table(c1, c2, 'user_role') update_one_table(c1, c2, 'user_role')
update_one_table(c1, c2, 'power') update_one_table(c1, c2, 'power')
update_one_table(c1, c2, 'role_power') update_one_table(c1, c2, 'role_power')
update_one_table(c1, c2, 'api_auth') update_one_table(c1, c2, 'api_login')
update_user_char(c2) update_one_table(c1, c2, 'user_char')
if not Config.UPDATE_WITH_NEW_CHARACTER_DATA:
update_one_table(c1, c2, 'character')
update_user_char(c2) # 更新user_char_full
os.remove('database/old_arcaea_database.db') os.remove('database/old_arcaea_database.db')
@@ -220,14 +231,16 @@ def unlock_all_user_item(c):
c.execute('''select user_id from user''') c.execute('''select user_id from user''')
x = c.fetchall() x = c.fetchall()
c.execute('''select item_id, type from item''') c.execute('''select item_id, type from purchase_item''')
y = c.fetchall() y = c.fetchall()
c.execute('''delete from user_item''')
if x and y: if x and y:
for i in x: for i in x:
for j in y: for j in y:
c.execute('''insert into user_item values(:a,:b,:c)''', { c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', {
'a': i[0], 'b': j[0], 'c': j[1]}) 'a': i[0], 'b': j[0], 'c': j[1]})
if c.fetchone() == (0,) and j[1] != 'character':
c.execute('''insert into user_item values(:a,:b,:c,1)''', {
'a': i[0], 'b': j[0], 'c': j[1]})
return return
@@ -235,44 +248,68 @@ def unlock_all_user_item(c):
def unlock_user_item(c, user_id): def unlock_user_item(c, user_id):
# 解锁用户购买 # 解锁用户购买
c.execute('''select item_id, type from item''') c.execute('''select item_id, type from purchase_item''')
y = c.fetchall() y = c.fetchall()
for j in y: for j in y:
c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', { c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', {
'a': user_id, 'b': j[0], 'c': j[1]}) 'a': user_id, 'b': j[0], 'c': j[1]})
if c.fetchone() == (0,): if c.fetchone() == (0,) and j[1] != 'character':
c.execute('''insert into user_item values(:a,:b,:c)''', { c.execute('''insert into user_item values(:a,:b,:c,1)''', {
'a': user_id, 'b': j[0], 'c': j[1]}) 'a': user_id, 'b': j[0], 'c': j[1]})
return return
def get_all_item(): def get_all_item():
# 所有购买数据查询 # 所有物品数据查询
with Connect() as c: with Connect() as c:
c.execute('''select * from item''') c.execute('''select * from item''')
x = c.fetchall() x = c.fetchall()
re = [] re = []
if x: if x:
for i in x: for i in x:
re.append({'item_id': i[0],
'type': i[1],
'is_available': int2b(i[2])
})
return re
def get_all_purchase():
# 所有购买数据查询
with Connect() as c:
c.execute('''select * from purchase''')
x = c.fetchall()
re = []
if x:
for i in x:
discount_from = None discount_from = None
discount_to = None discount_to = None
if i[5] and i[5] >= 0: if i[3] and i[3] >= 0:
discount_from = time.strftime( discount_from = time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[5])/1000)) "%Y-%m-%d %H:%M:%S", time.localtime(int(i[3])/1000))
if i[6] and i[6] >= 0: if i[4] and i[4] >= 0:
discount_to = time.strftime( discount_to = time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[6])//1000)) "%Y-%m-%d %H:%M:%S", time.localtime(int(i[4])//1000))
re.append({'item_id': i[0], c.execute(
'type': i[1], '''select * from purchase_item where purchase_name=?''', (i[0],))
'is_available': int2b(i[2]), y = c.fetchall()
'price': i[3], items = []
'orig_price': i[4], if y:
for j in y:
items.append({'item_id': j[1], 'type': j[2], 'amount':j[3]})
re.append({'purchase_name': i[0],
'price': i[1],
'orig_price': i[2],
'discount_from': discount_from, 'discount_from': discount_from,
'discount_to': discount_to 'discount_to': discount_to,
'items': items
}) })
return re return re
@@ -332,7 +369,7 @@ def update_all_save(c):
return return
def add_one_present(present_id, expire_ts, description, items): def add_one_present(present_id, expire_ts, description, item_id, item_type, item_amount):
# 添加一个奖励 # 添加一个奖励
message = None message = None
@@ -340,9 +377,16 @@ def add_one_present(present_id, expire_ts, description, items):
c.execute( c.execute(
'''select exists(select * from present where present_id=:a)''', {'a': present_id}) '''select exists(select * from present where present_id=:a)''', {'a': present_id})
if c.fetchone() == (0,): if c.fetchone() == (0,):
c.execute('''insert into present values(:a,:b,:c,:d)''', { c.execute(
'a': present_id, 'b': expire_ts, 'c': items, 'd': description}) '''select exists(select * from item where item_id=? and type=?)''', (item_id, item_type))
message = '添加成功 Successfully add it.' if c.fetchone() == (1,):
c.execute('''insert into present values(:a,:b,:c)''', {
'a': present_id, 'b': expire_ts, 'c': description})
c.execute('''insert into present_item values(?,?,?,?)''',
(present_id, item_id, item_type, item_amount))
message = '添加成功 Successfully add it.'
else:
message = '物品不存在 The item does not exist.'
else: else:
message = '奖励已存在 The present exists.' message = '奖励已存在 The present exists.'
@@ -361,6 +405,8 @@ def delete_one_present(present_id):
{'a': present_id}) {'a': present_id})
c.execute('''delete from user_present where present_id =:a''', { c.execute('''delete from user_present where present_id =:a''', {
'a': present_id}) 'a': present_id})
c.execute('''delete from present_item where present_id =:a''', {
'a': present_id})
message = '删除成功 Successfully delete it.' message = '删除成功 Successfully delete it.'
else: else:
message = '奖励不存在 The present does not exist.' message = '奖励不存在 The present does not exist.'
@@ -403,7 +449,7 @@ def deliver_all_user_present(c, present_id):
return return
def add_one_redeem(code, redeem_type, items): def add_one_redeem(code, redeem_type, item_id, item_type, item_amount):
# 添加一个兑换码 # 添加一个兑换码
message = None message = None
@@ -411,28 +457,41 @@ def add_one_redeem(code, redeem_type, items):
c.execute( c.execute(
'''select exists(select * from redeem where code=:a)''', {'a': code}) '''select exists(select * from redeem where code=:a)''', {'a': code})
if c.fetchone() == (0,): if c.fetchone() == (0,):
c.execute('''insert into redeem values(:a,:b,:c)''', { c.execute(
'a': code, 'b': items, 'c': redeem_type}) '''select exists(select * from item where item_id=? and type=?)''', (item_id, item_type))
message = '添加成功 Successfully add it.' if c.fetchone() == (1,):
c.execute('''insert into redeem values(:a,:c)''', {
'a': code, 'c': redeem_type})
c.execute('''insert into redeem_item values(?,?,?,?)''',
(code, item_id, item_type, item_amount))
message = '添加成功 Successfully add it.'
else:
message = '物品不存在 The item does not exist.'
else: else:
message = '兑换码已存在 The redeem code exists.' message = '兑换码已存在 The redeem code exists.'
return message return message
def add_some_random_redeem(amount, redeem_type, items): def add_some_random_redeem(amount, redeem_type, item_id, item_type, item_amount):
# 随机生成一堆10位的兑换码 # 随机生成一堆10位的兑换码
message = None message = None
with Connect() as c: with Connect() as c:
c.execute(
'''select exists(select * from item where item_id=? and type=?)''', (item_id, item_type))
if c.fetchone() == (0,):
return '物品不存在 The item does not exist.'
i = 1 i = 1
while i <= amount: while i <= amount:
code = random_str() code = random_str()
c.execute( c.execute(
'''select exists(select * from redeem where code=:a)''', {'a': code}) '''select exists(select * from redeem where code=:a)''', {'a': code})
if c.fetchone() == (0,): if c.fetchone() == (0,):
c.execute('''insert into redeem values(:a,:b,:c)''', c.execute('''insert into redeem values(:a,:c)''',
{'a': code, 'b': items, 'c': redeem_type}) {'a': code, 'c': redeem_type})
c.execute('''insert into redeem_item values(?,?,?,?)''',
(code, item_id, item_type, item_amount))
i += 1 i += 1
message = '添加成功 Successfully add it.' message = '添加成功 Successfully add it.'
@@ -451,6 +510,8 @@ def delete_one_redeem(code):
c.execute('''delete from redeem where code = :a''', {'a': code}) c.execute('''delete from redeem where code = :a''', {'a': code})
c.execute( c.execute(
'''delete from user_redeem where code =:a''', {'a': code}) '''delete from user_redeem where code =:a''', {'a': code})
c.execute(
'''delete from redeem_item where code =:a''', {'a': code})
message = '删除成功 Successfully delete it.' message = '删除成功 Successfully delete it.'
else: else:
message = '兑换码不存在 The redeem code does not exist.' message = '兑换码不存在 The redeem code does not exist.'
@@ -476,7 +537,7 @@ def ban_one_user(c, user_id):
def clear_user_score(c, user_id): def clear_user_score(c, user_id):
# 清除用户所有成绩包括best_score和recent30以及recent数据但不包括云端存档 # 清除用户所有成绩包括best_score和recent30以及recent数据但不包括云端存档
c.execute('''update user set rating_ptt=0, song_id='', difficulty=0, score=0, shiny_perfect_count=0, perfect_count=0, near_count=0, miss_count=0, health=0, time_played=0, rating=0 where user_id=:a''', c.execute('''update user set rating_ptt=0, song_id='', difficulty=0, score=0, shiny_perfect_count=0, perfect_count=0, near_count=0, miss_count=0, health=0, time_played=0, rating=0, world_rank_score=0 where user_id=:a''',
{'a': user_id}) {'a': user_id})
c.execute('''delete from best_score where user_id=:a''', {'a': user_id}) c.execute('''delete from best_score where user_id=:a''', {'a': user_id})
c.execute('''delete from recent30 where user_id=:a''', {'a': user_id}) c.execute('''delete from recent30 where user_id=:a''', {'a': user_id})