4 Commits
v2.3.2 ... v2.5

Author SHA1 Message Date
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
Lost-MSth
8a3cec05b7 Update to v2.4 2021-05-14 14:06:58 +08:00
Lost-MSth
e3d81436d0 Waiting for updating
+ new character
+ new byd map
+ new items
+ some new configs
+ two new operations in background
+ record email while registering
+ record ip while logging in
+ checking something before running and updating database automatically

building something about API

fix bugs:
about purchasing system
about hiding ptt
about login different accounts with same device
about some details

This is only a pre updating. Many things have been changed. It takes time to find some ways.
2021-05-12 00:45:13 +08:00
39 changed files with 3386 additions and 1347 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,7 +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
可能有问题 There may be problems 可能有问题 There may be problems
- Recent 30 - Recent 30
@@ -60,24 +58,36 @@ It is just so interesting. What it can do is under exploration.
## 更新日志 Update log ## 更新日志 Update log
只保留最新版本 Only keep the latest version. 只保留最新版本 Only keep the latest version.
> 提醒:更新时请注意保留原先的数据库,以防数据丢失。使用前请先运行**database_initialize.py**以获得初始数据库。 > 提醒:更新时请注意保留原先的数据库,以防数据丢失。
> >
> 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.
> Before using, please run **database_initialize.py** to get the initial database.
### Version 2.3.2 ### Version 2.5
- 适用于Arcaea 3.5.4版本 For Arcaea 3.5.4 - 适用于Arcaea 3.6.2版本 For Arcaea 3.6.2
- 更新了歌曲数据库 Update the song database. - 更新了歌曲数据库 Update the song database.
- 新增运行前关键性文件检查 Add checking critical files before running. - 新增世界排名 Add global rank.
- 新增可下载数据的客户端校验更新 Add client verification update of downloadable data. - 新增世界模式的奖励获取 Add reward acquisition of new world mode.
- 新增歌曲成绩的谱面校验 Add checking chart file when submitting the song score. - 完善角色系统,包括: Improve the character system, including
- 角色升级 Level up
- 角色觉醒 Upcapping
- 角色核心 Character cores
- 世界模式下的数值计算 Numerical calculation in world mode
- 某些场景下的角色解锁 Character unlocking in some cases
- 新增了一些设置选项和后台功能 Add some setting options and background functions.
- 正在构建API接口 Building API interface.
- 打算重新写后台界面 Plan to rewrite the background interface.
- 修复了一些Bug Fix some bugs. - 修复了一些Bug Fix some bugs.
- 登录时获取信息的并行计算导致的数据库锁错误 Database lock error caused by parallel computing of getting information during login
> 提醒:由于时间仓促,本版本可能存在较多问题与漏洞,请谨慎使用
> Tips: Due to lack of time, this version may have many bugs, please use it with caution.
## 运行环境与依赖 Running environment and requirements ## 运行环境与依赖 Running environment and requirements
- Windows/Linux/Mac OS/Android - Windows/Linux/Mac OS/Android
- Python 3 - Python 3
- Flask module - Flask module
- Charles (optional) - Charles, IDA, proxy app... (optional)
<!-- <!--
## 环境搭建 Environment construction ## 环境搭建 Environment construction
@@ -87,11 +97,12 @@ It is just so interesting. What it can do is under exploration.
## 使用说明 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)
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Attentions) [English](https://github.com/Lost-MSth/Arcaea-server/wiki/Attentions)
-->
## 鸣谢 Thanks ## 鸣谢 Thanks
歌曲数据库来自 Using song database from 歌曲数据库来自 Using song database from

View File

@@ -0,0 +1,118 @@
import hashlib
import time
from server.sql import Connect
import functools
from setting import Config
from flask import jsonify
class User():
# 用户类,当数据类型用
def __init__(self, user_id=None, role='', power=[]):
self.user_id = user_id
self.role = role
self.power = power
def login():
# 登录接口
return {'token': 1}, 0
def logout():
# 登出接口
pass
def id_get_role_id(c, user_id):
# user_id获取role_id
role_id = 1
c.execute('''select role_id from user_role where user_id = :a''',
{'a': user_id})
x = c.fetchone()
if x is not None:
role_id = int(x[0])
return role_id
def role_id_get_role(c, role_id):
# role_id获取role
role = ''
c.execute('''select role_name from role where role_id = :a''',
{'a': role_id})
x = c.fetchone()
if x is not None:
role = x[0]
return role
def api_token_get_id(c, token):
# api的token获取user_id
user_id = None
c.execute('''select user_id from api_login where token = :token''', {
'token': token})
x = c.fetchone()
if x is not None:
user_id = x[0]
return user_id
def get_role_power(c, role_id):
# 获取role_id对应power返回列表
role_power = []
c.execute('''select power_name from power where power_id in (select power_id from role_power where role_id=:a)''', {
'a': role_id})
x = c.fetchall()
for i in x:
role_power.append(i[0])
return role_power
def role_required(request, power=[]):
# api token验证写成了修饰器
def decorator(view):
@functools.wraps(view)
def wrapped_view(*args, **kwargs):
try:
request.json # 检查请求json格式
except:
return jsonify({'status': 400, 'code': -1, 'data': {}, 'msg': 'Payload must be a valid json'})
if not 'Token' in request.headers:
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No Token'})
user = User()
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
user.user_id = 0
elif power == []:
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
else:
with Connect() as c:
user.user_id = api_token_get_id(
c, request.headers['Token'])
if user.user_id is None:
return jsonify({'status': 404, 'code': -1, 'data': {}, 'msg': ''})
role_id = id_get_role_id(c, user.user_id)
user.role = role_id_get_role(c, role_id)
user.role_power = get_role_power(c, role_id)
f = False
for i in power:
if i in user.role_power:
f = True
break
if not f:
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
return view(user, *args, **kwargs)
return wrapped_view
return decorator

View File

@@ -0,0 +1,15 @@
def code_get_msg(code):
# api接口code获取msg返回字符串
msg = {
'0': '',
'-1': 'See status code',
'-2': 'No data',
'-3': 'No data or user',
'-4': 'No user_id',
'-101': 'Wrong data type',
'-102': 'Wrong query parameter',
'-103': 'Wrong sort parameter',
'-104': 'Wrong sort order parameter'
}
return msg[str(code)]

View File

@@ -0,0 +1,223 @@
from flask import (
Blueprint, request, jsonify
)
import api.api_auth
import api.users
import api.songs
from api.api_code import code_get_msg
bp = Blueprint('api', __name__, url_prefix='/api/v1')
def get_query_parameter(request, query_able=[], sort_able=[]):
# 提取查询请求参数返回四个参数和code
limit = -1
offset = 0
query = {} # {'name': 'admin'}
sort = [] # [{'column': 'user_id', 'order': 'ASC'}, ...]
if 'limit' in request.json:
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
@bp.route('/')
def ping():
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''})
@bp.route('/token', methods=['POST'])
def token_post():
# 登录获取token
if 'auth' in request.json:
data, code = api.api_auth.login(
request.json['auth'], request.remote_addr)
if code < 0:
return jsonify({'status': 200, 'code': code, 'data': {}, 'msg': code_get_msg(code)})
else:
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
else:
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No authentication'})
@bp.route('/token', methods=['GET'])
@api.api_auth.role_required(request, ['select_me', 'select'])
def token_get(user):
# 判断登录有效性
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''})
@bp.route('/token', methods=['DELETE'])
@api.api_auth.role_required(request, ['change_me', 'select_me', 'select'])
def token_delete(user):
# 登出
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''})
@bp.route('/users', methods=['GET'])
@api.api_auth.role_required(request, ['select'])
def users_get(user):
# 查询全用户信息
limit, offset, query, sort, code = get_query_parameter(request, ['user_id', 'name', 'user_code'], [
'user_id', 'name', 'user_code', 'join_date', 'rating_ptt', 'time_played', 'ticket', 'world_rank_score'])
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:
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
@bp.route('/users/<int:user_id>', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_me'])
def users_user_get(user, user_id):
# 查询用户信息
if user_id == 'me':
user_id = user.user_id
if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
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'})
data = api.users.get_user_info(user_id)
if not data:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
@bp.route('/users/<int:user_id>/b30', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_me'])
def users_user_b30_get(user, user_id):
# 查询用户b30
if user_id == 'me':
user_id = user.user_id
if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
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'})
data = api.users.get_user_b30(user_id)
if data['data'] == []:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
@bp.route('/users/<int:user_id>/best', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_me'])
def users_user_best_get(user, user_id):
# 查询用户所有best成绩
if user_id == 'me':
user_id = user.user_id
if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
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'})
limit, offset, query, sort, code = get_query_parameter(request, ['song_id', 'difficulty'], [
'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'] == []:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
@bp.route('/users/<int:user_id>/r30', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_me'])
def users_user_r30_get(user, user_id):
# 查询用户r30
if user_id == 'me':
user_id = user.user_id
if user_id <= 0:
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
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'})
data = api.users.get_user_r30(user_id)
if data['data'] == []:
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
@bp.route('/songs/<string:song_id>', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_song_info'])
def songs_song_get(user, song_id):
# 查询歌曲信息
data = api.songs.get_song_info(song_id)
if not data:
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
@bp.route('/songs', methods=['GET'])
@api.api_auth.role_required(request, ['select', 'select_song_info'])
def songs_get(user):
# 查询全歌曲信息
limit, offset, query, sort, code = 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'])
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:
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})

View File

@@ -0,0 +1,58 @@
from server.sql import Connect
from server.sql import Sql
import time
def get_song_info(song_id):
# 查询指定歌曲信息,返回字典
r = {}
with Connect('./database/arcsong.db') as c:
c.execute('''select * from songs where sid=:a''', {'a': song_id})
x = c.fetchone()
if x:
r = {'song_id': x[0],
'name': {'name_en': x[1],
'name_jp': x[2]},
'pakset': x[5],
'artist': x[6],
'date': x[9],
'rating_pst': x[13]/10,
'rating_prs': x[14]/10,
'rating_ftr': x[15]/10,
'rating_byn': x[16]/10,
'difficultly_pst': x[17]/2,
'difficultly_prs': x[18]/2,
'difficultly_ftr': x[19]/2,
'difficultly_byn': x[20]/2
}
return r
def get_songs(limit=-1, offset=0, query={}, sort=[]):
# 查询全部歌曲信息,返回字典列表
r = []
with Connect('./database/arcsong.db') as c:
x = Sql.select(c, 'songs', [], limit, offset, query, sort)
if x:
for i in x:
r.append({'sid': i[0],
'name': {'name_en': i[1],
'name_jp': i[2]},
'pakset': i[5],
'artist': i[6],
'date': i[9],
'rating_pst': i[13]/10,
'rating_prs': i[14]/10,
'rating_ftr': i[15]/10,
'rating_byn': i[16]/10,
'difficultly_pst': i[17]/2,
'difficultly_prs': i[18]/2,
'difficultly_ftr': i[19]/2,
'difficultly_byn': i[20]/2
})
return r

View File

@@ -0,0 +1,99 @@
from server.sql import Connect
from server.sql import Sql
import time
import web.webscore
import server.info
def get_users(limit=-1, offset=0, query={}, sort=[]):
# 获取全用户信息,返回字典列表
r = []
with Connect() as c:
x = Sql.select(c, 'user', [], limit, offset, query, sort)
if x:
for i in x:
if i[23] != -1:
character_id = i[23]
else:
character_id = i[6]
r.append({
'user_id': i[0],
'name': i[1],
'join_date': i[3],
'user_code': i[4],
'rating_ptt': i[5]/100,
'character_id': character_id,
'is_char_uncapped': i[8] == 1,
'is_char_uncapped_override': i[9] == 1,
'is_hide_rating': i[10],
'ticket': i[26]
})
return r
def get_user_info(user_id):
# 获取用户信息返回字典其实就是调用user/me信息
r = {}
with Connect() as c:
r = server.info.get_user_me(c, user_id)
return r
def get_user_b30(user_id):
# 获取用户b30信息返回字典
r = []
with Connect() as c:
r = web.webscore.get_user_score(c, user_id, 30)
bestptt = 0
for i in r:
if i['rating']:
bestptt += i['rating']
if 'time_played' in i:
i['time_played'] = int(time.mktime(time.strptime(
i['time_played'], '%Y-%m-%d %H:%M:%S')))
return {'user_id': user_id, 'b30_ptt': bestptt / 30, 'data': r}
def get_user_best(user_id, limit=-1, offset=0, query={}, sort=[]):
# 获取用户b30信息返回字典
r = []
with Connect() as c:
x = Sql.select(c, 'best_score', [], limit, offset, query, sort)
if x:
for i in x:
r.append({
"song_id": i[1],
"difficulty": i[2],
"score": i[3],
"shiny_perfect_count": i[4],
"perfect_count": i[5],
"near_count": i[6],
"miss_count": i[7],
"health": i[8],
"modifier": i[9],
"time_played": i[10],
"best_clear_type": i[11],
"clear_type": i[12],
"rating": i[13]
})
return {'user_id': user_id, 'data': r}
def get_user_r30(user_id):
# 获取用户r30信息返回字典
r = []
with Connect() as c:
r, r10_ptt = web.webscore.get_user_recent30(c, user_id)
return {'user_id': user_id, 'r10_ptt': r10_ptt, 'data': r}

Binary file not shown.

View File

@@ -1,376 +1,492 @@
import sqlite3 import sqlite3
import hashlib
import time import time
import json import json
# 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用 # 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用
conn = sqlite3.connect('arcaea_database.db') ARCAEA_SERVER_VERSION = 'v2.5'
c = conn.cursor()
c.execute('''create table if not exists user(user_id int primary key,
name text unique,
password text not null,
join_date char(20),
user_code char(10),
rating_ptt int,
character_id int,
is_skill_sealed int,
is_char_uncapped int,
is_char_uncapped_override int,
is_hide_rating int,
song_id text,
difficulty int,
score int,
shiny_perfect_count int,
perfect_count int,
near_count int,
miss_count int,
health int,
modifier int,
time_played int,
clear_type int,
rating real,
favorite_character int,
max_stamina_notification_enabled int,
current_map text,
ticket int,
prog_boost int
);''')
c.execute('''create table if not exists login(access_token text,
user_id int,
login_time int,
login_ip text,
login_device text,
primary key(access_token, user_id)
);''')
c.execute('''create table if not exists friend(user_id_me int,
user_id_other int,
primary key (user_id_me, user_id_other)
);''')
c.execute('''create table if not exists best_score(user_id int,
song_id text,
difficulty int,
score int,
shiny_perfect_count int,
perfect_count int,
near_count int,
miss_count int,
health int,
modifier int,
time_played int,
best_clear_type int,
clear_type int,
rating real,
primary key(user_id, song_id, difficulty)
);''')
c.execute('''create table if not exists user_char(user_id int,
character_id int,
level int,
exp real,
level_exp real,
frag real,
prog real,
overdrive real,
skill_id text,
skill_unlock_level int,
skill_requires_uncap int,
skill_id_uncap text,
char_type int,
is_uncapped int,
is_uncapped_override int,
primary key(user_id, character_id)
);''')
c.execute('''create table if not exists character(character_id int primary key,
name text,
level int,
exp real,
level_exp real,
frag real,
prog real,
overdrive real,
skill_id text,
skill_unlock_level int,
skill_requires_uncap int,
skill_id_uncap text,
char_type int,
uncap_cores text,
is_uncapped int,
is_uncapped_override int
);''')
c.execute('''create table if not exists recent30(user_id int primary key,
r0 real,
song_id0 text,
r1 real,
song_id1 text,
r2 real,
song_id2 text,
r3 real,
song_id3 text,
r4 real,
song_id4 text,
r5 real,
song_id5 text,
r6 real,
song_id6 text,
r7 real,
song_id7 text,
r8 real,
song_id8 text,
r9 real,
song_id9 text,
r10 real,
song_id10 text,
r11 real,
song_id11 text,
r12 real,
song_id12 text,
r13 real,
song_id13 text,
r14 real,
song_id14 text,
r15 real,
song_id15 text,
r16 real,
song_id16 text,
r17 real,
song_id17 text,
r18 real,
song_id18 text,
r19 real,
song_id19 text,
r20 real,
song_id20 text,
r21 real,
song_id21 text,
r22 real,
song_id22 text,
r23 real,
song_id23 text,
r24 real,
song_id24 text,
r25 real,
song_id25 text,
r26 real,
song_id26 text,
r27 real,
song_id27 text,
r28 real,
song_id28 text,
r29 real,
song_id29 text
);''')
c.execute('''create table if not exists user_world(user_id int,
map_id text,
curr_position int,
curr_capture real,
is_locked int,
primary key(user_id, map_id)
);''')
c.execute('''create table if not exists world_songplay(user_id int,
song_id text,
difficulty int,
stamina_multiply int,
fragment_multiply int,
prog_boost_multiply int,
primary key(user_id, song_id, difficulty)
);''')
c.execute('''create table if not exists download_token(user_id int,
song_id text,
file_name text,
token text,
time int,
primary key(user_id, song_id, file_name)
);''')
c.execute('''create table if not exists user_download(user_id int,
token text,
time int,
primary key(user_id, token, time)
);''')
c.execute('''create table if not exists item(item_id text,
type text,
is_available int,
price int,
orig_price int,
discount_from int,
discount_to int,
_id text,
primary key(item_id, type)
);''')
c.execute('''create table if not exists user_item(user_id int,
item_id text,
type text,
primary key(user_id, item_id, type)
);''')
c.execute('''create table if not exists user_save(user_id int primary key,
scores_data text,
clearlamps_data text,
clearedsongs_data text,
unlocklist_data text,
installid_data text,
devicemodelname_data text,
story_data text,
createdAt int
);''')
c.execute('''create table if not exists present(present_id text primary key,
expire_ts int,
items text,
description text
);''')
c.execute('''create table if not exists user_present(user_id int,
present_id text,
primary key(user_id, present_id)
);''')
c.execute('''create table if not exists songfile(song_id text,
file_type int,
md5 text,
primary key(song_id, file_type)
);''')
c.execute('''create table if not exists redeem(code text primary key,
items text,
type int
);''')
c.execute('''create table if not exists user_redeem(user_id int,
code text,
primary key(user_id, code)
);''')
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', def main(path='./'):
'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'] conn = sqlite3.connect(path+'arcaea_database.db')
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_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
'', '', '', '', '', '', '', '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,
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65]
prog = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 67, 63, 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]
overdrive = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 59, 90, 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, 90]
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]
print(len(char))
print(len(skill_id))
print(len(skill_id_uncap))
print(len(frag))
print(len(prog))
print(len(overdrive))
print(len(char_type))
for i in range(0, 45):
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43]:
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],
'e': overdrive[i], 'f': skill_id[i], 'g': skill_id_uncap[i], 'h': char_type[i]})
else:
if i != 5:
sql = '''insert into character values(:a,:b,20,10000,10000,:c,:d,:e,:f,0,0,:g,:h,'',0,0)'''
c.execute(sql, {'a': i, 'b': char[i], 'c': frag[i], 'd': prog[i],
'e': overdrive[i], 'f': skill_id[i], 'g': skill_id_uncap[i], 'h': char_type[i]})
def b2int(x):
# int与布尔值转换
if x:
return 1
else:
return 0
def insert_items(c, items):
# 物品数据导入
for i in items:
if 'discount_from' not in i:
discount_from = -1
else:
discount_from = i['discount_from']
if 'discount_to' not in i:
discount_to = -1
else:
discount_to = i['discount_to']
for j in i['items']:
if "_id" not in j:
_id = ''
else:
_id = j['_id']
if j['type'] != 'character':
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)''', {
'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})
f = open('singles.json', 'r')
singles = json.load(f)
f.close()
insert_items(c, singles)
f = open('packs.json', 'r')
packs = json.load(f)
f.close()
insert_items(c, packs)
conn.commit()
conn.close()
def arc_register(name: str, password: str):
def build_user_code(c):
return '123456789'
def build_user_id(c):
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):
# 为用户添加所有可用角色
c.execute('''select * from character''')
x = c.fetchall()
if 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)''', {
'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]})
conn = sqlite3.connect('arcaea_database.db')
c = conn.cursor() c = conn.cursor()
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest() c.execute('''create table if not exists config(id text primary key,
c.execute( value text
'''select exists(select * from user where name = :name)''', {'name': name}) );''')
if c.fetchone() == (0,): c.execute('''insert into config values("version", :a);''',
user_code = build_user_code(c) {'a': ARCAEA_SERVER_VERSION})
user_id = build_user_id(c) c.execute('''create table if not exists user(user_id int primary key,
now = int(time.time() * 1000) name text unique,
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt, password text not null,
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket) join_date char(20),
values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '', 114514) user_code char(10),
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd}) rating_ptt int,
c.execute('''insert into recent30(user_id) values(:user_id)''', { character_id int,
'user_id': user_id}) is_skill_sealed int,
is_char_uncapped int,
is_char_uncapped_override int,
is_hide_rating int,
song_id text,
difficulty int,
score int,
shiny_perfect_count int,
perfect_count int,
near_count int,
miss_count int,
health int,
modifier int,
time_played int,
clear_type int,
rating real,
favorite_character int,
max_stamina_notification_enabled int,
current_map text,
ticket int,
prog_boost int,
email text,
world_rank_score int
);''')
c.execute('''create table if not exists login(access_token text,
user_id int,
login_time int,
login_ip text,
login_device text,
primary key(access_token, user_id)
);''')
c.execute('''create table if not exists friend(user_id_me int,
user_id_other int,
primary key (user_id_me, user_id_other)
);''')
c.execute('''create table if not exists best_score(user_id int,
song_id text,
difficulty int,
score int,
shiny_perfect_count int,
perfect_count int,
near_count int,
miss_count int,
health int,
modifier int,
time_played int,
best_clear_type int,
clear_type int,
rating real,
primary key(user_id, song_id, difficulty)
);''')
c.execute('''create table if not exists user_char(user_id int,
character_id int,
level int,
exp real,
is_uncapped int,
is_uncapped_override int,
primary key(user_id, character_id)
);''')
c.execute('''create table if not exists user_char_full(user_id int,
character_id int,
level int,
exp real,
is_uncapped int,
is_uncapped_override int,
primary key(user_id, character_id)
);''')
c.execute('''create table if not exists character(character_id int primary key,
name text,
max_level int,
frag1 real,
prog1 real,
overdrive1 real,
frag20 real,
prog20 real,
overdrive20 real,
frag30 real,
prog30 real,
overdrive30 real,
skill_id text,
skill_unlock_level int,
skill_requires_uncap int,
skill_id_uncap text,
char_type int,
is_uncapped 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,
r0 real,
song_id0 text,
r1 real,
song_id1 text,
r2 real,
song_id2 text,
r3 real,
song_id3 text,
r4 real,
song_id4 text,
r5 real,
song_id5 text,
r6 real,
song_id6 text,
r7 real,
song_id7 text,
r8 real,
song_id8 text,
r9 real,
song_id9 text,
r10 real,
song_id10 text,
r11 real,
song_id11 text,
r12 real,
song_id12 text,
r13 real,
song_id13 text,
r14 real,
song_id14 text,
r15 real,
song_id15 text,
r16 real,
song_id16 text,
r17 real,
song_id17 text,
r18 real,
song_id18 text,
r19 real,
song_id19 text,
r20 real,
song_id20 text,
r21 real,
song_id21 text,
r22 real,
song_id22 text,
r23 real,
song_id23 text,
r24 real,
song_id24 text,
r25 real,
song_id25 text,
r26 real,
song_id26 text,
r27 real,
song_id27 text,
r28 real,
song_id28 text,
r29 real,
song_id29 text
);''')
c.execute('''create table if not exists user_world(user_id int,
map_id text,
curr_position int,
curr_capture real,
is_locked int,
primary key(user_id, map_id)
);''')
c.execute('''create table if not exists world_songplay(user_id int,
song_id text,
difficulty int,
stamina_multiply int,
fragment_multiply int,
prog_boost_multiply int,
primary key(user_id, song_id, difficulty)
);''')
c.execute('''create table if not exists download_token(user_id int,
song_id text,
file_name text,
token text,
time int,
primary key(user_id, song_id, file_name)
);''')
c.execute('''create table if not exists user_download(user_id int,
token text,
time int,
primary key(user_id, token, time)
);''')
c.execute('''create table if not exists item(item_id text,
type text,
is_available int,
_id text,
primary key(item_id, type)
);''')
c.execute('''create table if not exists user_item(user_id int,
item_id text,
type text,
amount int,
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,
primary key(purchase_name, item_id, type)
);''')
c.execute('''create table if not exists user_save(user_id int primary key,
scores_data text,
clearlamps_data text,
clearedsongs_data text,
unlocklist_data text,
installid_data text,
devicemodelname_data text,
story_data text,
createdAt int
);''')
c.execute('''create table if not exists present(present_id text primary key,
expire_ts int,
items text,
description text
);''')
c.execute('''create table if not exists user_present(user_id int,
present_id text,
primary key(user_id, present_id)
);''')
c.execute('''create table if not exists songfile(song_id text,
file_type int,
md5 text,
primary key(song_id, file_type)
);''')
c.execute('''create table if not exists redeem(code text primary key,
items text,
type int
);''')
c.execute('''create table if not exists user_redeem(user_id int,
code text,
primary key(user_id, code)
);''')
c.execute('''create table if not exists role(role_id int primary key,
role_name text,
caption text
);''')
c.execute('''create table if not exists user_role(user_id int,
role_id int,
primary key(user_id, role_id)
);''')
c.execute('''create table if not exists power(power_id int primary key,
power_name text,
caption text
);''')
c.execute('''create table if not exists role_power(role_id int,
power_id int,
primary key(role_id, power_id)
);''')
c.execute('''create table if not exists api_login(user_id int,
token text,
login_time int,
login_ip text,
primary key(user_id, token)
);''')
# 搭档初始化
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']
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']
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
skill_unlock_level = [0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0,
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0]
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42]
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42]
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]
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]
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]
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]
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 80, 90, 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]
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 67, 63, 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]
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 59, 90, 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]
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]
char_core = {
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
1: [{'core_id': 'core_hollow', 'amount': 5}, {'core_id': 'core_desolate', 'amount': 25}],
2: [{'core_id': 'core_hollow', 'amount': 5}, {'core_id': 'core_crimson', 'amount': 25}],
4: [{'core_id': 'core_ambivalent', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
13: [{'core_id': 'core_scarlet', 'amount': 30}],
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}]
}
for i in range(0, 46):
skill_requires_uncap = 1 if i == 2 else 0
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43]:
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:
if i != 5:
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
for i in char_core:
for j in char_core[i]:
c.execute('''insert into char_item values(?,?,'core',?)''',
(i, j['core_id'], j['amount']))
cores = ['core_hollow', 'core_desolate', 'core_chunithm', 'core_crimson',
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic']
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"]
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,))
def insert_items(c, items):
# 物品数据导入
for i in items:
if 'discount_from' not in i:
discount_from = -1
else:
discount_from = i['discount_from']
if 'discount_to' not in i:
discount_to = -1
else:
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']:
if "_id" not in j:
_id = ''
else:
_id = j['_id']
c.execute('''insert into item values(?,?,?,?)''',
(j['id'], j['type'], j['is_available'], _id))
c.execute('''insert into purchase_item values(?,?,?)''',
(i['name'], j['id'], j['type']))
# item初始化
f = open(path+'singles.json', 'r')
singles = json.load(f)
f.close()
insert_items(c, singles)
f = open(path+'packs.json', 'r')
packs = json.load(f)
f.close()
insert_items(c, packs)
# api权限与权限组初始化
role = ['admin', 'user', 'selecter']
role_caption = ['管理员', '用户', '查询接口']
power = ['select', 'select_me', 'change', 'change_me', 'grant',
'grant_inf', 'select_song_rank', 'select_song_info']
power_caption = ['总体查询权限', '自我查询权限', '总体修改权限',
'自我修改权限', '授权权限', '下级授权权限', '歌曲排行榜查询权限', '歌曲信息查询权限']
role_power = {'0': [0, 1, 3, 5, 6, 7],
'1': [1, 3, 6, 7],
'2': [0]
}
for i in range(0, len(role)):
c.execute('''insert into role values(:a,:b,:c)''', {
'a': i, 'b': role[i], 'c': role_caption[i]})
for i in range(0, len(power)):
c.execute('''insert into power values(:a,:b,:c)''', {
'a': i, 'b': power[i], 'c': power_caption[i]})
for i in role_power:
for j in role_power[i]:
c.execute('''insert into role_power values(:a,:b)''',
{'a': int(i), 'b': j})
conn.commit()
conn.close()
def arc_register(name: str):
def build_user_code(c):
return '123456789'
def build_user_id(c):
return 2000000
def insert_user_char(c, user_id):
# 为用户添加可用角色
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()
if x:
for i in x:
exp = 25000 if i[1] == 30 else 10000
c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
(user_id, i[0], i[1], exp, i[2], 0))
conn = sqlite3.connect(path+'arcaea_database.db')
c = conn.cursor()
hash_pwd = '41e5653fc7aeb894026d6bb7b2db7f65902b454945fa8fd65a6327047b5277fb'
c.execute( c.execute(
'''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''') '''select exists(select * from user where name = :name)''', {'name': name})
insert_user_char(c, user_id) if c.fetchone() == (0,):
conn.commit() user_code = build_user_code(c)
conn.close() user_id = build_user_id(c)
return None now = int(time.time() * 1000)
else: c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
conn.commit() character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
conn.close() values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '', 114514)
return None ''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
c.execute('''insert into recent30(user_id) values(:user_id)''', {
'user_id': user_id})
c.execute(
'''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''')
insert_user_char(c, user_id)
conn.commit()
conn.close()
return None
else:
conn.commit()
conn.close()
return None
arc_register('admin')
arc_register('admin', 'admin123') if __name__ == '__main__':
main()

