mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-09 01:07:27 +08:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5068c7215c | ||
|
|
99f05cabb7 | ||
|
|
1a097bc4d0 | ||
|
|
849f4f7260 | ||
|
|
add81ee639 | ||
|
|
b927ad23f8 | ||
|
|
8bc74b36c7 | ||
|
|
7dc8bfea9a | ||
|
|
d0be49fe20 | ||
|
|
b494b93c14 | ||
|
|
a523ff5aae | ||
|
|
acbe9fc4a1 | ||
|
|
9422bf7182 | ||
|
|
53acad4ffb | ||
|
|
13048e57f4 | ||
|
|
39d3f073ba | ||
|
|
2cf6595478 | ||
|
|
7c3bc99570 |
28
README.md
28
README.md
@@ -21,6 +21,7 @@ This procedure is mainly used for study and research, and shall not be used for
|
||||
- PTT
|
||||
- 世界排名 Global rank
|
||||
- 排名 Rank
|
||||
- Link Play
|
||||
- 好友系统 Friends
|
||||
- 数据同步 Data synchronization
|
||||
- 爬梯 Climbing steps
|
||||
@@ -53,7 +54,9 @@ It is just so interesting. What it can do is under exploration.
|
||||
## 下载 Download
|
||||
[这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases)
|
||||
|
||||
[Arcaea](https://konmai.cn/#arcaea)
|
||||
[Arcaea-CN official](https://arcaea.lowiro.com/zh)
|
||||
[Arcaea-lowi.ro](https://lowi.ro)
|
||||
[Arcaea-RhyDown](https://rhydown.com)
|
||||
|
||||
## 更新日志 Update log
|
||||
只保留最新版本 Only keep the latest version.
|
||||
@@ -62,18 +65,27 @@ It is just so interesting. What it can do is under exploration.
|
||||
>
|
||||
> Tips: When updating, please keep the original database in case of data loss.
|
||||
|
||||
### Version 2.6.1
|
||||
- 适用于Arcaea 3.7.0版本 For Arcaea 3.7.0
|
||||
|
||||
### Version 2.8
|
||||
- 适用于Arcaea 3.11.2版本 For Arcaea 3.11.2
|
||||
- 更新了歌曲数据库 Update the song database.
|
||||
- 新增活动图设置 Add available event maps setting.
|
||||
- 修复了一个Bug Fix a bug.
|
||||
- 添加了`user/me`接口 Add `user/me` interface.
|
||||
- 新增对Link Play的支持 Add support for Link Play.
|
||||
- 搭档**彩梦**已觉醒 Uncap the character **ayu**.
|
||||
|
||||
- 以下是累积更新 The following are cumulative updates:
|
||||
- 修复注册接口端点多了个杠的问题 Fix an unexpected slash at the registered interface endpoint.
|
||||
- 新搭档**白姬**已解锁 Unlock the character **shirahime**.
|
||||
- 修正两个角色的数值问题 Fix the values of two characters.
|
||||
- 新搭档**玛莉嘉**已解锁 Unlock the character **marija**.
|
||||
|
||||
> 提示:Link Play可能存在大量bug
|
||||
> Tips: There may be many bugs in Link Play system.
|
||||
|
||||
|
||||
## 运行环境与依赖 Running environment and requirements
|
||||
- Windows/Linux/Mac OS/Android
|
||||
- Windows/Linux/Mac OS/Android...
|
||||
- Python 3
|
||||
- Flask module
|
||||
- Flask module, Cryptography module
|
||||
- Charles, IDA, proxy app... (optional)
|
||||
|
||||
<!--
|
||||
|
||||
0
latest version/api/__init__.py
Normal file
0
latest version/api/__init__.py
Normal file
@@ -1,5 +1,7 @@
|
||||
import hashlib
|
||||
import base64
|
||||
import time
|
||||
import random
|
||||
from server.sql import Connect
|
||||
import functools
|
||||
from setting import Config
|
||||
@@ -14,14 +16,50 @@ class User():
|
||||
self.power = power
|
||||
|
||||
|
||||
def login():
|
||||
# 登录接口
|
||||
return {'token': 1}, 0
|
||||
def login(auth: str, ip: str):
|
||||
# 登录接口,返回字典和错误码
|
||||
|
||||
try:
|
||||
auth_decode = bytes.decode(base64.b64decode(auth))
|
||||
except:
|
||||
return {}, -100
|
||||
|
||||
if not ':' in auth_decode:
|
||||
return {}, -100
|
||||
|
||||
name, password = auth_decode.split(':', 1)
|
||||
|
||||
with Connect() as c:
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute('''select user_id, password from user where name = :name''', {
|
||||
'name': name})
|
||||
x = c.fetchone()
|
||||
if x is None:
|
||||
return {}, -201
|
||||
if x[1] == '':
|
||||
return {}, -202
|
||||
if hash_pwd != x[1]:
|
||||
return {}, -201
|
||||
|
||||
user_id = str(x[0])
|
||||
now = int(time.time() * 1000)
|
||||
token = hashlib.sha256(
|
||||
(user_id + str(random.randint(10000, 99999)) + str(now)).encode("utf8")).hexdigest()
|
||||
c.execute('''delete from api_login where user_id=?''', (user_id,))
|
||||
c.execute('''insert into api_login values(?,?,?,?)''',
|
||||
(user_id, token, now, ip))
|
||||
|
||||
return {'token': token, 'user_id': user_id}, 0
|
||||
|
||||
|
||||
def logout():
|
||||
# 登出接口
|
||||
pass
|
||||
def logout(user: User):
|
||||
# 登出接口,返回错误码
|
||||
code = 999
|
||||
with Connect() as c:
|
||||
c.execute('''delete from api_login where user_id=?''', (user.user_id,))
|
||||
code = 0
|
||||
|
||||
return code
|
||||
|
||||
|
||||
def id_get_role_id(c, user_id):
|
||||
@@ -86,7 +124,7 @@ def role_required(request, power=[]):
|
||||
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'})
|
||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No token'})
|
||||
|
||||
user = User()
|
||||
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
|
||||
@@ -98,7 +136,7 @@ def role_required(request, power=[]):
|
||||
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': ''})
|
||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No token'})
|
||||
|
||||
role_id = id_get_role_id(c, user.user_id)
|
||||
user.role = role_id_get_role(c, role_id)
|
||||
|
||||
@@ -6,10 +6,14 @@ def code_get_msg(code):
|
||||
'-2': 'No data',
|
||||
'-3': 'No data or user',
|
||||
'-4': 'No user_id',
|
||||
'-100': 'Wrong post data',
|
||||
'-101': 'Wrong data type',
|
||||
'-102': 'Wrong query parameter',
|
||||
'-103': 'Wrong sort parameter',
|
||||
'-104': 'Wrong sort order parameter'
|
||||
'-104': 'Wrong sort order parameter',
|
||||
'-201': 'Wrong username or password',
|
||||
'-202': 'User is banned',
|
||||
'-999': 'Unknown error'
|
||||
}
|
||||
|
||||
return msg[str(code)]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from flask import (
|
||||
Blueprint, request, jsonify
|
||||
)
|
||||
import functools
|
||||
import api.api_auth
|
||||
import api.users
|
||||
import api.songs
|
||||
@@ -10,93 +11,112 @@ from api.api_code import code_get_msg
|
||||
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||
|
||||
|
||||
class Query():
|
||||
# 查询类,当查询附加参数的数据类型用
|
||||
def __init__(self, limit=-1, offset=0, query={}, sort=[]) -> None:
|
||||
self.limit = limit
|
||||
self.offset = offset
|
||||
self.query = query # {'name': 'admin'}
|
||||
self.sort = sort # [{'column': 'user_id', 'order': 'ASC'}, ...]
|
||||
|
||||
|
||||
def get_query_parameter(request, query_able=[], sort_able=[]):
|
||||
# 提取查询请求参数,返回四个参数和code
|
||||
# 提取查询请求参数,返回Query类查询参数,写成修饰器
|
||||
|
||||
limit = -1
|
||||
offset = 0
|
||||
query = {} # {'name': 'admin'}
|
||||
sort = [] # [{'column': 'user_id', 'order': 'ASC'}, ...]
|
||||
def decorator(view):
|
||||
@functools.wraps(view)
|
||||
def wrapped_view(*args, **kwargs):
|
||||
|
||||
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
|
||||
re = Query()
|
||||
|
||||
return limit, offset, query, sort, 0
|
||||
if 'limit' in request.json:
|
||||
try:
|
||||
re.limit = int(request.json['limit'])
|
||||
except:
|
||||
return jsonify({'status': 200, 'code': -101, 'data': {}, 'msg': code_get_msg(-101)})
|
||||
|
||||
if 'offset' in request.json:
|
||||
try:
|
||||
re.offset = int(request.json['offset'])
|
||||
except:
|
||||
return jsonify({'status': 200, 'code': -101, 'data': {}, 'msg': code_get_msg(-101)})
|
||||
if 'query' in request.json:
|
||||
re.query = request.json['query']
|
||||
for i in re.query:
|
||||
if i not in query_able:
|
||||
return jsonify({'status': 200, 'code': -102, 'data': {}, 'msg': code_get_msg(-102)})
|
||||
if 'sort' in request.json:
|
||||
re.sort = request.json['sort']
|
||||
for i in re.sort:
|
||||
if 'column' not in i or i['column'] not in sort_able:
|
||||
return jsonify({'status': 200, 'code': -103, 'data': {}, 'msg': code_get_msg(-103)})
|
||||
if not 'order' in i:
|
||||
i['order'] = 'ASC'
|
||||
else:
|
||||
if i['order'] not in ['ASC', 'DESC']:
|
||||
return jsonify({'status': 200, 'code': -104, 'data': {}, 'msg': code_get_msg(-104)})
|
||||
|
||||
return view(re, *args, **kwargs)
|
||||
|
||||
return wrapped_view
|
||||
return decorator
|
||||
|
||||
|
||||
def return_encode(code: int = 0, data: dict = {}, status: int = 200, msg: str = ''):
|
||||
# 构造返回,返回jsonify处理过后的response_class
|
||||
if msg == '':
|
||||
msg = code_get_msg(code)
|
||||
if code < 0:
|
||||
return jsonify({'status': status, 'code': code, 'data': {}, 'msg': msg})
|
||||
else:
|
||||
return jsonify({'status': status, 'code': code, 'data': data, 'msg': msg})
|
||||
|
||||
|
||||
@bp.route('/')
|
||||
def ping():
|
||||
return jsonify({'status': 200, 'code': 0, 'data': {}, 'msg': ''})
|
||||
return return_encode()
|
||||
|
||||
|
||||
@bp.route('/token', methods=['POST'])
|
||||
def token_post():
|
||||
# 登录,获取token
|
||||
# {'auth': `base64(user_id:password)`}
|
||||
|
||||
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': ''})
|
||||
str(request.json['auth']), request.remote_addr)
|
||||
return return_encode(code, data)
|
||||
else:
|
||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No authentication'})
|
||||
return return_encode(-1, {}, 401, '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': ''})
|
||||
return return_encode()
|
||||
|
||||
|
||||
@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': ''})
|
||||
return return_encode(api.api_auth.logout(user))
|
||||
|
||||
|
||||
@bp.route('/users', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select'])
|
||||
def users_get(user):
|
||||
@get_query_parameter(request, ['user_id', 'name', 'user_code'], [
|
||||
'user_id', 'name', 'user_code', 'join_date', 'rating_ptt', 'time_played', 'ticket', 'world_rank_score'])
|
||||
def users_get(query, user):
|
||||
# 查询全用户信息
|
||||
|
||||
limit, offset, query, sort, code = get_query_parameter(request, ['user_id', 'name', 'user_code'], [
|
||||
'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)
|
||||
data = api.users.get_users(query)
|
||||
|
||||
if not data:
|
||||
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
|
||||
return return_encode(-2)
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>', methods=['GET'])
|
||||
@@ -108,17 +128,17 @@ def users_user_get(user, user_id):
|
||||
user_id = user.user_id
|
||||
|
||||
if user_id <= 0:
|
||||
return jsonify({'status': 200, 'code': -4, 'data': {}, 'msg': code_get_msg(-4)})
|
||||
return return_encode(-4)
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
return return_encode(-1, {}, 403, 'No permission')
|
||||
|
||||
data = api.users.get_user_info(user_id)
|
||||
|
||||
if not data:
|
||||
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
|
||||
return return_encode(-3)
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/b30', methods=['GET'])
|
||||
@@ -126,48 +146,39 @@ def users_user_get(user, user_id):
|
||||
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)})
|
||||
return return_encode(-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'})
|
||||
return return_encode(-1, {}, 403, '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 return_encode(-3)
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/best', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
||||
def users_user_best_get(user, user_id):
|
||||
@get_query_parameter(request, ['song_id', 'difficulty'], [
|
||||
'song_id', 'difficulty', 'score', 'time_played', 'rating'])
|
||||
def users_user_best_get(query, user, user_id):
|
||||
# 查询用户所有best成绩
|
||||
|
||||
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)})
|
||||
return return_encode(-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'})
|
||||
return return_encode(-1, {}, 403, '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)
|
||||
data = api.users.get_user_best(user_id, query)
|
||||
|
||||
if data['data'] == []:
|
||||
return jsonify({'status': 200, 'code': -3, 'data': {}, 'msg': code_get_msg(-3)})
|
||||
return return_encode(-3)
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/r30', methods=['GET'])
|
||||
@@ -175,21 +186,18 @@ def users_user_best_get(user, user_id):
|
||||
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)})
|
||||
return return_encode(-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'})
|
||||
return return_encode(-1, {}, 403, '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 return_encode(-3)
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/songs/<string:song_id>', methods=['GET'])
|
||||
@@ -200,24 +208,21 @@ 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 return_encode(-2)
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/songs', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_song_info'])
|
||||
def songs_get(user):
|
||||
@get_query_parameter(request, ['sid', 'name_en', 'name_jp', 'pakset', 'artist'], [
|
||||
'sid', 'name_en', 'name_jp', 'pakset', 'artist', 'date', 'rating_pst', 'rating_prs', 'rating_ftr', 'rating_byn'])
|
||||
def songs_get(query, user):
|
||||
# 查询全歌曲信息
|
||||
|
||||
limit, offset, query, sort, code = get_query_parameter(request, ['sid', 'name_en', 'name_jp', 'pakset', 'artist'], [
|
||||
'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)
|
||||
data = api.songs.get_songs(query)
|
||||
|
||||
if not data:
|
||||
return jsonify({'status': 200, 'code': -2, 'data': {}, 'msg': code_get_msg(-2)})
|
||||
return return_encode(-2)
|
||||
|
||||
return jsonify({'status': 200, 'code': 0, 'data': data, 'msg': ''})
|
||||
return return_encode(0, data)
|
||||
|
||||
@@ -30,12 +30,12 @@ def get_song_info(song_id):
|
||||
return r
|
||||
|
||||
|
||||
def get_songs(limit=-1, offset=0, query={}, sort=[]):
|
||||
def get_songs(query=None):
|
||||
# 查询全部歌曲信息,返回字典列表
|
||||
r = []
|
||||
|
||||
with Connect('./database/arcsong.db') as c:
|
||||
x = Sql.select(c, 'songs', [], limit, offset, query, sort)
|
||||
x = Sql.select(c, 'songs', [], query)
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
|
||||
@@ -5,12 +5,12 @@ import web.webscore
|
||||
import server.info
|
||||
|
||||
|
||||
def get_users(limit=-1, offset=0, query={}, sort=[]):
|
||||
def get_users(query=None):
|
||||
# 获取全用户信息,返回字典列表
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
x = Sql.select(c, 'user', [], limit, offset, query, sort)
|
||||
x = Sql.select(c, 'user', [], query)
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
@@ -62,12 +62,12 @@ def get_user_b30(user_id):
|
||||
return {'user_id': user_id, 'b30_ptt': bestptt / 30, 'data': r}
|
||||
|
||||
|
||||
def get_user_best(user_id, limit=-1, offset=0, query={}, sort=[]):
|
||||
def get_user_best(user_id, query=None):
|
||||
# 获取用户b30信息,返回字典
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
x = Sql.select(c, 'best_score', [], limit, offset, query, sort)
|
||||
x = Sql.select(c, 'best_score', [], query)
|
||||
if x:
|
||||
for i in x:
|
||||
r.append({
|
||||
|
||||
0
latest version/database/__init__.py
Normal file
0
latest version/database/__init__.py
Normal file
Binary file not shown.
@@ -4,7 +4,7 @@ import json
|
||||
|
||||
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
||||
|
||||
ARCAEA_SERVER_VERSION = 'v2.6.1'
|
||||
ARCAEA_SERVER_VERSION = 'v2.8'
|
||||
|
||||
|
||||
def main(path='./'):
|
||||
@@ -228,6 +228,7 @@ def main(path='./'):
|
||||
c.execute('''create table if not exists purchase_item(purchase_name text,
|
||||
item_id text,
|
||||
type text,
|
||||
amount int,
|
||||
primary key(purchase_name, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_save(user_id int primary key,
|
||||
@@ -298,46 +299,46 @@ def main(path='./'):
|
||||
|
||||
# 搭档初始化
|
||||
char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'stella', 'Hikari & Fisica', 'ilith', 'eto', 'luna', 'shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
|
||||
'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', '???', 'nami']
|
||||
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija']
|
||||
|
||||
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', 'gauge_safe_10', 'frags_nami']
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes']
|
||||
|
||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
|
||||
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
|
||||
'', '', '', '', '', 'ayu_uncap', '', '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, 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, 0, 0, 0, 0, 0, 0, 0, 8]
|
||||
|
||||
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
||||
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 50, 25]
|
||||
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 50, 25, 58, 50, 61, 45, 45, 38]
|
||||
|
||||
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
||||
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50]
|
||||
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58]
|
||||
|
||||
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
||||
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 50, 30]
|
||||
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 50, 30, 49, 15, 34, 45, 45, 38]
|
||||
|
||||
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
||||
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 100, 50]
|
||||
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60]
|
||||
|
||||
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
|
||||
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 100, 55]
|
||||
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90]
|
||||
|
||||
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
|
||||
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 100, 40]
|
||||
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60]
|
||||
|
||||
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, 100, 50]
|
||||
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
||||
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60]
|
||||
|
||||
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, 100, 55]
|
||||
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
|
||||
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90]
|
||||
|
||||
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, 100, 40]
|
||||
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
|
||||
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60]
|
||||
|
||||
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 0, 0]
|
||||
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
|
||||
char_core = {
|
||||
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
||||
@@ -352,13 +353,16 @@ def main(path='./'):
|
||||
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}]
|
||||
43: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
11: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_hollow', 'amount': 5}],
|
||||
12: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
||||
19: [{'core_id': 'core_colorful', 'amount': 30}]
|
||||
}
|
||||
|
||||
for i in range(0, 48):
|
||||
for i in range(0, 54):
|
||||
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]:
|
||||
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43, 11, 12, 19]:
|
||||
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]))
|
||||
@@ -368,18 +372,21 @@ def main(path='./'):
|
||||
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]))
|
||||
|
||||
c.execute('''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)''', (99,
|
||||
'shirahime', 38, 33, 28, 66, 58, 50, 66, 58, 50, 'frags_preferred_song', 0, 0, '', 0))
|
||||
|
||||
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']
|
||||
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful']
|
||||
|
||||
for i in cores:
|
||||
c.execute('''insert into item values(?,"core",1,'')''', (i,))
|
||||
|
||||
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
|
||||
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion"]
|
||||
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3"]
|
||||
for i in world_songs:
|
||||
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
|
||||
|
||||
@@ -411,10 +418,17 @@ def main(path='./'):
|
||||
_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']))
|
||||
c.execute(
|
||||
'''select exists(select * from item where item_id=?)''', (j['id'],))
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into item values(?,?,?,?)''',
|
||||
(j['id'], j['type'], j['is_available'], _id))
|
||||
if 'amount' in j:
|
||||
amount = j['amount']
|
||||
else:
|
||||
amount = 1
|
||||
c.execute('''insert into purchase_item values(?,?,?,?)''',
|
||||
(i['name'], j['id'], j['type'], amount))
|
||||
|
||||
# item初始化
|
||||
f = open(path+'singles.json', 'r')
|
||||
|
||||
376
latest version/database/map/byd_bookmaker.json
Normal file
376
latest version/database/map/byd_bookmaker.json
Normal file
@@ -0,0 +1,376 @@
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"is_legacy": false,
|
||||
"chapter": 1001,
|
||||
"available_from": -1,
|
||||
"available_to": 9999999999999,
|
||||
"is_repeatable": false,
|
||||
"require_id": "bookmaker2",
|
||||
"require_type": "chart_unlock",
|
||||
"coordinate": "-650,-650",
|
||||
"is_beyond": true,
|
||||
"stamina_cost": 3,
|
||||
"beyond_health": 300,
|
||||
"character_affinity": [
|
||||
23,
|
||||
34,
|
||||
47
|
||||
],
|
||||
"affinity_multiplier": [
|
||||
1.3,
|
||||
5,
|
||||
3.8
|
||||
],
|
||||
"step_count": 31,
|
||||
"custom_bg": "",
|
||||
"curr_position": 3,
|
||||
"curr_capture": 35.81730523472207,
|
||||
"is_locked": false,
|
||||
"steps": [
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 0,
|
||||
"capture": 10
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 1,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 2,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 3,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
],
|
||||
"restrict_ids": [
|
||||
"bookmaker",
|
||||
"viciousheroism",
|
||||
"battlenoone",
|
||||
"galaxyfriends",
|
||||
"rekkaresonance"
|
||||
],
|
||||
"restrict_type": "song_id"
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 4,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 5,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 6,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 7,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 8,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 9,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 10,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 11,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 12,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 13,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 14,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 15,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 16,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 17,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 18,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 19,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 20,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 21,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 22,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 23,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 24,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 25,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 26,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 27,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 28,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 29,
|
||||
"capture": 10,
|
||||
"items": [
|
||||
{
|
||||
"type": "fragment",
|
||||
"amount": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"map_id": "byd_bookmaker",
|
||||
"position": 30,
|
||||
"capture": 0,
|
||||
"items": [
|
||||
{
|
||||
"id": "bookmaker3",
|
||||
"type": "world_song"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,259 +1,556 @@
|
||||
[{
|
||||
"name": "core",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "core",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "shiawase",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "shiawase",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "kou",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "dynamix",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "dynamix",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "sapphire",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "mirai",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "mirai",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "lethe",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "yugamu",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "yugamu",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "lanota",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "lanota",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "nijuusei",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "nijuusei",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "rei",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "rei",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "tonesphere",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "tonesphere",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "groovecoaster",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "groovecoaster",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "zettai",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "zettai",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "chunithm",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "chunithm",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 300,
|
||||
"orig_price": 300
|
||||
}, {
|
||||
"name": "prelude",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "prelude",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 400,
|
||||
"orig_price": 400,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "omatsuri",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "omatsuri",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "vs",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "vs",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "extend",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "extend",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 700,
|
||||
"orig_price": 700,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
}, {
|
||||
"name": "alice",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "alice",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
}, {
|
||||
"name": "alice_append_1",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "alice_append_1",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
}, {
|
||||
"name": "ongeki",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "ongeki",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 400,
|
||||
"price": 400
|
||||
}, {
|
||||
"name": "maimai",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "maimai",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 400,
|
||||
"price": 400
|
||||
}, {
|
||||
"name": "chunithm_append_1",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "chunithm_append_1",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_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
|
||||
}, {
|
||||
"name": "observer_append_2",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "observer_append_2",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
}]
|
||||
[
|
||||
{
|
||||
"name": "core",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "core",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "shiawase",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "shiawase",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "character",
|
||||
"id": "kou",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "dynamix",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "dynamix",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "character",
|
||||
"id": "sapphire",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "mirai",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "mirai",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "character",
|
||||
"id": "lethe",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "yugamu",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "yugamu",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "lanota",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "lanota",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "nijuusei",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "nijuusei",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "rei",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "rei",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "tonesphere",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "tonesphere",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "groovecoaster",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "groovecoaster",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "zettai",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "zettai",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "chunithm",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "chunithm",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 3,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 300,
|
||||
"orig_price": 300
|
||||
},
|
||||
{
|
||||
"name": "prelude",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "prelude",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 4,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 400,
|
||||
"orig_price": 400,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "omatsuri",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "omatsuri",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "vs",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "vs",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "extend",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "extend",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 7,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 0,
|
||||
"orig_price": 700,
|
||||
"discount_from": 1615248000000,
|
||||
"discount_to": 1615852799000
|
||||
},
|
||||
{
|
||||
"name": "alice",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "alice",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
},
|
||||
{
|
||||
"name": "alice_append_1",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "alice_append_1",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 3,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
},
|
||||
{
|
||||
"name": "ongeki",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "ongeki",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 4,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 400,
|
||||
"price": 400
|
||||
},
|
||||
{
|
||||
"name": "maimai",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "maimai",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 4,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 400,
|
||||
"price": 400
|
||||
},
|
||||
{
|
||||
"name": "chunithm_append_1",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "chunithm_append_1",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 3,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
},
|
||||
{
|
||||
"name": "observer_append_1",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "observer_append_1",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 3,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
},
|
||||
{
|
||||
"name": "observer",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "observer",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
},
|
||||
{
|
||||
"name": "observer_append_2",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "observer_append_2",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 3,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
},
|
||||
{
|
||||
"name": "wacca",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "wacca",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
},
|
||||
{
|
||||
"name": "nijuusei_append_1",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "nijuusei_append_1",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 3,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 300,
|
||||
"price": 300
|
||||
},
|
||||
{
|
||||
"name": "dividedheart",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "dividedheart",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
},
|
||||
{
|
||||
"name": "musedash",
|
||||
"items": [
|
||||
{
|
||||
"type": "pack",
|
||||
"id": "musedash",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "character",
|
||||
"id": "marija",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 5,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"price": 400,
|
||||
"orig_price": 400
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from flask import Flask, request, jsonify, make_response, send_from_directory
|
||||
from flask import Flask, json, request, jsonify, make_response, send_from_directory
|
||||
from logging.config import dictConfig
|
||||
from setting import Config
|
||||
import base64
|
||||
@@ -16,8 +16,11 @@ import server.arcdownload
|
||||
import server.arcpurchase
|
||||
import server.init
|
||||
import server.character
|
||||
import server.arclinkplay
|
||||
from udpserver.udp_main import link_play
|
||||
import os
|
||||
import sys
|
||||
from multiprocessing import Process, Pipe
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
@@ -31,62 +34,7 @@ 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!')
|
||||
conn1, conn2 = Pipe()
|
||||
|
||||
|
||||
def add_url_prefix(url, strange_flag=False):
|
||||
@@ -119,7 +67,6 @@ def error_return(error_code, extra={}): # 错误返回
|
||||
# -4 您的账号已在别处登录
|
||||
# -3 无法连接至服务器
|
||||
# 2 Arcaea服务器正在维护
|
||||
# 5 请更新Arcaea到最新版本
|
||||
# 9 新版本请等待几分钟
|
||||
# 100 无法在此ip地址下登录游戏
|
||||
# 101 用户名占用
|
||||
@@ -127,12 +74,12 @@ def error_return(error_code, extra={}): # 错误返回
|
||||
# 103 已有一个账号由此设备创建
|
||||
# 104 用户名密码错误
|
||||
# 105 24小时内登入两台设备
|
||||
# 106 账户冻结
|
||||
# 106 121 账户冻结
|
||||
# 107 你没有足够的体力
|
||||
# 113 活动已结束
|
||||
# 114 该活动已结束,您的成绩不会提交
|
||||
# 115 请输入有效的电子邮箱地址
|
||||
# 120 封号警告
|
||||
# 121 账户冻结
|
||||
# 122 账户暂时冻结
|
||||
# 123 账户被限制
|
||||
# 124 你今天不能再使用这个IP地址创建新的账号
|
||||
@@ -151,8 +98,13 @@ def error_return(error_code, extra={}): # 错误返回
|
||||
# 905 请在再次使用此功能前等待24小时
|
||||
# 1001 设备数量达到上限
|
||||
# 1002 此设备已使用过此功能
|
||||
# 1201 房间已满
|
||||
# 1202 房间号码无效
|
||||
# 1203 请将Arcaea更新至最新版本
|
||||
# 1205 此房间目前无法加入
|
||||
# 9801 下载歌曲时发生问题,请再试一次
|
||||
# 9802 保存歌曲时发生问题,请检查设备空间容量
|
||||
# 9803 下载已取消
|
||||
# 9905 没有在云端发现任何数据
|
||||
# 9907 更新数据时发生了问题
|
||||
# 9908 服务器只支持最新的版本,请更新Arcaea
|
||||
@@ -190,7 +142,7 @@ def login():
|
||||
if 'AppVersion' in request.headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return error_return(5)
|
||||
return error_return(1203)
|
||||
|
||||
headers = request.headers
|
||||
id_pwd = headers['Authorization']
|
||||
@@ -214,7 +166,7 @@ def login():
|
||||
return error_return(error_code)
|
||||
|
||||
|
||||
@app.route(add_url_prefix('/user/'), methods=['POST']) # 注册接口
|
||||
@app.route(add_url_prefix('/user'), methods=['POST']) # 注册接口
|
||||
def register():
|
||||
if 'AppVersion' in request.headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
@@ -632,7 +584,7 @@ def world_all(user_id):
|
||||
})
|
||||
|
||||
|
||||
@app.route(add_url_prefix('/world/map/me/'), methods=['POST']) # 进入地图
|
||||
@app.route(add_url_prefix('/world/map/me'), methods=['POST']) # 进入地图
|
||||
@server.auth.auth_required(request)
|
||||
def world_in(user_id):
|
||||
map_id = request.form['map_id']
|
||||
@@ -694,6 +646,76 @@ def download(file_path):
|
||||
return error_return(108)
|
||||
|
||||
|
||||
# 创建房间
|
||||
@app.route(add_url_prefix('/multiplayer/me/room/create'), methods=['POST'])
|
||||
@server.auth.auth_required(request)
|
||||
def room_create(user_id):
|
||||
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
||||
return error_return(151), 404
|
||||
|
||||
client_song_map = request.json['clientSongMap']
|
||||
error_code, value = server.arclinkplay.create_room(
|
||||
conn1, user_id, client_song_map)
|
||||
|
||||
if error_code == 0:
|
||||
value['endPoint'] = request.host.split(':')[0]
|
||||
value['port'] = int(Config.UDP_PORT)
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"value": value
|
||||
})
|
||||
else:
|
||||
return error_return(error_code), 400
|
||||
|
||||
|
||||
# 加入房间
|
||||
@app.route(add_url_prefix('/multiplayer/me/room/join/<room_code>'), methods=['POST'])
|
||||
@server.auth.auth_required(request)
|
||||
def room_join(user_id, room_code):
|
||||
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
||||
return error_return(151), 404
|
||||
|
||||
client_song_map = request.json['clientSongMap']
|
||||
error_code, value = server.arclinkplay.join_room(
|
||||
conn1, user_id, client_song_map, room_code)
|
||||
|
||||
if error_code == 0:
|
||||
value['endPoint'] = request.host.split(':')[0]
|
||||
value['port'] = int(Config.UDP_PORT)
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"value": value
|
||||
})
|
||||
else:
|
||||
return error_return(error_code), 400
|
||||
|
||||
|
||||
@app.route(add_url_prefix('/multiplayer/me/update'), methods=['POST']) # 更新房间
|
||||
@server.auth.auth_required(request)
|
||||
def multiplayer_update(user_id):
|
||||
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
||||
return error_return(151), 404
|
||||
|
||||
token = request.json['token']
|
||||
error_code, value = server.arclinkplay.update_room(conn1, user_id, token)
|
||||
|
||||
if error_code == 0:
|
||||
value['endPoint'] = request.host.split(':')[0]
|
||||
value['port'] = int(Config.UDP_PORT)
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"value": value
|
||||
})
|
||||
else:
|
||||
return error_return(error_code), 400
|
||||
|
||||
|
||||
@app.route(add_url_prefix('/user/me/request_delete'), methods=['POST']) # 删除账号
|
||||
@server.auth.auth_required(request)
|
||||
def user_delete(user_id):
|
||||
return error_return(151), 404
|
||||
|
||||
|
||||
# 三个设置,写在最后降低优先级
|
||||
@app.route(add_url_prefix('/<path:path>', True), methods=['POST'])
|
||||
@server.auth.auth_required(request)
|
||||
@@ -707,11 +729,80 @@ def sys_set(user_id, path):
|
||||
|
||||
|
||||
def main():
|
||||
if Config.SSL_CERT and Config.SSL_KEY:
|
||||
app.run(Config.HOST, Config.PORT, ssl_context=(
|
||||
Config.SSL_CERT, Config.SSL_KEY))
|
||||
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.run(Config.HOST, Config.PORT)
|
||||
app.logger.info('Complete!')
|
||||
|
||||
if Config.UDP_PORT and Config.UDP_PORT != '':
|
||||
process = [Process(target=link_play, args=(
|
||||
conn2, Config.HOST, int(Config.UDP_PORT)))]
|
||||
[p.start() for p in process]
|
||||
app.logger.info("UDP server is running...")
|
||||
if Config.SSL_CERT and Config.SSL_KEY:
|
||||
app.run(Config.HOST, Config.PORT, ssl_context=(
|
||||
Config.SSL_CERT, Config.SSL_KEY))
|
||||
else:
|
||||
app.run(Config.HOST, Config.PORT)
|
||||
[p.join() for p in process]
|
||||
else:
|
||||
if Config.SSL_CERT and Config.SSL_KEY:
|
||||
app.run(Config.HOST, Config.PORT, ssl_context=(
|
||||
Config.SSL_CERT, Config.SSL_KEY))
|
||||
else:
|
||||
app.run(Config.HOST, Config.PORT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
2
latest version/requirements.txt
Normal file
2
latest version/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
flask
|
||||
cryptography
|
||||
0
latest version/server/__init__.py
Normal file
0
latest version/server/__init__.py
Normal file
113
latest version/server/arclinkplay.py
Normal file
113
latest version/server/arclinkplay.py
Normal file
@@ -0,0 +1,113 @@
|
||||
from .sql import Connect
|
||||
import base64
|
||||
|
||||
|
||||
def get_song_unlock(client_song_map):
|
||||
# 处理可用歌曲bit,返回bytes
|
||||
|
||||
user_song_unlock = [0] * 512
|
||||
for i in range(0, 1024, 2):
|
||||
x = 0
|
||||
y = 0
|
||||
if str(i) in client_song_map:
|
||||
if client_song_map[str(i)][0]:
|
||||
x += 1
|
||||
if client_song_map[str(i)][1]:
|
||||
x += 2
|
||||
if client_song_map[str(i)][2]:
|
||||
x += 4
|
||||
if client_song_map[str(i)][3]:
|
||||
x += 8
|
||||
if str(i+1) in client_song_map:
|
||||
if client_song_map[str(i+1)][0]:
|
||||
y += 1
|
||||
if client_song_map[str(i+1)][1]:
|
||||
y += 2
|
||||
if client_song_map[str(i+1)][2]:
|
||||
y += 4
|
||||
if client_song_map[str(i+1)][3]:
|
||||
y += 8
|
||||
|
||||
user_song_unlock[i // 2] = y*16 + x
|
||||
|
||||
return bytes(user_song_unlock)
|
||||
|
||||
|
||||
def create_room(conn, user_id, client_song_map):
|
||||
# 创建房间,返回错误码和房间与用户信息
|
||||
error_code = 108
|
||||
|
||||
with Connect() as c:
|
||||
c.execute('''select name from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
name = x[0]
|
||||
|
||||
song_unlock = get_song_unlock(client_song_map)
|
||||
|
||||
conn.send((1, name, song_unlock))
|
||||
data = conn.recv()
|
||||
if data[0] == 0:
|
||||
error_code = 0
|
||||
return error_code, {'roomCode': data[1],
|
||||
'roomId': str(data[2]),
|
||||
'token': str(data[3]),
|
||||
'key': (base64.b64encode(data[4])).decode(),
|
||||
'playerId': str(data[5]),
|
||||
'userId': user_id,
|
||||
'orderedAllowedSongs': (base64.b64encode(song_unlock)).decode()
|
||||
}
|
||||
|
||||
return error_code, None
|
||||
|
||||
|
||||
def join_room(conn, user_id, client_song_map, room_code):
|
||||
# 加入房间,返回错误码和房间与用户信息
|
||||
error_code = 108
|
||||
|
||||
with Connect() as c:
|
||||
c.execute('''select name from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
name = x[0]
|
||||
|
||||
song_unlock = get_song_unlock(client_song_map)
|
||||
|
||||
conn.send((2, name, song_unlock, room_code))
|
||||
data = conn.recv()
|
||||
if data[0] == 0:
|
||||
error_code = 0
|
||||
return error_code, {'roomCode': data[1],
|
||||
'roomId': str(data[2]),
|
||||
'token': str(data[3]),
|
||||
'key': (base64.b64encode(data[4])).decode(),
|
||||
'playerId': str(data[5]),
|
||||
'userId': user_id,
|
||||
'orderedAllowedSongs': (base64.b64encode(data[6])).decode()
|
||||
}
|
||||
else:
|
||||
error_code = data[0]
|
||||
|
||||
return error_code, None
|
||||
|
||||
|
||||
def update_room(conn, user_id, token):
|
||||
# 更新房间,返回错误码和房间与用户信息
|
||||
error_code = 108
|
||||
|
||||
conn.send((3, int(token)))
|
||||
data = conn.recv()
|
||||
if data[0] == 0:
|
||||
error_code = 0
|
||||
return error_code, {'roomCode': data[1],
|
||||
'roomId': str(data[2]),
|
||||
'token': token,
|
||||
'key': (base64.b64encode(data[3])).decode(),
|
||||
'playerId': str(data[4]),
|
||||
'userId': user_id,
|
||||
'orderedAllowedSongs': (base64.b64encode(data[5])).decode()
|
||||
}
|
||||
else:
|
||||
error_code = data[0]
|
||||
|
||||
return error_code, None
|
||||
@@ -25,15 +25,35 @@ def get_purchase(c, type='pack'):
|
||||
for i in x:
|
||||
items = []
|
||||
c.execute(
|
||||
'''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]})
|
||||
'''select a.*, b.amount from item a, purchase_item b where a.item_id=b.item_id and a.type=b.type and b.purchase_name=:name''', {'name': i[0]})
|
||||
y = c.fetchall()
|
||||
t = None
|
||||
if y:
|
||||
for j in y:
|
||||
items.append({
|
||||
"type": j[1],
|
||||
"id": j[0],
|
||||
"is_available": int2b(j[2])
|
||||
})
|
||||
if j[3]:
|
||||
amount = j[3]
|
||||
else:
|
||||
amount = 1
|
||||
if i[0] == j[0]:
|
||||
# 物品排序,否则客户端报错
|
||||
t = {
|
||||
"type": j[1],
|
||||
"id": j[0],
|
||||
"is_available": int2b(j[2]),
|
||||
'amount': amount
|
||||
}
|
||||
else:
|
||||
items.append({
|
||||
"type": j[1],
|
||||
"id": j[0],
|
||||
"is_available": int2b(j[2]),
|
||||
"amount": amount
|
||||
})
|
||||
|
||||
|
||||
if t is not None:
|
||||
# 放到列表头
|
||||
items = [t, items]
|
||||
|
||||
r = {"name": i[0],
|
||||
"items": items,
|
||||
@@ -104,7 +124,7 @@ def buy_thing(user_id, purchase_id):
|
||||
}
|
||||
|
||||
c.execute(
|
||||
'''select item_id, type from purchase_item where purchase_name=:a''', {'a': purchase_id})
|
||||
'''select item_id, type, amount from purchase_item where purchase_name=:a''', {'a': purchase_id})
|
||||
x = c.fetchall()
|
||||
if x:
|
||||
now = int(time.time() * 1000)
|
||||
@@ -115,7 +135,11 @@ def buy_thing(user_id, purchase_id):
|
||||
|
||||
if flag:
|
||||
for i in x:
|
||||
server.item.claim_user_item(c, user_id, i[0], i[1])
|
||||
if i[2]:
|
||||
amount = i[2]
|
||||
else:
|
||||
amount = 1
|
||||
server.item.claim_user_item(c, user_id, i[0], i[1], amount)
|
||||
|
||||
success_flag = True
|
||||
else:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,12 @@ import server.info
|
||||
import server.arcpurchase
|
||||
import os
|
||||
import time
|
||||
import random
|
||||
|
||||
|
||||
ETO_UNCAP_BONUS_PROGRESS = 7
|
||||
LUNA_UNCAP_BONUS_PROGRESS = 7
|
||||
AYU_UNCAP_BONUS_PROGRESS = 5
|
||||
|
||||
|
||||
def int2b(x):
|
||||
@@ -127,9 +133,12 @@ def get_available_maps():
|
||||
for i in Config.AVAILABLE_MAP:
|
||||
info = get_world_info(i)
|
||||
del info['steps']
|
||||
del info['is_locked']
|
||||
del info['curr_position']
|
||||
del info['curr_capture']
|
||||
try:
|
||||
del info['is_locked']
|
||||
del info['curr_position']
|
||||
del info['curr_capture']
|
||||
except:
|
||||
pass
|
||||
re.append(info)
|
||||
|
||||
return re
|
||||
@@ -360,12 +369,23 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
||||
'a': user_id, 'b': song_id, 'c': difficulty})
|
||||
|
||||
c.execute(
|
||||
'''select character_id, max_stamina_ts, stamina from user where user_id=?''', (user_id,))
|
||||
'''select character_id, max_stamina_ts, stamina, is_skill_sealed, is_char_uncapped, is_char_uncapped_override from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
character_id = x[0] if x and x[0] is not None else 0
|
||||
max_stamina_ts = x[1] if x and x[1] is not None else 0
|
||||
stamina = x[2] if x and x[2] is not None else 12
|
||||
c.execute('''select frag1,prog1,overdrive1,frag20,prog20,overdrive20,frag30,prog30,overdrive30 from character where character_id=?''', (character_id,))
|
||||
is_skill_sealed = x[3] if x and x[3] is not None else 1
|
||||
skill = False
|
||||
skill_uncap = False
|
||||
if not is_skill_sealed:
|
||||
if x:
|
||||
skill = True
|
||||
if x[4] is not None and x[4] == 1:
|
||||
skill_uncap = True
|
||||
if x[5] is not None and x[5] == 1:
|
||||
skill_uncap = False
|
||||
|
||||
c.execute('''select frag1,prog1,overdrive1,frag20,prog20,overdrive20,frag30,prog30,overdrive30,skill_id,skill_id_uncap from character where character_id=?''', (character_id,))
|
||||
x = c.fetchone()
|
||||
|
||||
if Config.CHARACTER_FULL_UNLOCK:
|
||||
@@ -386,10 +406,20 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
||||
flag = server.character.calc_char_value(level, x[0], x[3], x[6])
|
||||
prog = server.character.calc_char_value(level, x[1], x[4], x[7])
|
||||
overdrive = server.character.calc_char_value(level, x[2], x[5], x[8])
|
||||
if x[9] is not None and x[9] != '' and skill:
|
||||
skill = x[9]
|
||||
else:
|
||||
skill = None
|
||||
if x[10] is not None and x[9] != '' and skill_uncap:
|
||||
skill_uncap = x[10]
|
||||
else:
|
||||
skill_uncap = None
|
||||
else:
|
||||
flag = 0
|
||||
prog = 0
|
||||
overdrive = 0
|
||||
skill = None
|
||||
skill_uncap = None
|
||||
|
||||
c.execute('''select current_map from user where user_id = :a''', {
|
||||
'a': user_id})
|
||||
@@ -421,6 +451,52 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
||||
|
||||
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||
user_id, map_id, step, y[3], y[2])
|
||||
# Eto和Luna的技能
|
||||
character_bonus_progress = None
|
||||
skill_special = ''
|
||||
if skill_uncap is not None and skill_uncap and skill_uncap in ['eto_uncap', 'luna_uncap', 'ayu_uncap']:
|
||||
skill_special = skill_uncap
|
||||
elif skill is not None and skill and skill in ['eto_uncap', 'luna_uncap', 'ayu_uncap']:
|
||||
skill_special = skill
|
||||
if skill_special == 'eto_uncap':
|
||||
# eto觉醒技能,获得残片奖励时世界模式进度加7
|
||||
fragment_flag = False
|
||||
for i in rewards:
|
||||
for j in i['items']:
|
||||
if j['type'] == 'fragment':
|
||||
fragment_flag = True
|
||||
break
|
||||
if fragment_flag:
|
||||
break
|
||||
if fragment_flag:
|
||||
character_bonus_progress = ETO_UNCAP_BONUS_PROGRESS
|
||||
step += character_bonus_progress * step_times
|
||||
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
|
||||
|
||||
elif skill_special == 'luna_uncap':
|
||||
# luna觉醒技能,限制格开始时世界模式进度加7
|
||||
if 'restrict_id' in steps[0] and 'restrict_type' in steps[0] and steps[0]['restrict_type'] != '' and steps[0]['restrict_id'] != '':
|
||||
character_bonus_progress = LUNA_UNCAP_BONUS_PROGRESS
|
||||
step += character_bonus_progress * step_times
|
||||
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
|
||||
|
||||
elif skill_special == 'ayu_uncap':
|
||||
# ayu觉醒技能,世界模式进度+5或-5,但不会小于0
|
||||
if random.random() >= 0.5:
|
||||
character_bonus_progress = AYU_UNCAP_BONUS_PROGRESS
|
||||
else:
|
||||
character_bonus_progress = -AYU_UNCAP_BONUS_PROGRESS
|
||||
|
||||
step += character_bonus_progress * step_times
|
||||
if step < 0:
|
||||
character_bonus_progress += step / step_times
|
||||
step = 0
|
||||
|
||||
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
|
||||
@@ -496,6 +572,9 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
||||
"max_stamina_ts": max_stamina_ts
|
||||
}
|
||||
|
||||
if character_bonus_progress is not None:
|
||||
re['character_bonus_progress'] = character_bonus_progress
|
||||
|
||||
if stamina_multiply != 1:
|
||||
re['stamina_multiply'] = stamina_multiply
|
||||
if fragment_multiply != 100:
|
||||
|
||||
@@ -195,7 +195,7 @@ def auth_required(request):
|
||||
if 'AppVersion' in headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return jsonify({"success": False, "error_code": 5})
|
||||
return jsonify({"success": False, "error_code": 1203})
|
||||
|
||||
if 'Authorization' in headers:
|
||||
token = headers['Authorization']
|
||||
|
||||
@@ -30,10 +30,11 @@ class Connect():
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class Sql():
|
||||
|
||||
@staticmethod
|
||||
def select(c, table_name, target_column=[], limit=-1, offset=0, query={}, sort=[]):
|
||||
def select(c, table_name, target_column=[], query=None):
|
||||
# 执行查询单句sql语句,返回fetchall数据
|
||||
# 使用准确查询,且在单表内
|
||||
|
||||
@@ -51,9 +52,10 @@ class Sql():
|
||||
|
||||
where_field = []
|
||||
where_value = []
|
||||
for i in query:
|
||||
where_field.append(i)
|
||||
where_value.append(query[i])
|
||||
if query:
|
||||
for i in query.query:
|
||||
where_field.append(i)
|
||||
where_value.append(query.query[i])
|
||||
|
||||
if where_field and where_value:
|
||||
sql += ' where '
|
||||
@@ -64,16 +66,17 @@ class Sql():
|
||||
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 query and query.sort:
|
||||
sql += ' order by ' + \
|
||||
query.sort[0]['column'] + ' ' + query.sort[0]['order']
|
||||
for i in range(1, len(query.sort)):
|
||||
sql += ', ' + query.sort[i]['column'] + \
|
||||
' ' + query.sort[i]['order']
|
||||
|
||||
if limit >= 0:
|
||||
if query and query.limit >= 0:
|
||||
sql += ' limit :limit offset :offset'
|
||||
sql_dict['limit'] = limit
|
||||
sql_dict['offset'] = offset
|
||||
sql_dict['limit'] = query.limit
|
||||
sql_dict['offset'] = query.offset
|
||||
|
||||
c.execute(sql, sql_dict)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class Config():
|
||||
主机的地址和端口号
|
||||
Host and port of your server
|
||||
'''
|
||||
HOST = '192.168.1.101'
|
||||
HOST = '0.0.0.0'
|
||||
PORT = '80'
|
||||
'''
|
||||
--------------------
|
||||
@@ -19,7 +19,7 @@ class Config():
|
||||
游戏API地址前缀
|
||||
Game API's URL prefix
|
||||
'''
|
||||
GAME_API_PREFIX = '/kusoatui/15'
|
||||
GAME_API_PREFIX = '/merikuri/17'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
@@ -30,7 +30,18 @@ class Config():
|
||||
Allowed game versions
|
||||
If it is blank, all are allowed.
|
||||
'''
|
||||
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.7.0', '3.7.0c']
|
||||
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.11.2', '3.11.2c']
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
联机功能的端口号,若为空,则默认不开启联机功能
|
||||
Port of your link play server
|
||||
If it is blank, link play will be unavailable.
|
||||
'''
|
||||
UDP_PORT = '10900'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
@@ -106,7 +117,7 @@ class Config():
|
||||
API interface full control permission Token
|
||||
If you don't want to use it, leave it blank.
|
||||
'''
|
||||
API_TOKEN = '123'
|
||||
API_TOKEN = ''
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
@@ -179,6 +190,18 @@ class Config():
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
数据库更新时,是否采用最新的角色数据,如果你想采用最新的官方角色数据
|
||||
注意:如果是,旧的数据将丢失;如果否,某些角色的数据变动将无法同步
|
||||
If using the latest character data when updating database. If you want to only keep newest official character data, please set it `True`.
|
||||
Note: If `True`, the old data will be lost; If `False`, the data changes of some characters will not be synchronized.
|
||||
'''
|
||||
UPDATE_WITH_NEW_CHARACTER_DATA = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否全解锁搭档
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
<span class="char-num">{{x['type']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Amount: </span>
|
||||
<span class="char-num">{{x['amount']}}</span>
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
{% if not loop.last %}
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
<option value='pack'>Song pack</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='character'>Character</option>
|
||||
<option value='core'>Core</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="amount">Amount</label>
|
||||
<input name="amount" id="amount" value="1" required>
|
||||
<br />
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
@@ -36,6 +39,7 @@
|
||||
<option value='pack'>Pack</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='character'>Character</option>
|
||||
<option value='core'>Core</option>
|
||||
</select>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
0
latest version/udpserver/__init__.py
Normal file
0
latest version/udpserver/__init__.py
Normal file
24
latest version/udpserver/aes.py
Normal file
24
latest version/udpserver/aes.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import os
|
||||
from cryptography.hazmat.primitives.ciphers import (
|
||||
Cipher, algorithms, modes
|
||||
)
|
||||
|
||||
|
||||
def encrypt(key, plaintext, associated_data):
|
||||
iv = os.urandom(12)
|
||||
encryptor = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.GCM(iv, min_tag_length=12),
|
||||
).encryptor()
|
||||
encryptor.authenticate_additional_data(associated_data)
|
||||
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
|
||||
return (iv, ciphertext, encryptor.tag)
|
||||
|
||||
|
||||
def decrypt(key, associated_data, iv, ciphertext, tag):
|
||||
decryptor = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.GCM(iv, tag, min_tag_length=12),
|
||||
).decryptor()
|
||||
decryptor.authenticate_additional_data(associated_data)
|
||||
return decryptor.update(ciphertext) + decryptor.finalize()
|
||||
174
latest version/udpserver/udp_class.py
Normal file
174
latest version/udpserver/udp_class.py
Normal file
@@ -0,0 +1,174 @@
|
||||
def b(value, length=1):
|
||||
return value.to_bytes(length=length, byteorder='little')
|
||||
|
||||
|
||||
def bi(value):
|
||||
return int.from_bytes(value, byteorder='little')
|
||||
|
||||
|
||||
class Player:
|
||||
def __init__(self) -> None:
|
||||
self.player_id = 0
|
||||
self.player_name = b'\x45\x6d\x70\x74\x79\x50\x6c\x61\x79\x65\x72\x00\x00\x00\x00\x00'
|
||||
self.token = 0
|
||||
|
||||
self.character_id = 0xff
|
||||
self.last_character_id = 0xff
|
||||
self.is_uncapped = 0
|
||||
|
||||
self.difficulty = 0xff
|
||||
self.last_difficulty = 0xff
|
||||
self.score = 0
|
||||
self.last_score = 0
|
||||
self.timer = 0
|
||||
self.last_timer = 0
|
||||
self.cleartype = 0
|
||||
self.last_cleartype = 0
|
||||
self.best_score_flag = 0
|
||||
self.best_player_flag = 0
|
||||
self.finish_flag = 0
|
||||
|
||||
self.player_state = 1
|
||||
self.download_percent = 0
|
||||
self.online = 0
|
||||
|
||||
self.last_timestamp = 0
|
||||
self.extra_command_queue = []
|
||||
|
||||
self.song_unlock = b'\x00' * 512
|
||||
|
||||
self.start_command_num = 0
|
||||
|
||||
def set_player_name(self, player_name: str):
|
||||
self.player_name = player_name.encode('ascii')
|
||||
if len(self.player_name) > 16:
|
||||
self.player_name = self.player_name[:16]
|
||||
else:
|
||||
self.player_name += b'\x00' * (16 - len(self.player_name))
|
||||
|
||||
|
||||
class Room:
|
||||
def __init__(self) -> None:
|
||||
self.room_id = 0
|
||||
self.room_code = 'AAAA00'
|
||||
|
||||
self.countdown = 0xffffffff
|
||||
self.timestamp = 0
|
||||
self.state = 0
|
||||
self.song_idx = 0xffff
|
||||
self.last_song_idx = 0xffff
|
||||
|
||||
self.song_unlock = b'\x00' * 512
|
||||
|
||||
self.host_id = 0
|
||||
self.players = [Player(), Player(), Player(), Player()]
|
||||
self.player_num = 0
|
||||
|
||||
self.interval = 1000
|
||||
self.times = 100
|
||||
|
||||
self.round_switch = 0
|
||||
|
||||
self.command_queue = []
|
||||
self.command_queue_length = 0
|
||||
|
||||
def get_players_info(self):
|
||||
# 获取所有玩家信息
|
||||
re = b''
|
||||
for i in self.players:
|
||||
re += b(i.player_id, 8) + b(i.character_id) + b(i.is_uncapped) + b(i.difficulty) + b(i.score, 4) + \
|
||||
b(i.timer, 4) + b(i.cleartype) + b(i.player_state) + \
|
||||
b(i.download_percent) + b(i.online) + b'\x00' + i.player_name
|
||||
return re
|
||||
|
||||
def get_player_last_score(self):
|
||||
# 获取上次曲目玩家分数,返回bytes
|
||||
if self.last_song_idx == 0xffff:
|
||||
return b'\xff\xff\x00\x00\x00\x00\x00\x00\x00' * 4
|
||||
re = b''
|
||||
|
||||
for i in range(4):
|
||||
player = self.players[i]
|
||||
|
||||
if player.player_id != 0:
|
||||
re += b(player.last_character_id) + b(player.last_difficulty) + b(player.last_score, 4) + b(
|
||||
player.last_cleartype) + b(player.best_score_flag) + b(player.best_player_flag)
|
||||
else:
|
||||
re += b'\xff\xff\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
return re
|
||||
|
||||
def make_round(self):
|
||||
# 轮换房主
|
||||
for i in range(4):
|
||||
if self.players[i].player_id == self.host_id:
|
||||
for j in range(1, 4):
|
||||
if self.players[(i + j) % 4].player_id != 0:
|
||||
self.host_id = self.players[(i + j) % 4].player_id
|
||||
break
|
||||
break
|
||||
|
||||
def delete_player(self, player_index: int):
|
||||
# 删除某个玩家
|
||||
self.player_num -= 1
|
||||
if self.players[player_index].player_id == self.host_id:
|
||||
self.make_round()
|
||||
|
||||
self.players[player_index].online = 0
|
||||
self.players[player_index] = Player()
|
||||
self.update_song_unlock()
|
||||
|
||||
def update_song_unlock(self):
|
||||
# 更新房间可用歌曲
|
||||
r = bi(b'\xff' * 512)
|
||||
for i in self.players:
|
||||
if i.player_id != 0:
|
||||
r &= bi(i.song_unlock)
|
||||
|
||||
self.song_unlock = b(r, 512)
|
||||
|
||||
def is_ready(self, old_state: int, player_state: int):
|
||||
# 是否全部准备就绪
|
||||
if self.state == old_state:
|
||||
for i in self.players:
|
||||
if i.player_id != 0 and (i.player_state != player_state or i.online == 0):
|
||||
return False
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_finish(self):
|
||||
# 是否全部进入结算
|
||||
for i in self.players:
|
||||
if i.player_id != 0 and (i.finish_flag == 0 or i.online == 0):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def make_finish(self):
|
||||
# 结算
|
||||
self.state = 8
|
||||
self.last_song_idx = self.song_idx
|
||||
|
||||
max_score = 0
|
||||
max_score_i = []
|
||||
for i in range(4):
|
||||
player = self.players[i]
|
||||
if player.player_id != 0:
|
||||
player.finish_flag = 0
|
||||
player.last_timer = player.timer
|
||||
player.last_score = player.score
|
||||
player.last_cleartype = player.cleartype
|
||||
player.last_character_id = player.character_id
|
||||
player.last_difficulty = player.difficulty
|
||||
player.best_player_flag = 0
|
||||
|
||||
if player.last_score > max_score:
|
||||
max_score = player.last_score
|
||||
max_score_i = [i]
|
||||
elif player.last_score == max_score:
|
||||
max_score_i.append(i)
|
||||
|
||||
for i in max_score_i:
|
||||
self.players[i].best_player_flag = 1
|
||||
8
latest version/udpserver/udp_config.py
Normal file
8
latest version/udpserver/udp_config.py
Normal file
@@ -0,0 +1,8 @@
|
||||
class Config:
|
||||
TIME_LIMIT = 3600000
|
||||
|
||||
COMMAND_INTERVAL = 1000000
|
||||
|
||||
COUNTDOWM_TIME = 3999
|
||||
|
||||
PLAYER_TIMEOUT = 30000000
|
||||
223
latest version/udpserver/udp_main.py
Normal file
223
latest version/udpserver/udp_main.py
Normal file
@@ -0,0 +1,223 @@
|
||||
import socketserver
|
||||
import threading
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
# import binascii
|
||||
from . import aes
|
||||
from .udp_parser import CommandParser
|
||||
from .udp_class import Room, Player, bi
|
||||
from .udp_config import Config
|
||||
|
||||
|
||||
# token: {'key': key, 'room': Room, 'player_index': player_index, 'player_id': player_id}
|
||||
link_play_data = {}
|
||||
room_id_dict = {} # 'room_id': Room
|
||||
room_code_dict = {} # 'room_code': Room
|
||||
player_dict = {} # 'player_id' : Player
|
||||
clean_timer = 0
|
||||
|
||||
|
||||
def random_room_code():
|
||||
# 随机生成房间号
|
||||
re = ''
|
||||
for _ in range(4):
|
||||
re += chr(random.randint(65, 90))
|
||||
for _ in range(2):
|
||||
re += str(random.randint(0, 9))
|
||||
|
||||
return re
|
||||
|
||||
|
||||
def clear_player(token):
|
||||
# 清除玩家信息和token
|
||||
del player_dict[link_play_data[token]['player_id']]
|
||||
del link_play_data[token]
|
||||
|
||||
|
||||
def clear_room(room):
|
||||
# 清除房间信息
|
||||
room_id = room.room_id
|
||||
room_code = room.room_code
|
||||
del room_id_dict[room_id]
|
||||
del room_code_dict[room_code]
|
||||
del room
|
||||
|
||||
|
||||
def memory_clean(now):
|
||||
# 内存清理
|
||||
clean_room_list = []
|
||||
clean_player_list = []
|
||||
for token in link_play_data:
|
||||
room = link_play_data[token]['room']
|
||||
if now - room.timestamp >= Config.TIME_LIMIT:
|
||||
clean_room_list.append(room.room_id)
|
||||
|
||||
if now - room.players[link_play_data[token]['player_index']].last_timestamp // 1000 >= Config.TIME_LIMIT:
|
||||
clean_player_list.append(token)
|
||||
|
||||
for room_id in room_id_dict:
|
||||
if now - room_id_dict[room_id].timestamp >= Config.TIME_LIMIT:
|
||||
clean_room_list.append(room_id)
|
||||
|
||||
for room_id in clean_room_list:
|
||||
if room_id in room_id_dict:
|
||||
clear_room(room_id_dict[room_id])
|
||||
|
||||
for token in clean_player_list:
|
||||
clear_player(token)
|
||||
|
||||
|
||||
class UDPhandler(socketserver.BaseRequestHandler):
|
||||
def handle(self):
|
||||
client_msg, server = self.request
|
||||
token = client_msg[:8]
|
||||
iv = client_msg[8:20]
|
||||
tag = client_msg[20:32]
|
||||
ciphertext = client_msg[32:]
|
||||
if int.from_bytes(token, byteorder='little') in link_play_data:
|
||||
user = link_play_data[bi(token)]
|
||||
else:
|
||||
return None
|
||||
|
||||
plaintext = aes.decrypt(user['key'], b'', iv, ciphertext, tag)
|
||||
# print(binascii.b2a_hex(plaintext))
|
||||
|
||||
commands = CommandParser(
|
||||
user['room'], user['player_index']).get_commands(plaintext)
|
||||
|
||||
if user['room'].players[user['player_index']].player_id == 0:
|
||||
clear_player(bi(token))
|
||||
temp = []
|
||||
for i in commands:
|
||||
if i[:3] == b'\x06\x16\x12':
|
||||
temp.append(i)
|
||||
commands = temp
|
||||
# 处理不能正确被踢的问题
|
||||
|
||||
for i in commands:
|
||||
iv, ciphertext, tag = aes.encrypt(user['key'], i, b'')
|
||||
# print(binascii.b2a_hex(i))
|
||||
|
||||
server.sendto(token + iv + tag[:12] +
|
||||
ciphertext, self.client_address)
|
||||
|
||||
|
||||
def server_run(ip, port):
|
||||
server = socketserver.ThreadingUDPServer((ip, port), UDPhandler)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
def data_swap(conn):
|
||||
clean_timer = 0
|
||||
while True:
|
||||
try:
|
||||
data = conn.recv()
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
now = round(time.time() * 1000)
|
||||
if now - clean_timer >= Config.TIME_LIMIT:
|
||||
clean_timer = now
|
||||
memory_clean(now)
|
||||
|
||||
if data[0] == 1:
|
||||
# 开房
|
||||
key = os.urandom(16)
|
||||
room_id = bi(os.urandom(8))
|
||||
while room_id in room_id_dict and room_id == 0:
|
||||
room_id = bi(os.urandom(8))
|
||||
room = Room()
|
||||
room.room_id = room_id
|
||||
room_id_dict[room_id] = room
|
||||
|
||||
player_id = bi(os.urandom(3))
|
||||
while player_id in player_dict and player_id == 0:
|
||||
player_id = bi(os.urandom(3))
|
||||
player = Player()
|
||||
player.player_id = player_id
|
||||
player.set_player_name(data[1])
|
||||
player_dict[player_id] = player
|
||||
|
||||
player.song_unlock = data[2]
|
||||
room.song_unlock = data[2]
|
||||
room.host_id = player_id
|
||||
room.players[0] = player
|
||||
room.player_num = 1
|
||||
|
||||
room_code = random_room_code()
|
||||
while room_code in room_code_dict:
|
||||
room_code = random_room_code()
|
||||
room.room_code = room_code
|
||||
room_code_dict[room_code] = room
|
||||
|
||||
token = room_id
|
||||
player.token = token
|
||||
|
||||
link_play_data[token] = {'key': key,
|
||||
'room': room,
|
||||
'player_index': 0,
|
||||
'player_id': player_id}
|
||||
|
||||
conn.send((0, room_code, room_id, token, key, player_id))
|
||||
|
||||
elif data[0] == 2:
|
||||
room_code = data[3].upper()
|
||||
if room_code not in room_code_dict:
|
||||
# 房间号错误
|
||||
conn.send((1202, ))
|
||||
else:
|
||||
room = room_code_dict[room_code]
|
||||
if room.player_num == 4:
|
||||
# 满人
|
||||
conn.send((1201, ))
|
||||
elif room.state != 2:
|
||||
# 无法加入
|
||||
conn.send((1205, ))
|
||||
else:
|
||||
key = os.urandom(16)
|
||||
token = bi(os.urandom(8))
|
||||
|
||||
player_id = bi(os.urandom(3))
|
||||
while player_id in player_dict and player_id == 0:
|
||||
player_id = bi(os.urandom(3))
|
||||
player = Player()
|
||||
player.player_id = player_id
|
||||
player.set_player_name(data[1])
|
||||
player.token = token
|
||||
player_dict[player_id] = player
|
||||
|
||||
player.song_unlock = data[2]
|
||||
|
||||
room.update_song_unlock()
|
||||
for i in range(4):
|
||||
if room.players[i].player_id == 0:
|
||||
room.players[i] = player
|
||||
player_index = i
|
||||
break
|
||||
|
||||
link_play_data[token] = {'key': key,
|
||||
'room': room,
|
||||
'player_index': player_index,
|
||||
'player_id': player_id}
|
||||
|
||||
conn.send((0, room_code, room.room_id,
|
||||
token, key, player_id, room.song_unlock))
|
||||
elif data[0] == 3:
|
||||
token = data[1]
|
||||
if token in link_play_data:
|
||||
r = link_play_data[token]
|
||||
conn.send((0, r['room'].room_code, r['room'].room_id, r['key'],
|
||||
r['room'].players[r['player_index']].player_id, r['room'].song_unlock))
|
||||
else:
|
||||
conn.send((108, ))
|
||||
|
||||
|
||||
def link_play(conn, ip: str, port: int):
|
||||
try:
|
||||
server = threading.Thread(target=server_run, args=(ip, port))
|
||||
data_exchange = threading.Thread(target=data_swap, args=(conn,))
|
||||
server.start()
|
||||
data_exchange.start()
|
||||
except:
|
||||
pass
|
||||
333
latest version/udpserver/udp_parser.py
Normal file
333
latest version/udpserver/udp_parser.py
Normal file
@@ -0,0 +1,333 @@
|
||||
from operator import irshift
|
||||
from .udp_sender import CommandSender
|
||||
from .udp_class import bi, Room
|
||||
from .udp_config import Config
|
||||
import time
|
||||
|
||||
|
||||
class CommandParser:
|
||||
def __init__(self, room: Room, player_index: int = 0) -> None:
|
||||
self.room = room
|
||||
self.player_index = player_index
|
||||
|
||||
def get_commands(self, command):
|
||||
self.command = command
|
||||
l = {b'\x06\x16\x01': self.command_01,
|
||||
b'\x06\x16\x02': self.command_02,
|
||||
b'\x06\x16\x03': self.command_03,
|
||||
b'\x06\x16\x04': self.command_04,
|
||||
b'\x06\x16\x05': self.command_05,
|
||||
b'\x06\x16\x06': self.command_06,
|
||||
b'\x06\x16\x07': self.command_07,
|
||||
b'\x06\x16\x08': self.command_08,
|
||||
b'\x06\x16\x09': self.command_09,
|
||||
b'\x06\x16\x0a': self.command_0a,
|
||||
b'\x06\x16\x0b': self.command_0b
|
||||
}
|
||||
r = l[command[:3]]()
|
||||
|
||||
re = []
|
||||
|
||||
flag_13 = False
|
||||
for i in range(max(bi(self.command[12:16]), self.room.players[self.player_index].start_command_num), self.room.command_queue_length):
|
||||
if self.room.command_queue[i][:3] == b'\x06\x16\x13':
|
||||
if flag_13:
|
||||
break
|
||||
flag_13 = True
|
||||
re.append(self.room.command_queue[i])
|
||||
|
||||
if self.room.players[self.player_index].extra_command_queue:
|
||||
re += self.room.players[self.player_index].extra_command_queue
|
||||
self.room.players[self.player_index].extra_command_queue = []
|
||||
|
||||
if r:
|
||||
re += r
|
||||
|
||||
return re
|
||||
|
||||
def command_01(self):
|
||||
# 给房主
|
||||
player_id = bi(self.command[24:32])
|
||||
for i in self.room.players:
|
||||
if i.player_id == player_id and i.online == 1:
|
||||
self.room.host_id = player_id
|
||||
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_10())
|
||||
|
||||
return None
|
||||
|
||||
def command_02(self):
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
song_idx = bi(self.command[24:26])
|
||||
|
||||
flag = 2
|
||||
if self.room.state == 2:
|
||||
flag = 0
|
||||
self.room.state = 3
|
||||
self.room.song_idx = song_idx
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_11())
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return [x.command_0d(flag)]
|
||||
|
||||
def command_03(self):
|
||||
# 尝试进入结算
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
player = self.room.players[self.player_index]
|
||||
player.score = bi(self.command[24:28])
|
||||
player.cleartype = self.command[28]
|
||||
player.difficulty = self.command[29]
|
||||
player.best_score_flag = self.command[30]
|
||||
player.finish_flag = 1
|
||||
player.last_timestamp -= Config.COMMAND_INTERVAL
|
||||
self.room.last_song_idx = self.room.song_idx
|
||||
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(self.player_index))
|
||||
|
||||
if self.room.is_finish():
|
||||
self.room.make_finish()
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return None
|
||||
|
||||
def command_04(self):
|
||||
# 踢人
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
player_id = bi(self.command[24:32])
|
||||
flag = 2
|
||||
if self.room.players[self.player_index].player_id == self.room.host_id and player_id != self.room.host_id:
|
||||
for i in range(4):
|
||||
if self.room.players[i].player_id == player_id:
|
||||
flag = 1
|
||||
self.room.delete_player(i)
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(i))
|
||||
self.room.update_song_unlock()
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_14())
|
||||
break
|
||||
|
||||
return [x.command_0d(flag)]
|
||||
|
||||
def command_05(self):
|
||||
pass
|
||||
|
||||
def command_06(self):
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.state = 1
|
||||
self.room.song_idx = 0xffff
|
||||
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
return None
|
||||
|
||||
def command_07(self):
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.players[self.player_index].song_unlock = self.command[24:536]
|
||||
self.room.update_song_unlock()
|
||||
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_14())
|
||||
return None
|
||||
|
||||
def command_08(self):
|
||||
self.room.round_switch = bi(self.command[24:25])
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return None
|
||||
|
||||
def command_09(self):
|
||||
re = []
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
player = self.room.players[self.player_index]
|
||||
|
||||
if bi(self.command[12:16]) == 0:
|
||||
player.online = 1
|
||||
self.room.state = 1
|
||||
self.room.update_song_unlock()
|
||||
player.start_command_num = self.room.command_queue_length
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_15())
|
||||
else:
|
||||
if x.timestamp - player.last_timestamp >= Config.COMMAND_INTERVAL:
|
||||
re.append(x.command_0c())
|
||||
player.last_timestamp = x.timestamp
|
||||
|
||||
# 离线判断
|
||||
for i in range(4):
|
||||
if i != self.player_index:
|
||||
t = self.room.players[i]
|
||||
if t.player_id != 0:
|
||||
if t.last_timestamp != 0:
|
||||
if t.online == 1 and x.timestamp - t.last_timestamp >= 5000000:
|
||||
t.online = 0
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(i))
|
||||
elif t.online == 0 and x.timestamp - t.last_timestamp >= Config.PLAYER_TIMEOUT:
|
||||
self.room.delete_player(i)
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(i))
|
||||
|
||||
flag_11 = False
|
||||
flag_12 = False
|
||||
flag_13 = False
|
||||
|
||||
if player.online == 0:
|
||||
flag_12 = True
|
||||
player.online = 1
|
||||
|
||||
if self.room.is_ready(1, 1):
|
||||
flag_13 = True
|
||||
self.room.state = 2
|
||||
|
||||
if player.player_state != self.command[32]:
|
||||
flag_12 = True
|
||||
player.player_state = self.command[32]
|
||||
|
||||
if player.difficulty != self.command[33] and player.player_state != 5 and player.player_state != 6 and player.player_state != 7 and player.player_state != 8:
|
||||
flag_12 = True
|
||||
player.difficulty = self.command[33]
|
||||
|
||||
if player.cleartype != self.command[34] and player.player_state != 7 and player.player_state != 8:
|
||||
flag_12 = True
|
||||
player.cleartype = self.command[34]
|
||||
|
||||
if player.download_percent != self.command[35]:
|
||||
flag_12 = True
|
||||
player.download_percent = self.command[35]
|
||||
|
||||
if player.character_id != self.command[36]:
|
||||
flag_12 = True
|
||||
player.character_id = self.command[36]
|
||||
|
||||
if player.is_uncapped != self.command[37]:
|
||||
flag_12 = True
|
||||
player.is_uncapped = self.command[37]
|
||||
|
||||
if self.room.state == 3 and player.score != bi(self.command[24:28]):
|
||||
flag_12 = True
|
||||
player.score = bi(self.command[24:28])
|
||||
|
||||
if self.room.is_ready(3, 4):
|
||||
flag_13 = True
|
||||
self.room.countdown = Config.COUNTDOWM_TIME
|
||||
self.room.timestamp = round(time.time() * 1000)
|
||||
self.room.state = 4
|
||||
|
||||
if self.room.state == 4 or self.room.state == 5 or self.room.state == 6:
|
||||
timestamp = round(time.time() * 1000)
|
||||
self.room.countdown -= timestamp - self.room.timestamp
|
||||
self.room.timestamp = timestamp
|
||||
if self.room.state == 4 and self.room.countdown <= 0:
|
||||
# 此处不清楚
|
||||
self.room.state = 5
|
||||
self.room.countdown = 5999
|
||||
flag_11 = True
|
||||
flag_13 = True
|
||||
|
||||
if self.room.state == 5 and self.room.is_ready(5, 6):
|
||||
self.room.state = 6
|
||||
flag_13 = True
|
||||
|
||||
if self.room.state == 5 and self.room.is_ready(5, 7):
|
||||
self.room.state = 7
|
||||
self.room.countdown = 0xffffffff
|
||||
flag_13 = True
|
||||
|
||||
if self.room.state == 5 and self.room.countdown <= 0:
|
||||
print('我怎么知道这是啥')
|
||||
|
||||
if self.room.state == 6 and self.room.countdown <= 0:
|
||||
# 此处不清楚
|
||||
self.room.state = 7
|
||||
self.room.countdown = 0xffffffff
|
||||
flag_13 = True
|
||||
|
||||
if self.room.countdown <= 0:
|
||||
self.room.countdown = 0
|
||||
|
||||
if self.room.state == 7 or self.room.state == 8:
|
||||
if player.timer < bi(self.command[28:32]) or bi(self.command[28:32]) == 0 and player.timer != 0:
|
||||
player.last_timer = player.timer
|
||||
player.last_score = player.score
|
||||
player.timer = bi(self.command[28:32])
|
||||
player.score = bi(self.command[24:28])
|
||||
|
||||
if player.timer != 0 or self.room.state != 8:
|
||||
for i in self.room.players:
|
||||
i.extra_command_queue.append(
|
||||
x.command_0e(self.player_index))
|
||||
|
||||
if self.room.is_ready(8, 1):
|
||||
flag_13 = True
|
||||
self.room.state = 1
|
||||
self.room.song_idx = 0xffff
|
||||
if self.room.round_switch == 1:
|
||||
self.room.make_round()
|
||||
|
||||
for i in self.room.players:
|
||||
i.timer = 0
|
||||
i.score = 0
|
||||
|
||||
if self.room.is_finish():
|
||||
# 有人退房导致的结算
|
||||
self.room.make_finish()
|
||||
flag_13 = True
|
||||
|
||||
if flag_11:
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_11())
|
||||
if flag_12:
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(self.player_index))
|
||||
if flag_13:
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return re
|
||||
|
||||
def command_0a(self):
|
||||
# 退出房间
|
||||
self.room.delete_player(self.player_index)
|
||||
|
||||
x = CommandSender(self.room)
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(self.player_index))
|
||||
|
||||
if self.room.state == 3:
|
||||
self.room.state = 1
|
||||
self.room.song_idx = 0xffff
|
||||
# self.room.command_queue_length += 1
|
||||
# self.room.command_queue.append(x.command_11())
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_14())
|
||||
return None
|
||||
|
||||
def command_0b(self):
|
||||
# 推荐歌曲
|
||||
song_idx = bi(self.command[16:18])
|
||||
x = CommandSender(self.room)
|
||||
for i in range(4):
|
||||
if self.player_index != i and self.room.players[i].online == 1:
|
||||
self.room.players[i].extra_command_queue.append(
|
||||
x.command_0f(self.player_index, song_idx))
|
||||
|
||||
return None
|
||||
46
latest version/udpserver/udp_sender.py
Normal file
46
latest version/udpserver/udp_sender.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import time
|
||||
from .udp_class import Room, b
|
||||
|
||||
|
||||
class CommandSender:
|
||||
def __init__(self, room: Room = Room()) -> None:
|
||||
self.room = room
|
||||
self.timestamp = round(time.time() * 1000000)
|
||||
|
||||
self.random_code = b'\x11\x11\x11\x11\x00\x00\x00\x00'
|
||||
|
||||
def command_0c(self):
|
||||
return b'\x06\x16\x0c\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.state) + b(self.room.countdown, 4) + b(self.timestamp, 8) + b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
|
||||
|
||||
def command_0d(self, code: int):
|
||||
return b'\x06\x16\x0d\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(code) + b'\x07\x07\x07\x07\x07\x07\x07'
|
||||
|
||||
def command_0e(self, player_index: int):
|
||||
# 分数广播
|
||||
player = self.room.players[player_index]
|
||||
return b'\x06\x16\x0e\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + b(player.player_id, 8) + b(player.character_id) + b(player.is_uncapped) + b(player.difficulty) + b(player.score, 4) + b(player.timer, 4) + b(player.cleartype) + b(player.player_state) + b(player.download_percent) + b'\x01' + b(player.last_score, 4) + b(player.last_timer, 4) + b(player.online)
|
||||
|
||||
def command_0f(self, player_index: int, song_idx: int):
|
||||
# 歌曲推荐
|
||||
player = self.room.players[player_index]
|
||||
return b'\x06\x16\x0f\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + b(player.player_id, 8) + b(song_idx, 2) + b'\x06\x06\x06\x06\x06\x06'
|
||||
|
||||
def command_10(self):
|
||||
# 房主宣告
|
||||
return b'\x06\x16\x10\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.host_id, 8)
|
||||
|
||||
def command_11(self):
|
||||
return b'\x06\x16\x11\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + self.room.get_players_info() + b'\x08\x08\x08\x08\x08\x08\x08\x08'
|
||||
|
||||
def command_12(self, player_index: int):
|
||||
player = self.room.players[player_index]
|
||||
return b'\x06\x16\x12\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(player_index) + b(player.player_id, 8) + b(player.character_id) + b(player.is_uncapped) + b(player.difficulty) + b(player.score, 4) + b(player.timer, 4) + b(player.cleartype) + b(player.player_state) + b(player.download_percent) + b(player.online)
|
||||
|
||||
def command_13(self):
|
||||
return b'\x06\x16\x13\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.host_id, 8) + b(self.room.state) + b(self.room.countdown, 4) + b(self.timestamp, 8) + b(self.room.song_idx, 2) + b(self.room.interval, 2) + b(self.room.times, 7) + self.room.get_player_last_score() + b(self.room.last_song_idx, 2) + b(self.room.round_switch, 1) + b'\x01'
|
||||
|
||||
def command_14(self):
|
||||
return b'\x06\x16\x14\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + self.room.song_unlock + b'\x08\x08\x08\x08\x08\x08\x08\x08'
|
||||
|
||||
def command_15(self):
|
||||
return b'\x06\x16\x15\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.room.get_players_info() + self.room.song_unlock + b(self.room.host_id, 8) + b(self.room.state) + b(self.room.countdown, 4) + b(self.timestamp, 8) + b(self.room.song_idx, 2) + b(self.room.interval, 2) + b(self.room.times, 7) + self.room.get_player_last_score() + b(self.room.last_song_idx, 2) + b(self.room.round_switch, 1) + b'\x09\x09\x09\x09\x09\x09\x09\x09\x09'
|
||||
0
latest version/web/__init__.py
Normal file
0
latest version/web/__init__.py
Normal file
@@ -418,7 +418,7 @@ def all_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',
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami']
|
||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap']
|
||||
return render_template('web/changechar.html', skill_ids=skill_ids)
|
||||
|
||||
|
||||
@@ -777,12 +777,12 @@ def change_purchase():
|
||||
discount_from = int(time.mktime(time.strptime(
|
||||
discount_from, "%Y-%m-%dT%H:%M"))) * 1000
|
||||
else:
|
||||
discount_from = None
|
||||
discount_from = -1
|
||||
if discount_to:
|
||||
discount_to = int(time.mktime(time.strptime(
|
||||
discount_to, "%Y-%m-%dT%H:%M"))) * 1000
|
||||
else:
|
||||
discount_to = None
|
||||
discount_to = -1
|
||||
except:
|
||||
error = '数据错误 Wrong data.'
|
||||
flash(error)
|
||||
@@ -847,6 +847,7 @@ def change_purchase_item():
|
||||
purchase_name = request.form['purchase_name']
|
||||
item_id = request.form['item_id']
|
||||
item_type = request.form['type']
|
||||
amount = int(request.form['amount'])
|
||||
except:
|
||||
error = '数据错误 Wrong data.'
|
||||
flash(error)
|
||||
@@ -862,8 +863,8 @@ def change_purchase_item():
|
||||
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))
|
||||
c.execute('''insert into purchase_item values(?,?,?,?)''',
|
||||
(purchase_name, item_id, item_type, amount))
|
||||
flash('''购买项目的物品添加成功 Successfully add the purchase's item.''')
|
||||
else:
|
||||
error = '''物品不存在 The item does not exist.'''
|
||||
|
||||
@@ -5,6 +5,7 @@ import json
|
||||
import server.arcscore
|
||||
import hashlib
|
||||
from random import Random
|
||||
from setting import Config
|
||||
|
||||
|
||||
def int2b(x):
|
||||
@@ -204,13 +205,13 @@ def update_database():
|
||||
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_one_table(c1, c2, 'api_login')
|
||||
|
||||
update_one_table(c1, c2, 'user_char')
|
||||
|
||||
# ---You can comment this line by yourself, if you want to only keep newest official character data.
|
||||
update_one_table(c1, c2, 'character')
|
||||
# ---
|
||||
if not Config.UPDATE_WITH_NEW_CHARACTER_DATA:
|
||||
update_one_table(c1, c2, 'character')
|
||||
|
||||
update_user_char(c2) # 更新user_char_full
|
||||
|
||||
os.remove('database/old_arcaea_database.db')
|
||||
@@ -301,7 +302,7 @@ def get_all_purchase():
|
||||
items = []
|
||||
if y:
|
||||
for j in y:
|
||||
items.append({'item_id': j[1], 'type': j[2]})
|
||||
items.append({'item_id': j[1], 'type': j[2], 'amount':j[3]})
|
||||
|
||||
re.append({'purchase_name': i[0],
|
||||
'price': i[1],
|
||||
|
||||
Reference in New Issue
Block a user