View File

@@ -0,0 +1,78 @@
{
"map_id": "byd_lostcivilization",
"is_legacy": false,
"chapter": 1001,
"available_from": -1,
"available_to": 9999999999999,
"is_repeatable": false,
"require_id": "lostcivilization2",
"require_type": "chart_unlock",
"coordinate": "-650,-900",
"is_beyond": true,
"stamina_cost": 3,
"beyond_health": 250,
"character_affinity": [0, 1],
"affinity_multiplier": [1.9, 1.9],
"step_count": 7,
"custom_bg": "",
"curr_position": 0,
"curr_capture": 0,
"is_locked": false,
"steps": [{
"map_id": "byd_lostcivilization",
"position": 0,
"capture": 85
}, {
"map_id": "byd_lostcivilization",
"position": 1,
"capture": 5,
"items": [{
"type": "core",
"id": "core_generic",
"amount": 1
}]
}, {
"map_id": "byd_lostcivilization",
"position": 2,
"capture": 5,
"items": [{
"type": "core",
"id": "core_generic",
"amount": 1
}]
}, {
"map_id": "byd_lostcivilization",
"position": 3,
"capture": 5,
"items": [{
"type": "core",
"id": "core_generic",
"amount": 1
}]
}, {
"map_id": "byd_lostcivilization",
"position": 4,
"capture": 100,
"items": [{
"type": "core",
"id": "core_generic",
"amount": 1
}]
}, {
"map_id": "byd_lostcivilization",
"position": 5,
"capture": 50,
"items": [{
"type": "fragment",
"amount": 525
}]
}, {
"map_id": "byd_lostcivilization",
"position": 6,
"capture": 0,
"items": [{
"id": "lostcivilization3",
"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

@@ -3,10 +3,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "core", "id": "core",
"_id": "6046bab736651a07ebc537db",
"is_available": true "is_available": true
}], }],
"price": 400, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -15,15 +14,13 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "shiawase", "id": "shiawase",
"_id": "6046bab736651a07ebc53823",
"is_available": true "is_available": true
}, { }, {
"type": "character", "type": "character",
"id": "kou", "id": "kou",
"_id": "6046bab736651a07ebc53822",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -32,15 +29,13 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "dynamix", "id": "dynamix",
"_id": "6046bab736651a07ebc53827",
"is_available": true "is_available": true
}, { }, {
"type": "character", "type": "character",
"id": "sapphire", "id": "sapphire",
"_id": "6046bab736651a07ebc53826",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -49,15 +44,13 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "mirai", "id": "mirai",
"_id": "6046bab736651a07ebc5381d",
"is_available": true "is_available": true
}, { }, {
"type": "character", "type": "character",
"id": "lethe", "id": "lethe",
"_id": "6046bab736651a07ebc5381c",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -66,10 +59,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "yugamu", "id": "yugamu",
"_id": "6046bab736651a07ebc537dc",
"is_available": true "is_available": true
}], }],
"price": 400, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -78,10 +70,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "lanota", "id": "lanota",
"_id": "6046bab736651a07ebc537e8",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -90,10 +81,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "nijuusei", "id": "nijuusei",
"_id": "6046bab736651a07ebc537dd",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -102,10 +92,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "rei", "id": "rei",
"_id": "6046bab736651a07ebc537f6",
"is_available": true "is_available": true
}], }],
"price": 400, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -114,10 +103,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "tonesphere", "id": "tonesphere",
"_id": "6046bab736651a07ebc537ea",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -126,10 +114,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "groovecoaster", "id": "groovecoaster",
"_id": "6046bab736651a07ebc53812",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -138,10 +125,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "zettai", "id": "zettai",
"_id": "6046bab736651a07ebc537f8",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -150,7 +136,6 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "chunithm", "id": "chunithm",
"_id": "6046bab736651a07ebc53806",
"is_available": true "is_available": true
}], }],
"price": 300, "price": 300,
@@ -160,10 +145,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "prelude", "id": "prelude",
"_id": "6046bab736651a07ebc53813",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 400,
"orig_price": 400, "orig_price": 400,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -172,10 +156,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "omatsuri", "id": "omatsuri",
"_id": "6046bab736651a07ebc537f9",
"is_available": true "is_available": true
}], }],
"price": 250, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -184,10 +167,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "vs", "id": "vs",
"_id": "6046bab736651a07ebc53809",
"is_available": true "is_available": true
}], }],
"price": 400, "price": 500,
"orig_price": 500, "orig_price": 500,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -196,10 +178,9 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "extend", "id": "extend",
"_id": "6046bab736651a07ebc53816",
"is_available": true "is_available": true
}], }],
"price": 500, "price": 700,
"orig_price": 700, "orig_price": 700,
"discount_from": 1615248000000, "discount_from": 1615248000000,
"discount_to": 1615852799000 "discount_to": 1615852799000
@@ -208,7 +189,6 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "alice", "id": "alice",
"_id": "6046bab736651a07ebc537fe",
"is_available": true "is_available": true
}], }],
"orig_price": 500, "orig_price": 500,
@@ -218,7 +198,6 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "alice_append_1", "id": "alice_append_1",
"_id": "6046bab736651a07ebc537e6",
"is_available": true "is_available": true
}], }],
"orig_price": 300, "orig_price": 300,
@@ -228,7 +207,6 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "ongeki", "id": "ongeki",
"_id": "6046bab736651a07ebc5380c",
"is_available": true "is_available": true
}], }],
"orig_price": 400, "orig_price": 400,
@@ -238,7 +216,6 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "maimai", "id": "maimai",
"_id": "6046bab736651a07ebc53819",
"is_available": true "is_available": true
}], }],
"orig_price": 400, "orig_price": 400,
@@ -248,9 +225,26 @@
"items": [{ "items": [{
"type": "pack", "type": "pack",
"id": "chunithm_append_1", "id": "chunithm_append_1",
"_id": "6046bab736651a07ebc537f2",
"is_available": true "is_available": true
}], }],
"orig_price": 300, "orig_price": 300,
"price": 300 "price": 300
}, {
"name": "observer_append_1",
"items": [{
"type": "pack",
"id": "observer_append_1",
"is_available": true
}],
"orig_price": 300,
"price": 300
}, {
"name": "observer",
"items": [{
"type": "pack",
"id": "observer",
"is_available": true
}],
"orig_price": 500,
"price": 500
}] }]

View File

@@ -12,7 +12,6 @@
"items": [{ "items": [{
"id": "dataerror", "id": "dataerror",
"type": "single", "type": "single",
"_id": "606f9f5636651a07ebc541f3",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -24,7 +23,6 @@
"items": [{ "items": [{
"id": "yourvoiceso", "id": "yourvoiceso",
"type": "single", "type": "single",
"_id": "606f9f5636651a07ebc54201",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -36,7 +34,6 @@
"items": [{ "items": [{
"id": "crosssoul", "id": "crosssoul",
"type": "single", "type": "single",
"_id": "606f9f5636651a07ebc5420e",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -48,7 +45,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "impurebird", "id": "impurebird",
"_id": "606f9f5636651a07ebc541f4",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -60,7 +56,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "auxesia", "id": "auxesia",
"_id": "606f9f5636651a07ebc5420f",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -72,7 +67,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "modelista", "id": "modelista",
"_id": "606f9f5636651a07ebc54202",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -84,7 +78,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "yozakurafubuki", "id": "yozakurafubuki",
"_id": "606f9f5636651a07ebc541f5",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -96,7 +89,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "surrender", "id": "surrender",
"_id": "606f9f5636651a07ebc54210",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -108,7 +100,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "metallicpunisher", "id": "metallicpunisher",
"_id": "606f9f5636651a07ebc54203",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -118,7 +109,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "carminescythe", "id": "carminescythe",
"_id": "606f9f5636651a07ebc541f6",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -128,7 +118,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "bethere", "id": "bethere",
"_id": "606f9f5636651a07ebc54211",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -140,7 +129,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "callmyname", "id": "callmyname",
"_id": "606f9f5636651a07ebc5421c",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -152,7 +140,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "fallensquare", "id": "fallensquare",
"_id": "606f9f5636651a07ebc541e9",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -164,7 +151,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "dropdead", "id": "dropdead",
"_id": "606f9f5636651a07ebc541f7",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -176,7 +162,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "alexandrite", "id": "alexandrite",
"_id": "606f9f5636651a07ebc54212",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -188,7 +173,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "astraltale", "id": "astraltale",
"_id": "606f9f5636651a07ebc54205",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -200,7 +184,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "phantasia", "id": "phantasia",
"_id": "606f9f5636651a07ebc541ea",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -210,7 +193,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "empireofwinter", "id": "empireofwinter",
"_id": "606f9f5636651a07ebc541f8",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -220,7 +202,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "libertas", "id": "libertas",
"_id": "606f9f5636651a07ebc5421e",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -232,7 +213,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "dottodot", "id": "dottodot",
"_id": "606f9f5636651a07ebc541eb",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -244,7 +224,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "dreadnought", "id": "dreadnought",
"_id": "606f9f5636651a07ebc5421f",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -256,7 +235,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "mirzam", "id": "mirzam",
"_id": "606f9f5636651a07ebc541ec",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -268,7 +246,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "heavenlycaress", "id": "heavenlycaress",
"_id": "606f9f5636651a07ebc541fa",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -280,7 +257,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "filament", "id": "filament",
"_id": "606f9f5636651a07ebc54215",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -292,7 +268,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "avantraze", "id": "avantraze",
"_id": "606f9f5636651a07ebc54220",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -302,7 +277,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "battlenoone", "id": "battlenoone",
"_id": "606f9f5636651a07ebc54208",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -314,7 +288,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "saikyostronger", "id": "saikyostronger",
"_id": "606f9f5636651a07ebc541ed",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -324,7 +297,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "izana", "id": "izana",
"_id": "606f9f5636651a07ebc541fb",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -336,7 +308,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "einherjar", "id": "einherjar",
"_id": "606f9f5636651a07ebc54216",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -348,7 +319,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "laqryma", "id": "laqryma",
"_id": "606f9f5636651a07ebc54221",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -360,7 +330,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "amygdata", "id": "amygdata",
"_id": "606f9f5636651a07ebc54209",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -372,7 +341,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "altale", "id": "altale",
"_id": "606f9f5636651a07ebc541ee",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -384,7 +352,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "feelssoright", "id": "feelssoright",
"_id": "606f9f5636651a07ebc54222",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -396,7 +363,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "scarletcage", "id": "scarletcage",
"_id": "606f9f5636651a07ebc5420a",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -406,7 +372,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "teriqma", "id": "teriqma",
"_id": "606f9f5636651a07ebc541ef",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -416,7 +381,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "mahoroba", "id": "mahoroba",
"_id": "606f9f5636651a07ebc541fd",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -428,7 +392,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "badtek", "id": "badtek",
"_id": "606f9f5636651a07ebc54223",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -440,7 +403,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "maliciousmischance", "id": "maliciousmischance",
"_id": "606f9f5636651a07ebc54218",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -450,7 +412,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "buchigireberserker", "id": "buchigireberserker",
"_id": "606f9f5636651a07ebc541f0",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -460,7 +421,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "galaxyfriends", "id": "galaxyfriends",
"_id": "606f9f5636651a07ebc541fe",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -472,7 +432,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "xeraphinite", "id": "xeraphinite",
"_id": "606f9f5636651a07ebc54219",
"is_available": true "is_available": true
}], }],
"orig_price": 100, "orig_price": 100,
@@ -482,7 +441,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "xanatos", "id": "xanatos",
"_id": "606f9f5636651a07ebc54224",
"is_available": true "is_available": true
}], }],
"price": 100, "price": 100,
@@ -492,7 +450,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "attraqtia", "id": "attraqtia",
"_id": "606f9f5636651a07ebc5420d",
"is_available": true "is_available": true
}], }],
"orig_price": 100, "orig_price": 100,
@@ -502,7 +459,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "gimmedablood", "id": "gimmedablood",
"_id": "606f9f5636651a07ebc541f2",
"is_available": true "is_available": true
}], }],
"orig_price": 100, "orig_price": 100,
@@ -512,7 +468,6 @@
"items": [{ "items": [{
"type": "single", "type": "single",
"id": "bassline", "id": "bassline",
"_id": "606f9f5636651a07ebc54200",
"is_available": true "is_available": true
}], }],
"orig_price": 100, "orig_price": 100,

View File

@@ -10,10 +10,12 @@ import server.setme
import server.arcscore import server.arcscore
import web.login import web.login
import web.index import web.index
import api.api_main
import server.arcworld 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
@@ -21,6 +23,95 @@ import sys
app = Flask(__name__) app = Flask(__name__)
wsgi_app = app.wsgi_app wsgi_app = app.wsgi_app
os.chdir(sys.path[0]) # 更改工作路径,以便于愉快使用相对路径
app.config.from_mapping(SECRET_KEY=Config.SECRET_KEY)
app.config['SESSION_TYPE'] = 'filesystem'
app.register_blueprint(web.login.bp)
app.register_blueprint(web.index.bp)
app.register_blueprint(api.api_main.bp)
log_dict = {
'version': 1,
'root': {
'level': 'INFO',
'handlers': ['wsgi', 'error_file']
},
'handlers': {
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
},
"error_file": {
"class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "ERROR",
"formatter": "default",
"filename": "./log/error.log"
}
},
'formatters': {
'default': {
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
}
}
}
if Config.ALLOW_LOG_INFO:
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file']
log_dict['handlers']['info_file'] = {
"class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "INFO",
"formatter": "default",
"filename": "./log/info.log"
}
dictConfig(log_dict)
if not server.init.check_before_run(app):
app.logger.error('Something wrong. The server will not run.')
input('Press ENTER key to exit.')
sys.exit()
app.logger.info("Start to initialize data in 'songfile' table...")
try:
error = server.arcdownload.initialize_songfile()
except:
error = 'Something wrong.'
if error:
app.logger.warning(error)
else:
app.logger.info('Complete!')
def add_url_prefix(url, strange_flag=False):
# 给url加前缀返回字符串
if not url or not Config.GAME_API_PREFIX:
return Config.GAME_API_PREFIX + url
prefix = Config.GAME_API_PREFIX
if prefix[0] != '/':
prefix = '/' + prefix
if prefix[-1] == '/':
prefix = prefix[:-1]
if url[0] != '/':
r = '/' + url
else:
r = url
if strange_flag and prefix.count('/') >= 1: # 为了方便处理双斜杠
t = prefix[::-1]
t = t[t.find('/')+1:]
prefix = t[::-1]
return prefix + r
def error_return(error_code): # 错误返回 def error_return(error_code): # 错误返回
# -7 处理交易时发生了错误 # -7 处理交易时发生了错误
@@ -74,6 +165,7 @@ def error_return(error_code): # 错误返回
@app.route('/') @app.route('/')
def hello(): def hello():
return "Hello World!" return "Hello World!"
# 自定义路径
@app.route('/favicon.ico', methods=['GET']) # 图标 @app.route('/favicon.ico', methods=['GET']) # 图标
@@ -85,8 +177,13 @@ def favicon():
return app.send_static_file('favicon.ico') return app.send_static_file('favicon.ico')
@app.route('/latte/13/auth/login', methods=['POST']) # 登录接口 @app.route(add_url_prefix('/auth/login'), methods=['POST']) # 登录接口
def login(): def login():
if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return jsonify({"success": False, "error_code": 5})
headers = request.headers headers = request.headers
id_pwd = headers['Authorization'] id_pwd = headers['Authorization']
id_pwd = base64.b64decode(id_pwd[6:]).decode() id_pwd = base64.b64decode(id_pwd[6:]).decode()
@@ -96,7 +193,8 @@ def login():
else: else:
device_id = 'low_version' device_id = 'low_version'
token, error_code = server.auth.arc_login(name, password, device_id) token, error_code = server.auth.arc_login(
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
@@ -105,27 +203,33 @@ def login():
return error_return(error_code) return error_return(error_code)
@app.route('/latte/13/user/', methods=['POST']) # 注册接口 @app.route(add_url_prefix('/user/'), methods=['POST']) # 注册接口
def register(): def register():
if 'AppVersion' in request.headers: # 版本检查
if Config.ALLOW_APPVERSION:
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return jsonify({"success": False, "error_code": 5})
name = request.form['name'] name = request.form['name']
password = request.form['password'] password = request.form['password']
email = request.form['email']
if 'device_id' in request.form: if 'device_id' in request.form:
device_id = request.form['device_id'] device_id = request.form['device_id']
else: else:
device_id = 'low_version' device_id = 'low_version'
user_id, token, error_code = server.auth.arc_register( user_id, token, error_code = server.auth.arc_register(
name, password, device_id) name, password, device_id, email, request.remote_addr)
if user_id is not None: if user_id is not None:
r = {"success": True, "value": { r = {"success": True, "value": {
'user_id': user_id, 'access_token': token}} 'user_id': user_id, 'access_token': token}}
return jsonify(r) return jsonify(r)
else: else:
return error_return(error_code) # 应该是101用户名被占用毕竟电子邮箱没记录 return error_return(error_code)
# 集成式请求,没想到什么好办法处理,就先这样写着 # 集成式请求,没想到什么好办法处理,就先这样写着
@app.route('/latte/13/compose/aggregate', methods=['GET']) @app.route(add_url_prefix('/compose/aggregate'), methods=['GET'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
def aggregate(user_id): def aggregate(user_id):
calls = request.args.get('calls') calls = request.args.get('calls')
@@ -136,7 +240,7 @@ def aggregate(user_id):
return jsonify(r) return jsonify(r)
@app.route('/latte/13/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):
character_id = request.form['character'] character_id = request.form['character']
@@ -155,7 +259,8 @@ def character_change(user_id):
return error_return(108) return error_return(108)
@app.route('/latte/<path:path>/toggle_uncap', 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: while '//' in path:
@@ -174,7 +279,42 @@ def character_uncap(user_id, path):
return error_return(108) return error_return(108)
@app.route('/latte/13/friend/me/add', methods=['POST']) # 加好友 # 角色觉醒
@app.route(add_url_prefix('/<path:path>/uncap', True), methods=['POST'])
@server.auth.auth_required(request)
def character_first_uncap(user_id, path):
while '//' in path:
path = path.replace('//', '/')
character_id = int(path[21:])
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):
while '//' in path:
path = path.replace('//', '/')
character_id = int(path[21:])
amount = int(request.form['amount'])
r = server.character.char_use_core(user_id, character_id, amount)
if r is not None:
return jsonify({
"success": True,
"value": r
})
else:
return error_return(108)
@app.route(add_url_prefix('/friend/me/add'), methods=['POST']) # 加好友
@server.auth.auth_required(request) @server.auth.auth_required(request)
def add_friend(user_id): def add_friend(user_id):
friend_code = request.form['friend_code'] friend_code = request.form['friend_code']
@@ -200,7 +340,7 @@ def add_friend(user_id):
return error_return(401) return error_return(401)
@app.route('/latte/13/friend/me/delete', methods=['POST']) # 删好友 @app.route(add_url_prefix('/friend/me/delete'), methods=['POST']) # 删好友
@server.auth.auth_required(request) @server.auth.auth_required(request)
def delete_friend(user_id): def delete_friend(user_id):
friend_id = int(request.form['friend_id']) friend_id = int(request.form['friend_id'])
@@ -222,7 +362,8 @@ def delete_friend(user_id):
return error_return(401) return error_return(401)
@app.route('/latte/13/score/song/friend', methods=['GET']) # 好友排名默认最多50 # 好友排名默认最多50
@app.route(add_url_prefix('/score/song/friend'), methods=['GET'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
def song_score_friend(user_id): def song_score_friend(user_id):
song_id = request.args.get('song_id') song_id = request.args.get('song_id')
@@ -237,7 +378,7 @@ def song_score_friend(user_id):
return error_return(108) return error_return(108)
@app.route('/latte/13/score/song/me', methods=['GET']) # 我的排名默认最多20 @app.route(add_url_prefix('/score/song/me'), methods=['GET']) # 我的排名默认最多20
@server.auth.auth_required(request) @server.auth.auth_required(request)
def song_score_me(user_id): def song_score_me(user_id):
song_id = request.args.get('song_id') song_id = request.args.get('song_id')
@@ -252,7 +393,7 @@ def song_score_me(user_id):
return error_return(108) return error_return(108)
@app.route('/latte/13/score/song', methods=['GET']) # TOP20 @app.route(add_url_prefix('/score/song'), methods=['GET']) # TOP20
@server.auth.auth_required(request) @server.auth.auth_required(request)
def song_score_top(user_id): def song_score_top(user_id):
song_id = request.args.get('song_id') song_id = request.args.get('song_id')
@@ -267,7 +408,7 @@ def song_score_top(user_id):
return error_return(108) return error_return(108)
@app.route('/latte/13/score/song', methods=['POST']) # 成绩上传 @app.route(add_url_prefix('/score/song'), methods=['POST']) # 成绩上传
@server.auth.auth_required(request) @server.auth.auth_required(request)
def song_score_post(user_id): def song_score_post(user_id):
song_token = request.form['song_token'] song_token = request.form['song_token']
@@ -291,22 +432,17 @@ 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)
@app.route('/latte/13/score/token', methods=['GET']) # 成绩上传所需的token显然我不想验证 # 成绩上传所需的token显然我不想验证
@app.route(add_url_prefix('/score/token'), methods=['GET'])
def score_token(): def score_token():
return jsonify({ return jsonify({
"success": True, "success": True,
@@ -317,7 +453,7 @@ def score_token():
# 世界模式成绩上传所需的token无验证 # 世界模式成绩上传所需的token无验证
@app.route('/latte/13/score/token/world', methods=['GET']) @app.route(add_url_prefix('/score/token/world'), methods=['GET'])
@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
@@ -332,7 +468,7 @@ def score_token_world(user_id):
}) })
@app.route('/latte/13/user/me/save', methods=['GET']) # 从云端同步 @app.route(add_url_prefix('/user/me/save'), methods=['GET']) # 从云端同步
@server.auth.auth_required(request) @server.auth.auth_required(request)
def cloud_get(user_id): def cloud_get(user_id):
r = server.arcscore.arc_all_get(user_id) r = server.arcscore.arc_all_get(user_id)
@@ -345,7 +481,7 @@ def cloud_get(user_id):
return error_return(108) return error_return(108)
@app.route('/latte/13/user/me/save', methods=['POST']) # 向云端同步 @app.route(add_url_prefix('/user/me/save'), methods=['POST']) # 向云端同步
@server.auth.auth_required(request) @server.auth.auth_required(request)
def cloud_post(user_id): def cloud_post(user_id):
scores_data = request.form['scores_data'] scores_data = request.form['scores_data']
@@ -366,7 +502,7 @@ def cloud_post(user_id):
}) })
@app.route('/latte/13/purchase/me/redeem', methods=['POST']) # 兑换码 @app.route(add_url_prefix('/purchase/me/redeem'), methods=['POST']) # 兑换码
@server.auth.auth_required(request) @server.auth.auth_required(request)
def redeem(user_id): def redeem(user_id):
code = request.form['code'] code = request.form['code']
@@ -388,7 +524,7 @@ def redeem(user_id):
# 礼物确认 # 礼物确认
@app.route('/latte/13/present/me/claim/<present_id>', methods=['POST']) @app.route(add_url_prefix('/present/me/claim/<present_id>'), methods=['POST'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
def claim_present(user_id, present_id): def claim_present(user_id, present_id):
flag = server.arcpurchase.claim_user_present(user_id, present_id) flag = server.arcpurchase.claim_user_present(user_id, present_id)
@@ -400,7 +536,8 @@ def claim_present(user_id, present_id):
return error_return(108) return error_return(108)
@app.route('/latte/13/purchase/me/item', methods=['POST']) # 购买world模式boost # 购买world模式boost
@app.route(add_url_prefix('/purchase/me/item'), methods=['POST'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
def prog_boost(user_id): def prog_boost(user_id):
re = {"success": False} re = {"success": False}
@@ -417,20 +554,19 @@ def prog_boost(user_id):
return jsonify(re) return jsonify(re)
@app.route('/latte/13/purchase/me/pack', methods=['POST']) # 曲包和单曲购买 @app.route(add_url_prefix('/purchase/me/pack'), methods=['POST']) # 曲包和单曲购买
@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_pack(user_id, request.form['pack_id'])) 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_single(user_id, request.form['single_id'])) return jsonify(server.arcpurchase.buy_thing(user_id, request.form['single_id']))
return jsonify({ return jsonify({"success": True})
"success": True
})
@app.route('/latte/13/purchase/bundle/single', methods=['GET']) # 单曲购买信息获取 # 单曲购买信息获取
@app.route(add_url_prefix('/purchase/bundle/single'), methods=['GET'])
def single(): def single():
return jsonify({ return jsonify({
"success": True, "success": True,
@@ -438,7 +574,7 @@ def single():
}) })
@app.route('/latte/13/world/map/me', methods=['GET']) # 获得世界模式信息,所有地图 @app.route(add_url_prefix('/world/map/me'), methods=['GET']) # 获得世界模式信息,所有地图
@server.auth.auth_required(request) @server.auth.auth_required(request)
def world_all(user_id): def world_all(user_id):
return jsonify({ return jsonify({
@@ -451,7 +587,7 @@ def world_all(user_id):
}) })
@app.route('/latte/13/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']
@@ -461,7 +597,8 @@ def world_in(user_id):
}) })
@app.route('/latte/13/world/map/me/<map_id>', methods=['GET']) # 获得单个地图完整信息 # 获得单个地图完整信息
@app.route(add_url_prefix('/world/map/me/<map_id>'), methods=['GET'])
@server.auth.auth_required(request) @server.auth.auth_required(request)
def world_one(user_id, map_id): def world_one(user_id, map_id):
server.arcworld.change_user_current_map(user_id, map_id) server.arcworld.change_user_current_map(user_id, map_id)
@@ -475,7 +612,7 @@ def world_one(user_id, map_id):
}) })
@app.route('/latte/13/serve/download/me/song', methods=['GET']) # 歌曲下载 @app.route(add_url_prefix('/serve/download/me/song'), methods=['GET']) # 歌曲下载
@server.auth.auth_required(request) @server.auth.auth_required(request)
def download_song(user_id): def download_song(user_id):
song_ids = request.args.getlist('sid') song_ids = request.args.getlist('sid')
@@ -511,7 +648,8 @@ def download(file_path):
return error_return(108) return error_return(108)
@app.route('/latte/<path:path>', 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[10:]
@@ -523,69 +661,6 @@ def sys_set(user_id, path):
def main(): def main():
os.chdir(sys.path[0]) # 更改工作路径,以便于愉快使用相对路径
app.config.from_mapping(SECRET_KEY=Config.SECRET_KEY)
app.config['SESSION_TYPE'] = 'filesystem'
app.register_blueprint(web.login.bp)
app.register_blueprint(web.index.bp)
log_dict = {
'version': 1,
'root': {
'level': 'INFO',
'handlers': ['wsgi', 'error_file']
},
'handlers': {
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
},
"error_file": {
"class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "ERROR",
"formatter": "default",
"filename": "./log/error.log"
}
},
'formatters': {
'default': {
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
}
}
}
if Config.ALLOW_LOG_INFO:
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file']
log_dict['handlers']['info_file'] = {
"class": "logging.handlers.RotatingFileHandler",
"maxBytes": 1024 * 1024,
"backupCount": 1,
"encoding": "utf-8",
"level": "INFO",
"formatter": "default",
"filename": "./log/info.log"
}
dictConfig(log_dict)
if not server.init.check_before_run(app):
app.logger.error('Something wrong. The server will not run.')
input('Press ENTER key to exit.')
sys.exit()
app.logger.info("Start to initialize data in 'songfile' table...")
try:
error = server.arcdownload.initialize_songfile()
except:
error = 'Something wrong.'
if error:
app.logger.warning(error)
else:
app.logger.info('Complete!')
if Config.SSL_CERT and Config.SSL_KEY: if Config.SSL_CERT and Config.SSL_KEY:
app.run(Config.HOST, Config.PORT, ssl_context=( app.run(Config.HOST, Config.PORT, ssl_context=(
Config.SSL_CERT, Config.SSL_KEY)) Config.SSL_CERT, Config.SSL_KEY))

View File

@@ -1,4 +1,6 @@
from server.sql import Connect from server.sql import Connect
import server.item
import server.character
import time import time
import json import json
@@ -11,28 +13,37 @@ 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.* 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]) if y:
}], for j in y:
"price": i[3], items.append({
"orig_price": i[4]} "type": j[1],
"id": j[0],
"is_available": int2b(j[2])
})
if i[5] > 0: r = {"name": i[0],
r['discount_from'] = i[5] "items": items,
if i[6] > 0: "price": i[1],
r['discount_to'] = i[6] "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)
@@ -43,7 +54,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
@@ -67,49 +78,65 @@ def buy_item(c, user_id, price):
return True, ticket - price return True, ticket - price
def buy_pack(user_id, pack_id): def buy_thing(user_id, purchase_id):
# 曲包购买,返回字典 # 购买物品接口,返回字典
re = {"success": False} success_flag = False
ticket = 0
packs = []
singles = []
characters = []
with Connect() as c: with Connect() as c:
c.execute('''select price from item where item_id = :a''', c.execute('''select price, orig_price, discount_from, discount_to from purchase where purchase_name=:a''',
{'a': pack_id}) {'a': purchase_id})
price = c.fetchone() x = c.fetchone()
if price: price = 0
price = price[0] flag = False
if x:
price = x[0]
orig_price = x[1]
discount_from = x[2]
discount_to = x[3]
else: else:
price = 0 return {
"success": False,
"error_code": 501
}
flag, ticket = buy_item(c, user_id, price) c.execute(
'''select item_id, type from purchase_item where purchase_name=:a''', {'a': purchase_id})
x = c.fetchall()
if x:
now = int(time.time() * 1000)
if not(discount_from <= now <= discount_to):
price = orig_price
if flag: flag, ticket = buy_item(c, user_id, price)
c.execute('''insert into user_item values(:a,:b,'pack')''',
{'a': user_id, 'b': pack_id})
re = {"success": True} if flag:
for i in x:
server.item.claim_user_item(c, user_id, i[0], i[1])
return re success_flag = True
def buy_single(user_id, single_id):
# 单曲购买,返回字典
re = {"success": False}
with Connect() as c:
c.execute('''select price from item where item_id = :a''',
{'a': single_id})
price = c.fetchone()
if price:
price = price[0]
else: else:
price = 0 return {
"success": False,
"error_code": 501
}
flag, ticket = buy_item(c, user_id, price) packs = server.item.get_user_items(c, user_id, 'pack')
singles = server.item.get_user_items(c, user_id, 'single')
characters = server.character.get_user_characters(c, user_id)
if flag: return {
c.execute('''insert into user_item values(:a,:b,'single')''', "success": success_flag,
{'a': user_id, 'b': single_id}) "value": {'user_id': user_id,
re = {"success": True} 'ticket': ticket,
'packs': packs,
return re 'singles': singles,
'characters': characters
}
}
def get_prog_boost(user_id): def get_prog_boost(user_id):

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,8 @@
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 os import os
@@ -152,6 +155,7 @@ def play_world_song(user_id, args):
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 +249,155 @@ 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 from user where user_id=?''', (user_id,))
x = c.fetchone()
character_id = x[0] if x else 0
c.execute('''select frag1,prog1,overdrive1,frag20,prog20,overdrive20,frag30,prog30,overdrive30 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])
else:
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 = 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 = 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 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": 12,
"max_stamina_ts": 1586274871917
}
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": 12,
"max_stamina_ts": 1586274871917
}
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

View File

@@ -1,12 +1,12 @@
import hashlib import hashlib
import time import time
import server.arcworld
from server.sql import Connect from server.sql import Connect
import functools import functools
from setting import Config from setting import Config
from flask import jsonify
def arc_login(name: str, password: str, device_id: str): # 登录判断 def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
# 查询数据库中的user表验证账号密码返回并记录token多返回个error code # 查询数据库中的user表验证账号密码返回并记录token多返回个error code
# token采用user_id和时间戳连接后hash生成真的是瞎想的没用bear # token采用user_id和时间戳连接后hash生成真的是瞎想的没用bear
# 密码和token的加密方式为 SHA-256 # 密码和token的加密方式为 SHA-256
@@ -43,8 +43,8 @@ def arc_login(name: str, password: str, device_id: str): # 登录判断
if not Config.ALLOW_LOGIN_SAME_DEVICE: if not Config.ALLOW_LOGIN_SAME_DEVICE:
if device_id in device_list: # 对相同设备进行删除 if device_id in device_list: # 对相同设备进行删除
c.execute('''delete from login where login_device=:a''', { c.execute('''delete from login where login_device=:a and user_id=:b''', {
'a': device_id}) 'a': device_id, 'b': user_id})
should_delete_num = len( should_delete_num = len(
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
@@ -52,8 +52,8 @@ def arc_login(name: str, password: str, device_id: str): # 登录判断
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)})
c.execute('''insert into login(access_token, user_id, login_device) values(:access_token, :user_id, :device_id)''', { c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
'user_id': user_id, 'access_token': token, 'device_id': device_id}) 'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
error_code = None error_code = None
else: else:
# 密码错误 # 密码错误
@@ -65,8 +65,8 @@ def arc_login(name: str, password: str, device_id: str): # 登录判断
return token, error_code return token, error_code
def arc_register(name: str, password: str, device_id: str): # 注册 def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册
# 账号注册,记录hash密码用户名生成user_id和user_code自动登录返回token # 账号注册记录hash密码用户名和邮箱生成user_id和user_code自动登录返回token
# token和密码的处理同登录部分 # token和密码的处理同登录部分
def build_user_code(c): def build_user_code(c):
@@ -91,13 +91,18 @@ def arc_register(name: str, password: str, device_id: 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
@@ -107,23 +112,28 @@ def arc_register(name: str, password: str, device_id: str): # 注册
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,):
user_code = build_user_code(c) c.execute(
user_id = build_user_id(c) '''select exists(select * from user where email = :email)''', {'email': email})
now = int(time.time() * 1000) if c.fetchone() == (0,):
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt, user_code = build_user_code(c)
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket, prog_boost) user_id = build_user_id(c)
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', :memories, 0) now = int(time.time() * 1000)
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd, 'memories': Config.DEFAULT_MEMORIES}) c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
c.execute('''insert into recent30(user_id) values(:user_id)''', { character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket, prog_boost, email)
'user_id': user_id}) values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', :memories, 0, :email)
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd, 'memories': Config.DEFAULT_MEMORIES, 'email': email})
c.execute('''insert into recent30(user_id) values(:user_id)''', {
'user_id': user_id})
token = hashlib.sha256( token = hashlib.sha256(
(str(user_id) + str(now)).encode("utf8")).hexdigest() (str(user_id) + str(now)).encode("utf8")).hexdigest()
c.execute('''insert into login(access_token, user_id, login_device) values(:access_token, :user_id, :device_id)''', { c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
'user_id': user_id, 'access_token': token, 'device_id': device_id}) 'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
insert_user_char(c, user_id) insert_user_char(c, user_id)
error_code = 0 error_code = 0
else:
error_code = 102
else: else:
error_code = 101 error_code = 101
@@ -166,17 +176,22 @@ def auth_required(request):
def wrapped_view(*args, **kwargs): def wrapped_view(*args, **kwargs):
user_id = None user_id = None
with Connect() as c: headers = request.headers
headers = request.headers
if 'Authorization' in headers: if 'AppVersion' in headers: # 版本检查
token = headers['Authorization'] if Config.ALLOW_APPVERSION:
token = token[7:] if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
user_id = token_get_id(token) return jsonify({"success": False, "error_code": 5})
if 'Authorization' in headers:
token = headers['Authorization']
token = token[7:]
user_id = token_get_id(token)
if user_id is not None: if user_id is not None:
return view(user_id, *args, **kwargs) return view(user_id, *args, **kwargs)
else: else:
return '''{"success":false,"error_code":108}''' return jsonify({"success": False, "error_code": 108})
return wrapped_view return wrapped_view
return decorator return decorator

View File

@@ -0,0 +1,255 @@
from setting import Config
from server.sql import Connect
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*250)
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:
for i in x:
server.item.claim_user_item(c, user_id, i[1], i[2], -i[3])
c.execute('''update user_char set is_uncapped=1, is_uncapped_override=0 where user_id=? and character_id=?''',
(user_id, character_id))
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,6 +2,8 @@ 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
@@ -46,47 +48,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,19 +72,27 @@ 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])
is_char_uncapped_override = int2b(z[1]) is_char_uncapped_override = int2b(z[1])
rating = y[5]
if int2b(y[10]):
rating = -1
s.append({ s.append({
"is_mutual": is_mutual, "is_mutual": is_mutual,
"is_char_uncapped_override": is_char_uncapped_override, "is_char_uncapped_override": is_char_uncapped_override,
"is_char_uncapped": is_char_uncapped, "is_char_uncapped": is_char_uncapped,
"is_skill_sealed": int2b(y[7]), "is_skill_sealed": int2b(y[7]),
"rating": y[5], "rating": rating,
"join_date": int(y[3]), "join_date": int(y[3]),
"character": character, "character": character,
"recent_score": get_recent_score(c, i[0]), "recent_score": get_recent_score(c, i[0]),
@@ -134,44 +103,44 @@ def get_user_friend(c, user_id):
return s return s
def get_user_singles(c, user_id): def get_user_me(c, user_id):
# 得到用户的单曲,返回列表 # 构造user/me的数据,返回字典
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_value_0(c, user_id):
# 构造value id=0的数据返回字典
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函数是为了节省一次查询
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:
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
@@ -181,7 +150,7 @@ def get_value_0(c, user_id):
"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])
}, },
@@ -190,7 +159,7 @@ def get_value_0(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],
@@ -198,16 +167,17 @@ def get_value_0(c, user_id):
"next_fragstam_ts": -1, "next_fragstam_ts": -1,
"max_stamina_ts": 1586274871917, "max_stamina_ts": 1586274871917,
"stamina": 12, "stamina": 12,
"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"], "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": world_rank
} }
return r return r
@@ -220,7 +190,7 @@ def arc_aggregate_small(user_id):
r = {"success": True, r = {"success": True,
"value": [{ "value": [{
"id": 0, "id": 0,
"value": get_value_0(c, user_id) "value": get_user_me(c, user_id)
}]} }]}
return r return r
@@ -230,16 +200,23 @@ 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_value_0(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": {
@@ -247,97 +224,7 @@ def arc_aggregate_big(user_id):
"stamina_recover_tick": 1800000, "stamina_recover_tick": 1800000,
"core_exp": 250, "core_exp": 250,
"curr_ts": int(time.time())*1000, "curr_ts": int(time.time())*1000,
"level_steps": [{ "level_steps": server.character.get_level_steps(),
"level": 1,
"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, "world_ranking_enabled": False,
"is_byd_chapter_unlocked": True "is_byd_chapter_unlocked": True
} }
@@ -346,11 +233,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

@@ -1,4 +1,23 @@
import os import os
from shutil import copy, copy2
from server.sql import Connect
from database.database_initialize import main, ARCAEA_SERVER_VERSION
from web.system import update_database
def try_rename(path, new_path):
# 尝试重命名文件,并尝试避免命名冲突,返回最终路径
final_path = new_path
if os.path.exists(new_path):
i = 1
while os.path.exists(new_path + str(i)):
i += 1
os.rename(path, new_path + str(i))
final_path = new_path + str(i)
else:
os.rename(path, new_path)
return final_path
def check_before_run(app): def check_before_run(app):
@@ -21,6 +40,56 @@ def check_before_run(app):
if not os.path.exists('database/arcaea_database.db'): if not os.path.exists('database/arcaea_database.db'):
app.logger.warning('File `database/arcaea_database.db` is missing.') app.logger.warning('File `database/arcaea_database.db` is missing.')
f = False f = False
try:
app.logger.info(
'Try to new the file `database/arcaea_database.db`.')
main('./database/')
app.logger.info(
'Success to new the file `database/arcaea_database.db`.')
f = True
except:
app.logger.warning(
'Fail to new the file `database/arcaea_database.db`.')
else:
with Connect() as c:
try:
c.execute('''select value from config where id="version"''')
x = c.fetchone()
except:
x = None
# 数据库自动更新,不强求
if not x or x[0] != ARCAEA_SERVER_VERSION:
app.logger.warning(
'Maybe the file `database/arcaea_database.db` is an old version.')
try:
app.logger.info(
'Try to update the file `database/arcaea_database.db`.')
path = try_rename('database/arcaea_database.db',
'database/arcaea_database.db.bak')
try:
copy2(path, 'database/arcaea_database.db')
except:
copy(path, 'database/arcaea_database.db')
if os.path.isfile("database/old_arcaea_database.db"):
os.remove('database/old_arcaea_database.db')
try_rename('database/arcaea_database.db',
'database/old_arcaea_database.db')
main('./database/')
update_database()
app.logger.info(
'Success to update the file `database/arcaea_database.db`.')
except:
app.logger.warning(
'Fail to update the file `database/arcaea_database.db`.')
if not os.path.exists('database/arcsong.db'): if not os.path.exists('database/arcsong.db'):
app.logger.warning('File `database/arcsong.db` is missing.') app.logger.warning('File `database/arcsong.db` is missing.')

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

@@ -29,3 +29,52 @@ class Connect():
traceback.format_exception(exc_type, exc_val, exc_tb)) traceback.format_exception(exc_type, exc_val, exc_tb))
return True return True
class Sql():
@staticmethod
def select(c, table_name, target_column=[], limit=-1, offset=0, query={}, sort=[]):
# 执行查询单句sql语句返回fetchall数据
# 使用准确查询,且在单表内
sql = 'select '
sql_dict = {}
if len(target_column) >= 2:
sql += target_column[0]
for i in range(1, len(target_column)):
sql += ',' + target_column[i]
sql += ' from ' + table_name
elif len(target_column) == 1:
sql += target_column[0] + ' from ' + table_name
else:
sql += '* from ' + table_name
where_field = []
where_value = []
for i in query:
where_field.append(i)
where_value.append(query[i])
if where_field and where_value:
sql += ' where '
sql += where_field[0] + '=:' + where_field[0]
sql_dict[where_field[0]] = where_value[0]
if len(where_field) >= 2:
for i in range(1, len(where_field)):
sql_dict[where_field[i]] = where_value[i]
sql += ' and ' + where_field[i] + '=:' + where_field[i]
if sort:
sql += ' order by ' + sort[0]['column'] + ' ' + sort[0]['order']
if len(sort) >= 2:
for i in range(1, len(sort)):
sql += ', ' + sort[i]['column'] + ' ' + sort[i]['order']
if limit >= 0:
sql += ' limit :limit offset :offset'
sql_dict['limit'] = limit
sql_dict['offset'] = offset
c.execute(sql, sql_dict)
return c.fetchall()

View File

@@ -14,6 +14,27 @@ class Config():
-------------------- --------------------
''' '''
'''
--------------------
游戏API地址前缀
Game API's URL prefix
'''
GAME_API_PREFIX = '/blockchain/14'
'''
--------------------
'''
'''
--------------------
允许使用的游戏版本,若为空,则默认全部允许
Allowed game versions
If it is blank, all are allowed.
'''
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.6.2', '3.6.2c']
'''
--------------------
'''
''' '''
-------------------- --------------------
SSL证书路径 SSL证书路径
@@ -37,6 +58,16 @@ class Config():
-------------------- --------------------
''' '''
'''
--------------------
世界排名的最大显示数量
The largest number of global rank
'''
WORLD_RANK_MAX = 200
'''
--------------------
'''
''' '''
-------------------- --------------------
Web后台管理页面的用户名和密码 Web后台管理页面的用户名和密码
@@ -59,6 +90,17 @@ class Config():
-------------------- --------------------
''' '''
'''
--------------------
API接口完全控制权限Token留空则不使用
API interface full control permission Token
If you don't want to use it, leave it blank.
'''
API_TOKEN = ''
'''
--------------------
'''
''' '''
-------------------- --------------------
歌曲下载地址前缀,留空则自动获取 歌曲下载地址前缀,留空则自动获取
@@ -120,6 +162,36 @@ class Config():
-------------------- --------------------
''' '''
'''
--------------------
是否全解锁搭档
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
'''
--------------------
'''
''' '''
-------------------- --------------------
是否强制使用全解锁云端存档 是否强制使用全解锁云端存档

View File

@@ -20,4 +20,4 @@
{% endfor %} {% block content %}{% endblock %} {% endfor %} {% block content %}{% endblock %}
</section> </section>
<footer id="footer" class="footer">Made by Lost@2020</footer> <footer id="footer" class="footer">Made by Lost@2020-2021</footer>

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

@@ -0,0 +1,57 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}All items{% 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 />
{% if not loop.last %}
<br />
{% endif %}
{% endfor %}
{% endif %}
</div>
{% if not loop.last %}
<br />
<hr />
<br />
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}

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

@@ -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,44 @@
{% 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</option>
<option value='pack'>Pack</option>
<option value='world_song'>World song</option>
<option value='character'>Character</option>
</select>
</div>
<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>
</select>
</div>
<br />
<input type="submit" value="Delete">
</form>
{% endblock %}

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

@@ -19,7 +19,7 @@
<input name="rating_ftr" id="rating_ftr" required> <input name="rating_ftr" id="rating_ftr" required>
<label for="rating_byd">Beyond chart const</label> <label for="rating_byd">Beyond chart const</label>
<input name="rating_byd" id="rating_byd" required> <input name="rating_byd" id="rating_byd" required>
<div class="content">如果没有某个面,应该填入-1。</div> <div class="content">如果没有某个面,应该填入-1。</div>
<div class="content">If there is no some chart, fill in -1 please.</div> <div class="content">If there is no some chart, fill in -1 please.</div>
<input type="submit" value="Add"> <input type="submit" value="Add">
</form> </form>

View File

@@ -13,9 +13,10 @@
<a href="{{ url_for('index.single_player_score') }}">单个玩家成绩查询 Single player score</a></br></br> <a href="{{ url_for('index.single_player_score') }}">单个玩家成绩查询 Single player score</a></br></br>
<a href="{{ url_for('index.single_player_ptt') }}">单个玩家PTT详情查询 Single player ptt</a></br></br> <a href="{{ url_for('index.single_player_ptt') }}">单个玩家PTT详情查询 Single player ptt</a></br></br>
<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

@@ -18,4 +18,26 @@
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.
</div> </div>
<br />
<hr />
<form action="/web/updatedatabase/refreshsonghash" method="post">
<div class="title">Refresh songs' hash</div>
<br />
<input type="submit" value="Refresh">
<div class="content">这里可以刷新储存在数据库中的songs文件夹下所有文件的Hash值。目的是应对歌曲谱面文件的更新。</div>
<div class="content">Here you can refresh the hash values of all the files stored in the songs folder in the
database. The purpose is to deal with the updating of songs' files.</div>
</form>
<br />
<hr />
<form action="/web/updatedatabase/refreshsongrating" method="post">
<div class="title">Refresh songs' rating</div>
<br />
<input type="submit" value="Refresh">
<div class="content">这里可以刷新数据库中所有成绩的评分。目的是应对歌曲谱面定数的更新。</div>
<div class="content">Here you can refresh the ratings of all scores in the database. The purpose is to deal
with the updating of songs' chart consts.</div>
</form>
{% endblock %} {% endblock %}

View File

@@ -10,6 +10,7 @@ import time
import server.arcscore import server.arcscore
import os import os
import json import json
from server.arcdownload import initialize_songfile
UPLOAD_FOLDER = 'database' UPLOAD_FOLDER = 'database'
ALLOWED_EXTENSIONS = {'db'} ALLOWED_EXTENSIONS = {'db'}
@@ -191,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.'
@@ -277,6 +278,30 @@ def update_database():
return render_template('web/updatedatabase.html') return render_template('web/updatedatabase.html')
@bp.route('/updatedatabase/refreshsonghash', methods=['POST'])
@login_required
def update_song_hash():
# 更新数据库内谱面文件hash值
error = initialize_songfile()
if error:
flash(error)
else:
flash('数据刷新成功 Success refresh data.')
return render_template('web/updatedatabase.html')
@bp.route('/updatedatabase/refreshsongrating', methods=['POST'])
@login_required
def update_song_rating():
# 更新所有分数的rating
error = server.arcscore.refresh_all_score_rating()
if error:
flash(error)
else:
flash('数据刷新成功 Success refresh data.')
return render_template('web/updatedatabase.html')
@bp.route('/changesong', methods=['GET']) @bp.route('/changesong', methods=['GET'])
@login_required @login_required
def change_song(): def change_song():
@@ -362,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.'
@@ -386,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'] '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']
return render_template('web/changechar.html', skill_ids=skill_ids) return render_template('web/changechar.html', skill_ids=skill_ids)
@@ -400,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)
@@ -430,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.')
@@ -640,16 +649,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)
@@ -657,25 +666,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)
@@ -698,43 +785,128 @@ def change_item():
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']
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))
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'])
@@ -1203,3 +1375,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

@@ -161,17 +161,18 @@ 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():
@@ -189,14 +190,22 @@ 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, '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, 'role')
update_one_table(c1, c2, 'user_role')
update_one_table(c1, c2, 'power')
update_one_table(c1, c2, 'role_power')
update_one_table(c1, c2, 'api_auth')
update_user_char(c2) update_one_table(c1, c2, 'user_char')
update_user_char(c2) # 更新user_char_full
os.remove('database/old_arcaea_database.db') os.remove('database/old_arcaea_database.db')
@@ -244,30 +253,54 @@ def unlock_user_item(c, user_id):
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]})
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
@@ -471,7 +504,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})

View File

@@ -1,11 +1,11 @@
import time import time
def get_user_score(c, user_id, limit=-1): def get_user_score(c, user_id, limit=-1, offset=0):
# 返回用户的所有歌曲数据,带排名,返回字典列表 # 返回用户的所有歌曲数据,带排名,返回字典列表
if limit >= 0: if limit >= 0:
c.execute('''select * from best_score where user_id =:a order by rating DESC limit :b''', c.execute('''select * from best_score where user_id =:a order by rating DESC limit :b offset :c''',
{'a': user_id, 'b': limit}) {'a': user_id, 'b': limit, 'c': offset})
else: else:
c.execute( c.execute(
'''select * from best_score where user_id =:a order by rating DESC''', {'a': user_id}) '''select * from best_score where user_id =:a order by rating DESC''', {'a': user_id})
@@ -77,7 +77,7 @@ def get_user(c, user_id):
def get_user_recent30(c, user_id): def get_user_recent30(c, user_id):
# 获取玩家recent30信息并计算这一部分的ptt返回字典列表和一个值 # 获取玩家recent30信息并计算recent10的ptt返回字典列表和一个值
c.execute('''select * from recent30 where user_id=:a''', {'a': user_id}) c.execute('''select * from recent30 where user_id=:a''', {'a': user_id})
sumr = 0 sumr = 0
x = c.fetchone() x = c.fetchone()