mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-08 16:57:26 +08:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5de274fdf5 | ||
|
|
b60457c38b | ||
|
|
35b954e549 | ||
|
|
ceebba4664 | ||
|
|
5bc9b9a3dc | ||
|
|
d9a543bc5a | ||
|
|
4666c9c153 | ||
|
|
32bcbb0ccd | ||
|
|
34497d0638 | ||
|
|
23bf3c020f | ||
|
|
e1fc1bbcd1 | ||
|
|
355ec3557f | ||
|
|
cc4ac192e7 | ||
|
|
5068c7215c | ||
|
|
99f05cabb7 | ||
|
|
1a097bc4d0 | ||
|
|
849f4f7260 | ||
|
|
add81ee639 | ||
|
|
b927ad23f8 | ||
|
|
8bc74b36c7 | ||
|
|
7dc8bfea9a | ||
|
|
d0be49fe20 | ||
|
|
b494b93c14 | ||
|
|
a523ff5aae | ||
|
|
acbe9fc4a1 | ||
|
|
9422bf7182 | ||
|
|
53acad4ffb | ||
|
|
13048e57f4 | ||
|
|
39d3f073ba | ||
|
|
2cf6595478 | ||
|
|
7c3bc99570 | ||
|
|
168e5f0b12 | ||
|
|
9286caf900 | ||
|
|
b0647bedb6 | ||
|
|
f49a868707 | ||
|
|
28bca25092 | ||
|
|
0227d2b44a | ||
|
|
0d95b886bc | ||
|
|
9b61acd163 | ||
|
|
be134bbc6e | ||
|
|
c8d0463b9e | ||
|
|
871c2082a8 | ||
|
|
14b685850c | ||
|
|
3fbf473de2 | ||
|
|
8a3cec05b7 | ||
|
|
e3d81436d0 | ||
|
|
941a79ccc7 | ||
|
|
62902a561c | ||
|
|
1660bc5899 | ||
|
|
d577246d28 | ||
|
|
9fd626468e | ||
|
|
c51a1b4e85 | ||
|
|
0e42123417 | ||
|
|
e216e6144b | ||
|
|
b70c435a4a | ||
|
|
eb03d2be93 | ||
|
|
fcb5d264ef | ||
|
|
d617951e1f | ||
|
|
febf931f1a | ||
|
|
981961d6a1 | ||
|
|
a90380df4c | ||
|
|
4f66f492fc | ||
|
|
df181f104e | ||
|
|
a890a9af4a | ||
|
|
c6bb4c9afb | ||
|
|
98358ad216 | ||
|
|
e0e6128826 | ||
|
|
08872399cc | ||
|
|
29a6cfaac1 | ||
|
|
f25bd4130e | ||
|
|
68c757f9d7 | ||
|
|
26216bf84a | ||
|
|
5771d0c669 |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*.log
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
70
README.md
70
README.md
@@ -2,41 +2,49 @@
|
||||
一个微型的Arcaea本地服务器 A small local server for Arcaea
|
||||
|
||||
## 简介 Introduction
|
||||
这是基于Python以及Flask的微型本地Arcaea服务器,可以模拟游戏的主要功能,一时兴起在五天之内糊了出来。这可能是我第一次写这种大程序,若有不妥之处,敬请谅解。
|
||||
This is a small local Arcaea server based on Python and Flask, which can simulate the main functions of the game. I completed it on the spur of the moment in five days. This may be the first time I have written such a large program. Please understand if there is something wrong with it.
|
||||
这是基于Python以及Flask的微型本地Arcaea服务器,可以模拟游戏的主要功能。这可能是我第一次写这种大程序,若有不妥之处,敬请谅解。
|
||||
|
||||
本程序主要用于学习研究,不得用于任何商业行为,否则后果自负,这不是强制要求,只是一个提醒与警告。
|
||||
|
||||
This is a small local Arcaea server based on Python and Flask, which can simulate the main functions of the game. This may be the first time I have written such a large program. Please understand if there is something wrong with it.
|
||||
|
||||
This procedure is mainly used for study and research, and shall not be used for any commercial activities, otherwise the consequences will be borne by oneself. This is not a mandatory requirement, just a reminder and warning.
|
||||
|
||||
> 虽然看起来很蠢,但是可以用!
|
||||
> It looks stupid, but it works!
|
||||
|
||||
## 特性 Features
|
||||
有以下 We have:
|
||||
- 登录、注册 Login and registration
|
||||
- 多设备登录 Multi device login
|
||||
- 成绩上传 Score upload
|
||||
- PTT
|
||||
- 世界排名 Global rank
|
||||
- 排名 Rank
|
||||
- Link Play
|
||||
- 好友系统 Friends
|
||||
- 数据同步 Data synchronization
|
||||
- 爬梯 Climbing steps
|
||||
- 自定义世界模式 Customizable World Mode
|
||||
- 自定义歌曲下载 Customizable songs download
|
||||
- 单曲和曲包购买(没啥用) Single songs and song packs purchase(useless)
|
||||
- 全角色立绘 All character drawings
|
||||
- 角色技能 Character skills
|
||||
- 自定义角色属性 Customizable characters attributes
|
||||
- 奖励系统 Present system
|
||||
- 兑换码系统 Redeem code system
|
||||
- 角色系统 Character system
|
||||
- 全剧情解锁 Unlock all the stories
|
||||
- 后台查询 Background search
|
||||
- 后台自定义信息 Customize some things in the background
|
||||
- 成绩校验 Score check
|
||||
- 下载校验 Download check
|
||||
- 服务器日志 Server log
|
||||
|
||||
没有以下 We don't have:
|
||||
- 角色数值 Character characteristic value
|
||||
- 数据同步的时间记录 The record of time for data synchronization
|
||||
- 服务器安全性保证 Server security assurance
|
||||
- 登录奖励 Login present
|
||||
|
||||
可能有问题 There may be problems:
|
||||
- Recent 30
|
||||
- 一些歌曲的解锁 Some Songs' unlocking
|
||||
- 一些歌曲的解锁 Some songs' unlocking
|
||||
- 同设备多共存登录 Multiple app logins on the same device
|
||||
|
||||
## 说明 Statement
|
||||
只是很有趣,用处探索中。
|
||||
@@ -46,37 +54,48 @@ 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.
|
||||
|
||||
> 提醒:更新时请注意保留原先的数据库,以防数据丢失
|
||||
> 提醒:更新时请注意保留原先的数据库,以防数据丢失。
|
||||
>
|
||||
> Tips: When updating, please keep the original database in case of data loss.
|
||||
> Tips: When updating, please keep the original database in case of data loss.
|
||||
|
||||
### Version 1.8.1
|
||||
- 适用于Arcaea 3.3.1版本 For Arcaea 3.3.1
|
||||
|
||||
### Version 2.8.1
|
||||
- 适用于Arcaea 3.12.0版本 For Arcaea 3.12.0
|
||||
- 更新了歌曲数据库 Update the song database.
|
||||
- 修复了一些Bug Fix some bugs.
|
||||
- 新搭档**维塔**已解锁 Unlock the character **Vita**.
|
||||
- 新增对**维塔**的技能支持 Add support for the skill of **Vita**.
|
||||
- 修正世界模式进度计算方式 Revise the algorithm of world mode progress.
|
||||
- 修复世界模式下 **对立(风暴)** 数值计算错误的问题 Fix the wrong value of **Tairitsu(Tempest)** in the World Mode.
|
||||
|
||||
|
||||
- 以下是累积更新 The following are cumulative updates:
|
||||
- #35 集成式接口优化(By Young-Lord) Optimize `aggregate` interface. (By Young-Lord)
|
||||
- 新增填写Link Play服务器地址的选项,解决地址无法正确自动获取的问题 Add the option of filling in Link Play server address, which can solve the problem that the address cannot be obtained automatically.
|
||||
- 修复高版本iOS客户端无法登陆的问题 Fix a bug that the high version of iOS client cannot log in.
|
||||
- 修复有关下载的安全性问题 Fix a safety problem about downloading.
|
||||
- 修复有不同音频的Beyond谱面无法下载的问题 Fix a bug about unable to download a beyond chart which has another audio file.
|
||||
|
||||
|
||||
|
||||
> 提醒:只是一次小更新,本次更新前后登录信息无法同步,需重新登录(我忘记给login表加主键了同步不了哈哈哈)
|
||||
> 已发现的问题:设置Favorite角色可能并不有效
|
||||
> 预告:登录奖励系统
|
||||
>
|
||||
> Tips: It is only a small update. The login information before and after this update can't be synchronized, so you need to log in again. (I forgot to add a primary key to the login table, so it can't be synchronized. lol)
|
||||
> Problem found: Setting the favorite character may be useless.
|
||||
> Preview: Login present system
|
||||
|
||||
## 运行环境与依赖 Running environment and requirements
|
||||
- Windows操作系统 Windows operating system
|
||||
- Windows/Linux/Mac OS/Android...
|
||||
- Python 3
|
||||
- Flask模块 Flask module
|
||||
- Charles
|
||||
- Flask module, Cryptography module
|
||||
- Charles, IDA, proxy app... (optional)
|
||||
|
||||
<!--
|
||||
## 环境搭建 Environment construction
|
||||
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA)
|
||||
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Environment-construction)
|
||||
-->
|
||||
|
||||
## 使用说明 Instruction for use
|
||||
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
|
||||
@@ -86,6 +105,7 @@ It is just so interesting. What it can do is under exploration.
|
||||
[中文](https://github.com/Lost-MSth/Arcaea-server/wiki/%E6%B3%A8%E6%84%8F)
|
||||
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Attentions)
|
||||
|
||||
|
||||
## 鸣谢 Thanks
|
||||
歌曲数据库来自 Using song database from
|
||||
[BotArcAPI releases](https://github.com/TheSnowfield/BotArcAPI/releases)
|
||||
|
||||
0
latest version/api/__init__.py
Normal file
0
latest version/api/__init__.py
Normal file
156
latest version/api/api_auth.py
Normal file
156
latest version/api/api_auth.py
Normal file
@@ -0,0 +1,156 @@
|
||||
import hashlib
|
||||
import base64
|
||||
import time
|
||||
import random
|
||||
from server.sql import Connect
|
||||
import functools
|
||||
from setting import Config
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
class User():
|
||||
# 用户类,当数据类型用
|
||||
def __init__(self, user_id=None, role='', power=[]):
|
||||
self.user_id = user_id
|
||||
self.role = role
|
||||
self.power = power
|
||||
|
||||
|
||||
def login(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(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):
|
||||
# user_id获取role_id
|
||||
role_id = 1
|
||||
c.execute('''select role_id from user_role where user_id = :a''',
|
||||
{'a': user_id})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
role_id = int(x[0])
|
||||
|
||||
return role_id
|
||||
|
||||
|
||||
def role_id_get_role(c, role_id):
|
||||
# role_id获取role
|
||||
role = ''
|
||||
c.execute('''select role_name from role where role_id = :a''',
|
||||
{'a': role_id})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
role = x[0]
|
||||
|
||||
return role
|
||||
|
||||
|
||||
def api_token_get_id(c, token):
|
||||
# api的token获取user_id
|
||||
user_id = None
|
||||
c.execute('''select user_id from api_login where token = :token''', {
|
||||
'token': token})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
user_id = x[0]
|
||||
|
||||
return user_id
|
||||
|
||||
|
||||
def get_role_power(c, role_id):
|
||||
# 获取role_id对应power,返回列表
|
||||
|
||||
role_power = []
|
||||
|
||||
c.execute('''select power_name from power where power_id in (select power_id from role_power where role_id=:a)''', {
|
||||
'a': role_id})
|
||||
x = c.fetchall()
|
||||
for i in x:
|
||||
role_power.append(i[0])
|
||||
|
||||
return role_power
|
||||
|
||||
|
||||
def role_required(request, power=[]):
|
||||
# api token验证,写成了修饰器
|
||||
def decorator(view):
|
||||
@functools.wraps(view)
|
||||
def wrapped_view(*args, **kwargs):
|
||||
|
||||
try:
|
||||
request.json # 检查请求json格式
|
||||
except:
|
||||
return jsonify({'status': 400, 'code': -1, 'data': {}, 'msg': 'Payload must be a valid json'})
|
||||
|
||||
if not 'Token' in request.headers:
|
||||
return jsonify({'status': 401, 'code': -1, 'data': {}, 'msg': 'No token'})
|
||||
|
||||
user = User()
|
||||
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
|
||||
user.user_id = 0
|
||||
elif power == []:
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
else:
|
||||
with Connect() as c:
|
||||
user.user_id = api_token_get_id(
|
||||
c, request.headers['Token'])
|
||||
if user.user_id is None:
|
||||
return jsonify({'status': 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)
|
||||
user.role_power = get_role_power(c, role_id)
|
||||
|
||||
f = False
|
||||
for i in power:
|
||||
if i in user.role_power:
|
||||
f = True
|
||||
break
|
||||
if not f:
|
||||
return jsonify({'status': 403, 'code': -1, 'data': {}, 'msg': 'No permission'})
|
||||
|
||||
return view(user, *args, **kwargs)
|
||||
|
||||
return wrapped_view
|
||||
return decorator
|
||||
19
latest version/api/api_code.py
Normal file
19
latest version/api/api_code.py
Normal file
@@ -0,0 +1,19 @@
|
||||
def code_get_msg(code):
|
||||
# api接口code获取msg,返回字符串
|
||||
msg = {
|
||||
'0': '',
|
||||
'-1': 'See status code',
|
||||
'-2': 'No data',
|
||||
'-3': 'No data or user',
|
||||
'-4': 'No user_id',
|
||||
'-100': 'Wrong post data',
|
||||
'-101': 'Wrong data type',
|
||||
'-102': 'Wrong query parameter',
|
||||
'-103': 'Wrong sort parameter',
|
||||
'-104': 'Wrong sort order parameter',
|
||||
'-201': 'Wrong username or password',
|
||||
'-202': 'User is banned',
|
||||
'-999': 'Unknown error'
|
||||
}
|
||||
|
||||
return msg[str(code)]
|
||||
228
latest version/api/api_main.py
Normal file
228
latest version/api/api_main.py
Normal file
@@ -0,0 +1,228 @@
|
||||
from flask import (
|
||||
Blueprint, request, jsonify
|
||||
)
|
||||
import functools
|
||||
import api.api_auth
|
||||
import api.users
|
||||
import api.songs
|
||||
from api.api_code import code_get_msg
|
||||
|
||||
|
||||
bp = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||
|
||||
|
||||
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=[]):
|
||||
# 提取查询请求参数,返回Query类查询参数,写成修饰器
|
||||
|
||||
def decorator(view):
|
||||
@functools.wraps(view)
|
||||
def wrapped_view(*args, **kwargs):
|
||||
|
||||
re = Query()
|
||||
|
||||
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 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(
|
||||
str(request.json['auth']), request.remote_addr)
|
||||
return return_encode(code, data)
|
||||
else:
|
||||
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 return_encode()
|
||||
|
||||
|
||||
@bp.route('/token', methods=['DELETE'])
|
||||
@api.api_auth.role_required(request, ['change_me', 'select_me', 'select'])
|
||||
def token_delete(user):
|
||||
# 登出
|
||||
return return_encode(api.api_auth.logout(user))
|
||||
|
||||
|
||||
@bp.route('/users', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select'])
|
||||
@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):
|
||||
# 查询全用户信息
|
||||
|
||||
data = api.users.get_users(query)
|
||||
|
||||
if not data:
|
||||
return return_encode(-2)
|
||||
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
||||
def users_user_get(user, user_id):
|
||||
# 查询用户信息
|
||||
|
||||
if user_id == 'me':
|
||||
user_id = user.user_id
|
||||
|
||||
if user_id <= 0:
|
||||
return return_encode(-4)
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return return_encode(-1, {}, 403, 'No permission')
|
||||
|
||||
data = api.users.get_user_info(user_id)
|
||||
|
||||
if not data:
|
||||
return return_encode(-3)
|
||||
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/b30', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
||||
def users_user_b30_get(user, user_id):
|
||||
# 查询用户b30
|
||||
|
||||
if user_id <= 0:
|
||||
return return_encode(-4)
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return return_encode(-1, {}, 403, 'No permission')
|
||||
|
||||
data = api.users.get_user_b30(user_id)
|
||||
|
||||
if data['data'] == []:
|
||||
return return_encode(-3)
|
||||
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/best', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
||||
@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 <= 0:
|
||||
return return_encode(-4)
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return return_encode(-1, {}, 403, 'No permission')
|
||||
|
||||
data = api.users.get_user_best(user_id, query)
|
||||
|
||||
if data['data'] == []:
|
||||
return return_encode(-3)
|
||||
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/users/<int:user_id>/r30', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_me'])
|
||||
def users_user_r30_get(user, user_id):
|
||||
# 查询用户r30
|
||||
|
||||
if user_id <= 0:
|
||||
return return_encode(-4)
|
||||
|
||||
if user_id != user.user_id and not 'select' in user.power and user.user_id != 0: # 查别人需要select权限
|
||||
return return_encode(-1, {}, 403, 'No permission')
|
||||
|
||||
data = api.users.get_user_r30(user_id)
|
||||
|
||||
if data['data'] == []:
|
||||
return return_encode(-3)
|
||||
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/songs/<string:song_id>', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_song_info'])
|
||||
def songs_song_get(user, song_id):
|
||||
# 查询歌曲信息
|
||||
|
||||
data = api.songs.get_song_info(song_id)
|
||||
|
||||
if not data:
|
||||
return return_encode(-2)
|
||||
|
||||
return return_encode(0, data)
|
||||
|
||||
|
||||
@bp.route('/songs', methods=['GET'])
|
||||
@api.api_auth.role_required(request, ['select', 'select_song_info'])
|
||||
@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):
|
||||
# 查询全歌曲信息
|
||||
|
||||
data = api.songs.get_songs(query)
|
||||
|
||||
if not data:
|
||||
return return_encode(-2)
|
||||
|
||||
return return_encode(0, data)
|
||||
58
latest version/api/songs.py
Normal file
58
latest version/api/songs.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from server.sql import Connect
|
||||
from server.sql import Sql
|
||||
import time
|
||||
|
||||
|
||||
def get_song_info(song_id):
|
||||
# 查询指定歌曲信息,返回字典
|
||||
r = {}
|
||||
|
||||
with Connect('./database/arcsong.db') as c:
|
||||
c.execute('''select * from songs where sid=:a''', {'a': song_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
r = {'song_id': x[0],
|
||||
'name': {'name_en': x[1],
|
||||
'name_jp': x[2]},
|
||||
'pakset': x[5],
|
||||
'artist': x[6],
|
||||
'date': x[9],
|
||||
'rating_pst': x[13]/10,
|
||||
'rating_prs': x[14]/10,
|
||||
'rating_ftr': x[15]/10,
|
||||
'rating_byn': x[16]/10,
|
||||
'difficultly_pst': x[17]/2,
|
||||
'difficultly_prs': x[18]/2,
|
||||
'difficultly_ftr': x[19]/2,
|
||||
'difficultly_byn': x[20]/2
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_songs(query=None):
|
||||
# 查询全部歌曲信息,返回字典列表
|
||||
r = []
|
||||
|
||||
with Connect('./database/arcsong.db') as c:
|
||||
x = Sql.select(c, 'songs', [], query)
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
r.append({'sid': i[0],
|
||||
'name': {'name_en': i[1],
|
||||
'name_jp': i[2]},
|
||||
'pakset': i[5],
|
||||
'artist': i[6],
|
||||
'date': i[9],
|
||||
'rating_pst': i[13]/10,
|
||||
'rating_prs': i[14]/10,
|
||||
'rating_ftr': i[15]/10,
|
||||
'rating_byn': i[16]/10,
|
||||
'difficultly_pst': i[17]/2,
|
||||
'difficultly_prs': i[18]/2,
|
||||
'difficultly_ftr': i[19]/2,
|
||||
'difficultly_byn': i[20]/2
|
||||
})
|
||||
|
||||
return r
|
||||
99
latest version/api/users.py
Normal file
99
latest version/api/users.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from server.sql import Connect
|
||||
from server.sql import Sql
|
||||
import time
|
||||
import web.webscore
|
||||
import server.info
|
||||
|
||||
|
||||
def get_users(query=None):
|
||||
# 获取全用户信息,返回字典列表
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
x = Sql.select(c, 'user', [], query)
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
if i[23] != -1:
|
||||
character_id = i[23]
|
||||
else:
|
||||
character_id = i[6]
|
||||
r.append({
|
||||
'user_id': i[0],
|
||||
'name': i[1],
|
||||
'join_date': i[3],
|
||||
'user_code': i[4],
|
||||
'rating_ptt': i[5]/100,
|
||||
'character_id': character_id,
|
||||
'is_char_uncapped': i[8] == 1,
|
||||
'is_char_uncapped_override': i[9] == 1,
|
||||
'is_hide_rating': i[10],
|
||||
'ticket': i[26]
|
||||
})
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_user_info(user_id):
|
||||
# 获取用户信息,返回字典,其实就是调用user/me信息
|
||||
|
||||
r = {}
|
||||
with Connect() as c:
|
||||
r = server.info.get_user_me(c, user_id)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_user_b30(user_id):
|
||||
# 获取用户b30信息,返回字典
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
r = web.webscore.get_user_score(c, user_id, 30)
|
||||
|
||||
bestptt = 0
|
||||
for i in r:
|
||||
if i['rating']:
|
||||
bestptt += i['rating']
|
||||
if 'time_played' in i:
|
||||
i['time_played'] = int(time.mktime(time.strptime(
|
||||
i['time_played'], '%Y-%m-%d %H:%M:%S')))
|
||||
|
||||
return {'user_id': user_id, 'b30_ptt': bestptt / 30, 'data': r}
|
||||
|
||||
|
||||
def get_user_best(user_id, query=None):
|
||||
# 获取用户b30信息,返回字典
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
x = Sql.select(c, 'best_score', [], query)
|
||||
if x:
|
||||
for i in x:
|
||||
r.append({
|
||||
"song_id": i[1],
|
||||
"difficulty": i[2],
|
||||
"score": i[3],
|
||||
"shiny_perfect_count": i[4],
|
||||
"perfect_count": i[5],
|
||||
"near_count": i[6],
|
||||
"miss_count": i[7],
|
||||
"health": i[8],
|
||||
"modifier": i[9],
|
||||
"time_played": i[10],
|
||||
"best_clear_type": i[11],
|
||||
"clear_type": i[12],
|
||||
"rating": i[13]
|
||||
})
|
||||
|
||||
return {'user_id': user_id, 'data': r}
|
||||
|
||||
|
||||
def get_user_r30(user_id):
|
||||
# 获取用户r30信息,返回字典
|
||||
|
||||
r = []
|
||||
with Connect() as c:
|
||||
r, r10_ptt = web.webscore.get_user_recent30(c, user_id)
|
||||
|
||||
return {'user_id': user_id, 'r10_ptt': r10_ptt, 'data': r}
|
||||
0
latest version/database/__init__.py
Normal file
0
latest version/database/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,340 +1,525 @@
|
||||
import sqlite3
|
||||
import hashlib
|
||||
import time
|
||||
import json
|
||||
|
||||
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
||||
|
||||
conn = sqlite3.connect('arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''create table if not exists user(user_id int primary key,
|
||||
name text unique,
|
||||
password text not null,
|
||||
join_date char(20),
|
||||
user_code char(10),
|
||||
rating_ptt int,
|
||||
character_id int,
|
||||
is_skill_sealed int,
|
||||
is_char_uncapped int,
|
||||
is_char_uncapped_override int,
|
||||
is_hide_rating int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
favorite_character int,
|
||||
max_stamina_notification_enabled int,
|
||||
current_map text,
|
||||
ticket int
|
||||
);''')
|
||||
c.execute('''create table if not exists login(access_token text,
|
||||
user_id int,
|
||||
last_login_time int,
|
||||
last_login_ip text,
|
||||
last_login_device text
|
||||
);''')
|
||||
c.execute('''create table if not exists friend(user_id_me int,
|
||||
user_id_other int,
|
||||
primary key (user_id_me, user_id_other)
|
||||
);''')
|
||||
c.execute('''create table if not exists best_score(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
best_clear_type int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_char(user_id int,
|
||||
character_id int,
|
||||
level int,
|
||||
exp real,
|
||||
level_exp real,
|
||||
frag real,
|
||||
prog real,
|
||||
overdrive real,
|
||||
skill_id text,
|
||||
skill_unlock_level int,
|
||||
skill_requires_uncap int,
|
||||
skill_id_uncap text,
|
||||
char_type int,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int,
|
||||
primary key(user_id, character_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists character(character_id int primary key,
|
||||
name text,
|
||||
level int,
|
||||
exp real,
|
||||
level_exp real,
|
||||
frag real,
|
||||
prog real,
|
||||
overdrive real,
|
||||
skill_id text,
|
||||
skill_unlock_level int,
|
||||
skill_requires_uncap int,
|
||||
skill_id_uncap text,
|
||||
char_type int,
|
||||
uncap_cores text,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int
|
||||
);''')
|
||||
c.execute('''create table if not exists recent30(user_id int primary key,
|
||||
r0 real,
|
||||
song_id0 text,
|
||||
r1 real,
|
||||
song_id1 text,
|
||||
r2 real,
|
||||
song_id2 text,
|
||||
r3 real,
|
||||
song_id3 text,
|
||||
r4 real,
|
||||
song_id4 text,
|
||||
r5 real,
|
||||
song_id5 text,
|
||||
r6 real,
|
||||
song_id6 text,
|
||||
r7 real,
|
||||
song_id7 text,
|
||||
r8 real,
|
||||
song_id8 text,
|
||||
r9 real,
|
||||
song_id9 text,
|
||||
r10 real,
|
||||
song_id10 text,
|
||||
r11 real,
|
||||
song_id11 text,
|
||||
r12 real,
|
||||
song_id12 text,
|
||||
r13 real,
|
||||
song_id13 text,
|
||||
r14 real,
|
||||
song_id14 text,
|
||||
r15 real,
|
||||
song_id15 text,
|
||||
r16 real,
|
||||
song_id16 text,
|
||||
r17 real,
|
||||
song_id17 text,
|
||||
r18 real,
|
||||
song_id18 text,
|
||||
r19 real,
|
||||
song_id19 text,
|
||||
r20 real,
|
||||
song_id20 text,
|
||||
r21 real,
|
||||
song_id21 text,
|
||||
r22 real,
|
||||
song_id22 text,
|
||||
r23 real,
|
||||
song_id23 text,
|
||||
r24 real,
|
||||
song_id24 text,
|
||||
r25 real,
|
||||
song_id25 text,
|
||||
r26 real,
|
||||
song_id26 text,
|
||||
r27 real,
|
||||
song_id27 text,
|
||||
r28 real,
|
||||
song_id28 text,
|
||||
r29 real,
|
||||
song_id29 text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_world(user_id int,
|
||||
map_id text,
|
||||
curr_position int,
|
||||
curr_capture real,
|
||||
is_locked int,
|
||||
primary key(user_id, map_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists world_songplay(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
stamina_multiply int,
|
||||
fragment_multiply int,
|
||||
prog_boost_multiply int,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists download_token(user_id int,
|
||||
song_id text,
|
||||
file_name text,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, song_id, file_name)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_download(user_id int,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, token, time)
|
||||
);''')
|
||||
c.execute('''create table if not exists item(item_id text,
|
||||
type text,
|
||||
is_available int,
|
||||
price int,
|
||||
orig_price int,
|
||||
discount_from int,
|
||||
discount_to int,
|
||||
_id text,
|
||||
primary key(item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_item(user_id int,
|
||||
item_id text,
|
||||
type text,
|
||||
primary key(user_id, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_save(user_id int primary key,
|
||||
scores_data text,
|
||||
clearlamps_data text,
|
||||
clearedsongs_data text,
|
||||
unlocklist_data text,
|
||||
installid_data text,
|
||||
devicemodelname_data text,
|
||||
story_data text
|
||||
);''')
|
||||
c.execute('''create table if not exists present(present_id text primary key,
|
||||
expire_ts int,
|
||||
items text,
|
||||
description text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_present(user_id int,
|
||||
present_id text,
|
||||
primary key(user_id, present_id)
|
||||
);''')
|
||||
ARCAEA_SERVER_VERSION = 'v2.8.1'
|
||||
|
||||
|
||||
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']
|
||||
|
||||
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']
|
||||
|
||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', '', '', 'shirabe_entry_fee',
|
||||
'', '', '', '', '', '', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
||||
|
||||
for i in range(0, 40):
|
||||
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
|
||||
sql = 'insert into character values('+str(
|
||||
i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',1,1)'''
|
||||
c.execute(sql)
|
||||
else:
|
||||
if i != 5:
|
||||
sql = 'insert into character values('+str(
|
||||
i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',0,0)'''
|
||||
c.execute(sql)
|
||||
|
||||
|
||||
def b2int(x):
|
||||
# int与布尔值转换
|
||||
if x:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def insert_items(c, items):
|
||||
# 物品数据导入
|
||||
for i in items:
|
||||
if 'discount_from' not in i:
|
||||
discount_from = -1
|
||||
else:
|
||||
discount_from = i['discount_from']
|
||||
if 'discount_to' not in i:
|
||||
discount_to = -1
|
||||
else:
|
||||
discount_to = i['discount_to']
|
||||
for j in i['items']:
|
||||
if "_id" not in j:
|
||||
_id = ''
|
||||
else:
|
||||
_id = j['_id']
|
||||
if j['type'] != 'character':
|
||||
c.execute('''insert into item(item_id, type, is_available, price, orig_price, discount_from, discount_to, _id) values(:a,:b,:c,:d,:e,:f,:g,:h)''', {
|
||||
'a': j['id'], 'b': j['type'], 'c': b2int(j['is_available']), 'd': i['price'], 'e': i['orig_price'], 'f': discount_from, 'g': discount_to, 'h': _id})
|
||||
|
||||
|
||||
f = open('singles.json', 'r')
|
||||
singles = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, singles)
|
||||
|
||||
f = open('packs.json', 'r')
|
||||
packs = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, packs)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def arc_register(name: str, password: str):
|
||||
def build_user_code(c):
|
||||
return '123456789'
|
||||
|
||||
def build_user_id(c):
|
||||
return 2000000
|
||||
|
||||
# def insert_user_char(c, user_id):
|
||||
# for i in range(0, 38):
|
||||
# if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
|
||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)'''
|
||||
# c.execute(sql)
|
||||
# else:
|
||||
# if i != 5:
|
||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)'''
|
||||
# c.execute(sql)
|
||||
def insert_user_char(c, user_id):
|
||||
# 为用户添加所有可用角色
|
||||
c.execute('''select * from character''')
|
||||
x = c.fetchall()
|
||||
if x != []:
|
||||
for i in x:
|
||||
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', {
|
||||
'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]})
|
||||
|
||||
conn = sqlite3.connect('arcaea_database.db')
|
||||
def main(path='./'):
|
||||
conn = sqlite3.connect(path+'arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute(
|
||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '', 114514)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
c.execute('''create table if not exists config(id text primary key,
|
||||
value text
|
||||
);''')
|
||||
c.execute('''insert into config values("version", :a);''',
|
||||
{'a': ARCAEA_SERVER_VERSION})
|
||||
c.execute('''create table if not exists user(user_id int primary key,
|
||||
name text unique,
|
||||
password text not null,
|
||||
join_date char(20),
|
||||
user_code char(10),
|
||||
rating_ptt int,
|
||||
character_id int,
|
||||
is_skill_sealed int,
|
||||
is_char_uncapped int,
|
||||
is_char_uncapped_override int,
|
||||
is_hide_rating int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
favorite_character int,
|
||||
max_stamina_notification_enabled int,
|
||||
current_map text,
|
||||
ticket int,
|
||||
prog_boost int,
|
||||
email text,
|
||||
world_rank_score int,
|
||||
ban_flag text,
|
||||
next_fragstam_ts int,
|
||||
max_stamina_ts int,
|
||||
stamina int
|
||||
);''')
|
||||
c.execute('''create table if not exists login(access_token text,
|
||||
user_id int,
|
||||
login_time int,
|
||||
login_ip text,
|
||||
login_device text,
|
||||
primary key(access_token, user_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists friend(user_id_me int,
|
||||
user_id_other int,
|
||||
primary key (user_id_me, user_id_other)
|
||||
);''')
|
||||
c.execute('''create table if not exists best_score(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
score int,
|
||||
shiny_perfect_count int,
|
||||
perfect_count int,
|
||||
near_count int,
|
||||
miss_count int,
|
||||
health int,
|
||||
modifier int,
|
||||
time_played int,
|
||||
best_clear_type int,
|
||||
clear_type int,
|
||||
rating real,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_char(user_id int,
|
||||
character_id int,
|
||||
level int,
|
||||
exp real,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int,
|
||||
primary key(user_id, character_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_char_full(user_id int,
|
||||
character_id int,
|
||||
level int,
|
||||
exp real,
|
||||
is_uncapped int,
|
||||
is_uncapped_override int,
|
||||
primary key(user_id, character_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists character(character_id int primary key,
|
||||
name text,
|
||||
max_level int,
|
||||
frag1 real,
|
||||
prog1 real,
|
||||
overdrive1 real,
|
||||
frag20 real,
|
||||
prog20 real,
|
||||
overdrive20 real,
|
||||
frag30 real,
|
||||
prog30 real,
|
||||
overdrive30 real,
|
||||
skill_id text,
|
||||
skill_unlock_level int,
|
||||
skill_requires_uncap int,
|
||||
skill_id_uncap text,
|
||||
char_type int,
|
||||
is_uncapped int
|
||||
);''')
|
||||
c.execute('''create table if not exists char_item(character_id int,
|
||||
item_id text,
|
||||
type text,
|
||||
amount int,
|
||||
primary key(character_id, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists recent30(user_id int primary key,
|
||||
r0 real,
|
||||
song_id0 text,
|
||||
r1 real,
|
||||
song_id1 text,
|
||||
r2 real,
|
||||
song_id2 text,
|
||||
r3 real,
|
||||
song_id3 text,
|
||||
r4 real,
|
||||
song_id4 text,
|
||||
r5 real,
|
||||
song_id5 text,
|
||||
r6 real,
|
||||
song_id6 text,
|
||||
r7 real,
|
||||
song_id7 text,
|
||||
r8 real,
|
||||
song_id8 text,
|
||||
r9 real,
|
||||
song_id9 text,
|
||||
r10 real,
|
||||
song_id10 text,
|
||||
r11 real,
|
||||
song_id11 text,
|
||||
r12 real,
|
||||
song_id12 text,
|
||||
r13 real,
|
||||
song_id13 text,
|
||||
r14 real,
|
||||
song_id14 text,
|
||||
r15 real,
|
||||
song_id15 text,
|
||||
r16 real,
|
||||
song_id16 text,
|
||||
r17 real,
|
||||
song_id17 text,
|
||||
r18 real,
|
||||
song_id18 text,
|
||||
r19 real,
|
||||
song_id19 text,
|
||||
r20 real,
|
||||
song_id20 text,
|
||||
r21 real,
|
||||
song_id21 text,
|
||||
r22 real,
|
||||
song_id22 text,
|
||||
r23 real,
|
||||
song_id23 text,
|
||||
r24 real,
|
||||
song_id24 text,
|
||||
r25 real,
|
||||
song_id25 text,
|
||||
r26 real,
|
||||
song_id26 text,
|
||||
r27 real,
|
||||
song_id27 text,
|
||||
r28 real,
|
||||
song_id28 text,
|
||||
r29 real,
|
||||
song_id29 text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_world(user_id int,
|
||||
map_id text,
|
||||
curr_position int,
|
||||
curr_capture real,
|
||||
is_locked int,
|
||||
primary key(user_id, map_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists world_songplay(user_id int,
|
||||
song_id text,
|
||||
difficulty int,
|
||||
stamina_multiply int,
|
||||
fragment_multiply int,
|
||||
prog_boost_multiply int,
|
||||
primary key(user_id, song_id, difficulty)
|
||||
);''')
|
||||
c.execute('''create table if not exists download_token(user_id int,
|
||||
song_id text,
|
||||
file_name text,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, song_id, file_name)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_download(user_id int,
|
||||
token text,
|
||||
time int,
|
||||
primary key(user_id, token, time)
|
||||
);''')
|
||||
c.execute('''create table if not exists item(item_id text,
|
||||
type text,
|
||||
is_available int,
|
||||
_id text,
|
||||
primary key(item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_item(user_id int,
|
||||
item_id text,
|
||||
type text,
|
||||
amount int,
|
||||
primary key(user_id, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists purchase(purchase_name text primary key,
|
||||
price int,
|
||||
orig_price int,
|
||||
discount_from int,
|
||||
discount_to int
|
||||
);''')
|
||||
c.execute('''create table if not exists purchase_item(purchase_name text,
|
||||
item_id text,
|
||||
type text,
|
||||
amount int,
|
||||
primary key(purchase_name, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists user_save(user_id int primary key,
|
||||
scores_data text,
|
||||
clearlamps_data text,
|
||||
clearedsongs_data text,
|
||||
unlocklist_data text,
|
||||
installid_data text,
|
||||
devicemodelname_data text,
|
||||
story_data text,
|
||||
createdAt int
|
||||
);''')
|
||||
c.execute('''create table if not exists present(present_id text primary key,
|
||||
expire_ts int,
|
||||
description text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_present(user_id int,
|
||||
present_id text,
|
||||
primary key(user_id, present_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists present_item(present_id text,
|
||||
item_id text,
|
||||
type text,
|
||||
amount int,
|
||||
primary key(present_id, item_id, type)
|
||||
);''')
|
||||
c.execute('''create table if not exists songfile(song_id text,
|
||||
file_type int,
|
||||
md5 text,
|
||||
primary key(song_id, file_type)
|
||||
);''')
|
||||
c.execute('''create table if not exists redeem(code text primary key,
|
||||
type int
|
||||
);''')
|
||||
c.execute('''create table if not exists user_redeem(user_id int,
|
||||
code text,
|
||||
primary key(user_id, code)
|
||||
);''')
|
||||
c.execute('''create table if not exists redeem_item(code text,
|
||||
item_id text,
|
||||
type text,
|
||||
amount int,
|
||||
primary key(code, item_id, type)
|
||||
);''')
|
||||
|
||||
c.execute('''create table if not exists role(role_id int primary key,
|
||||
role_name text,
|
||||
caption text
|
||||
);''')
|
||||
c.execute('''create table if not exists user_role(user_id int,
|
||||
role_id int,
|
||||
primary key(user_id, role_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists power(power_id int primary key,
|
||||
power_name text,
|
||||
caption text
|
||||
);''')
|
||||
c.execute('''create table if not exists role_power(role_id int,
|
||||
power_id int,
|
||||
primary key(role_id, power_id)
|
||||
);''')
|
||||
c.execute('''create table if not exists api_login(user_id int,
|
||||
token text,
|
||||
login_time int,
|
||||
login_ip text,
|
||||
primary key(user_id, token)
|
||||
);''')
|
||||
|
||||
# 搭档初始化
|
||||
char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'stella', 'Hikari & Fisica', 'ilith', 'eto', 'luna', 'shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
|
||||
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija', 'vita']
|
||||
|
||||
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', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes', 'skill_vita']
|
||||
|
||||
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, 0, 0, 8, 0]
|
||||
|
||||
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
||||
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 50, 25, 58, 50, 61, 45, 45, 38, 34]
|
||||
|
||||
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
||||
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50]
|
||||
|
||||
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
||||
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 50, 30, 49, 15, 34, 45, 45, 38, 67]
|
||||
|
||||
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
||||
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60, 51]
|
||||
|
||||
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
|
||||
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90, 75]
|
||||
|
||||
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
|
||||
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60, 100]
|
||||
|
||||
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
||||
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60, 51]
|
||||
|
||||
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
|
||||
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90, 75]
|
||||
|
||||
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
|
||||
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60, 100]
|
||||
|
||||
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]
|
||||
|
||||
char_core = {
|
||||
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
||||
1: [{'core_id': 'core_hollow', 'amount': 5}, {'core_id': 'core_desolate', 'amount': 25}],
|
||||
2: [{'core_id': 'core_hollow', 'amount': 5}, {'core_id': 'core_crimson', 'amount': 25}],
|
||||
4: [{'core_id': 'core_ambivalent', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
||||
13: [{'core_id': 'core_scarlet', 'amount': 30}],
|
||||
21: [{'core_id': 'core_scarlet', 'amount': 30}],
|
||||
26: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
27: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
28: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
29: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
36: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
42: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
43: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||
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, 55):
|
||||
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, 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]))
|
||||
else:
|
||||
if i != 5 and i != 46:
|
||||
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
|
||||
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
||||
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
||||
|
||||
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_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", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril"]
|
||||
for i in world_songs:
|
||||
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
|
||||
|
||||
world_unlocks = ["scenery_chap1", "scenery_chap2",
|
||||
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6"]
|
||||
for i in world_unlocks:
|
||||
c.execute('''insert into item values(?,"world_unlock",1,'')''', (i,))
|
||||
|
||||
c.execute('''insert into item values(?,?,?,?)''',
|
||||
('fragment', 'fragment', 1, ''))
|
||||
c.execute('''insert into item values(?,?,?,?)''',
|
||||
('memory', 'memory', 1, ''))
|
||||
|
||||
def insert_items(c, items):
|
||||
# 物品数据导入
|
||||
for i in items:
|
||||
if 'discount_from' not in i:
|
||||
discount_from = -1
|
||||
else:
|
||||
discount_from = i['discount_from']
|
||||
if 'discount_to' not in i:
|
||||
discount_to = -1
|
||||
else:
|
||||
discount_to = i['discount_to']
|
||||
c.execute('''insert into purchase values(?,?,?,?,?)''',
|
||||
(i['name'], i['price'], i['orig_price'], discount_from, discount_to))
|
||||
for j in i['items']:
|
||||
if "_id" not in j:
|
||||
_id = ''
|
||||
else:
|
||||
_id = j['_id']
|
||||
c.execute(
|
||||
'''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')
|
||||
singles = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, singles)
|
||||
|
||||
f = open(path+'packs.json', 'r')
|
||||
packs = json.load(f)
|
||||
f.close()
|
||||
insert_items(c, packs)
|
||||
|
||||
# api权限与权限组初始化
|
||||
role = ['admin', 'user', 'selecter']
|
||||
role_caption = ['管理员', '用户', '查询接口']
|
||||
|
||||
power = ['select', 'select_me', 'change', 'change_me', 'grant',
|
||||
'grant_inf', 'select_song_rank', 'select_song_info']
|
||||
power_caption = ['总体查询权限', '自我查询权限', '总体修改权限',
|
||||
'自我修改权限', '授权权限', '下级授权权限', '歌曲排行榜查询权限', '歌曲信息查询权限']
|
||||
|
||||
role_power = {'0': [0, 1, 3, 5, 6, 7],
|
||||
'1': [1, 3, 6, 7],
|
||||
'2': [0]
|
||||
}
|
||||
|
||||
for i in range(0, len(role)):
|
||||
c.execute('''insert into role values(:a,:b,:c)''', {
|
||||
'a': i, 'b': role[i], 'c': role_caption[i]})
|
||||
for i in range(0, len(power)):
|
||||
c.execute('''insert into power values(:a,:b,:c)''', {
|
||||
'a': i, 'b': power[i], 'c': power_caption[i]})
|
||||
for i in role_power:
|
||||
for j in role_power[i]:
|
||||
c.execute('''insert into role_power values(:a,:b)''',
|
||||
{'a': int(i), 'b': j})
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def arc_register(name: str):
|
||||
def build_user_code(c):
|
||||
return '123456789'
|
||||
|
||||
def build_user_id(c):
|
||||
return 2000000
|
||||
|
||||
def insert_user_char(c, user_id):
|
||||
# 为用户添加可用角色
|
||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||
(user_id, 0, 30, 25000, 1, 0))
|
||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||
(user_id, 1, 30, 25000, 1, 0))
|
||||
|
||||
c.execute(
|
||||
'''select character_id, max_level, is_uncapped from character''')
|
||||
x = c.fetchall()
|
||||
if x:
|
||||
for i in x:
|
||||
exp = 25000 if i[1] == 30 else 10000
|
||||
c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
|
||||
(user_id, i[0], i[1], exp, i[2], 0))
|
||||
|
||||
conn = sqlite3.connect(path+'arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
hash_pwd = '41e5653fc7aeb894026d6bb7b2db7f65902b454945fa8fd65a6327047b5277fb'
|
||||
c.execute(
|
||||
'''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''')
|
||||
insert_user_char(c, user_id)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 1250, 1, 0, 1, 0, 0, -1, 0, '', 114514)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
c.execute(
|
||||
'''insert into best_score values(2000000,'vexaria',3,10000000,100,0,0,0,100,0,1599667200,3,3,10.8)''')
|
||||
insert_user_char(c, user_id)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
arc_register('admin')
|
||||
|
||||
|
||||
arc_register('admin', 'admin123')
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
78
latest version/database/map/byd_lostcivilization.json
Normal file
78
latest version/database/map/byd_lostcivilization.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"map_id": "byd_lostcivilization",
|
||||
"is_legacy": false,
|
||||
"chapter": 1001,
|
||||
"available_from": -1,
|
||||
"available_to": 9999999999999,
|
||||
"is_repeatable": false,
|
||||
"require_id": "lostcivilization2",
|
||||
"require_type": "chart_unlock",
|
||||
"coordinate": "-650,-900",
|
||||
"is_beyond": true,
|
||||
"stamina_cost": 3,
|
||||
"beyond_health": 250,
|
||||
"character_affinity": [0, 1],
|
||||
"affinity_multiplier": [1.9, 1.9],
|
||||
"step_count": 7,
|
||||
"custom_bg": "",
|
||||
"curr_position": 0,
|
||||
"curr_capture": 0,
|
||||
"is_locked": false,
|
||||
"steps": [{
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 0,
|
||||
"capture": 85
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 1,
|
||||
"capture": 5,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 2,
|
||||
"capture": 5,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 3,
|
||||
"capture": 5,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 4,
|
||||
"capture": 100,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 5,
|
||||
"capture": 50,
|
||||
"items": [{
|
||||
"type": "fragment",
|
||||
"amount": 525
|
||||
}]
|
||||
}, {
|
||||
"map_id": "byd_lostcivilization",
|
||||
"position": 6,
|
||||
"capture": 0,
|
||||
"items": [{
|
||||
"id": "lostcivilization3",
|
||||
"type": "world_song"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
114
latest version/database/map/test3.json
Normal file
114
latest version/database/map/test3.json
Normal file
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"map_id": "test3",
|
||||
"is_legacy": true,
|
||||
"is_beyond": false,
|
||||
"beyond_health": 100,
|
||||
"character_affinity": [],
|
||||
"affinity_multiplier": [],
|
||||
"chapter": 0,
|
||||
"available_from": -1,
|
||||
"available_to": 2106124800000,
|
||||
"is_repeatable": false,
|
||||
"require_id": "",
|
||||
"require_type": "",
|
||||
"require_value": 0,
|
||||
"coordinate": "300,200",
|
||||
"step_count": 12,
|
||||
"custom_bg": "",
|
||||
"stamina_cost": 1,
|
||||
"curr_position": 0,
|
||||
"curr_capture": 0,
|
||||
"is_locked": false,
|
||||
"steps": [{
|
||||
"position": 0,
|
||||
"capture": 10
|
||||
}, {
|
||||
"position": 1,
|
||||
"capture": 10,
|
||||
"items": [{
|
||||
"type": "fragment",
|
||||
"amount": 1000
|
||||
}]
|
||||
}, {
|
||||
"position": 2,
|
||||
"capture": 10,
|
||||
"restrict_id": "base",
|
||||
"restrict_type": "pack_id",
|
||||
"items": [{
|
||||
"type": "memory",
|
||||
"amount": 150
|
||||
}]
|
||||
}, {
|
||||
"position": 3,
|
||||
"capture": 10,
|
||||
"restrict_id": "base",
|
||||
"restrict_type": "pack_id",
|
||||
"step_type": ["randomsong"],
|
||||
"items": [{
|
||||
"id": "supernova",
|
||||
"type": "world_song"
|
||||
}]
|
||||
}, {
|
||||
"position": 4,
|
||||
"capture": 10,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_generic",
|
||||
"amount": 1
|
||||
}]
|
||||
}, {
|
||||
"position": 5,
|
||||
"capture": 10,
|
||||
"step_type": ["speedlimit"],
|
||||
"speed_limit_value": 60,
|
||||
"items": [{
|
||||
"id": "scenery_chap4",
|
||||
"type": "world_unlock"
|
||||
}]
|
||||
}, {
|
||||
"position": 6,
|
||||
"capture": 10,
|
||||
"items": [{
|
||||
"type": "fragment",
|
||||
"amount": 1250
|
||||
}]
|
||||
}, {
|
||||
"position": 7,
|
||||
"capture": 10,
|
||||
"step_type": ["plusstamina"],
|
||||
"plus_stamina_value": 2,
|
||||
"items": [{
|
||||
"type": "fragment",
|
||||
"amount": 125
|
||||
}]
|
||||
}, {
|
||||
"position": 8,
|
||||
"capture": 10,
|
||||
"items": [{
|
||||
"type": "fragment",
|
||||
"amount": 125
|
||||
}]
|
||||
}, {
|
||||
"position": 9,
|
||||
"capture": 10,
|
||||
"items": [{
|
||||
"type": "memory",
|
||||
"amount": 150
|
||||
}]
|
||||
}, {
|
||||
"position": 10,
|
||||
"capture": 10,
|
||||
"items": [{"id": "2", "type": "character"}]
|
||||
}, {
|
||||
"position": 11,
|
||||
"capture": 0,
|
||||
"items": [{
|
||||
"type": "core",
|
||||
"id": "core_crimson",
|
||||
"amount": 50
|
||||
}, {
|
||||
"type": "fragment",
|
||||
"amount": 125
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -1,218 +1,556 @@
|
||||
[{
|
||||
"name": "core",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "core",
|
||||
"_id": "5fd01460a6e343038b057d28",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "shiawase",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "shiawase",
|
||||
"_id": "5fd01461a6e343038b057d65",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "kou",
|
||||
"_id": "5fd01461a6e343038b057d64",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1552089600000,
|
||||
"discount_to": 1552694399000
|
||||
}, {
|
||||
"name": "dynamix",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "dynamix",
|
||||
"_id": "5fd01461a6e343038b057d6f",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "sapphire",
|
||||
"_id": "5fd01461a6e343038b057d6e",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "mirai",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "mirai",
|
||||
"_id": "5fd01461a6e343038b057d69",
|
||||
"is_available": true
|
||||
}, {
|
||||
"type": "character",
|
||||
"id": "lethe",
|
||||
"_id": "5fd01461a6e343038b057d68",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1552089600000,
|
||||
"discount_to": 1552694399000
|
||||
}, {
|
||||
"name": "yugamu",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "yugamu",
|
||||
"_id": "5fd01460a6e343038b057d29",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "lanota",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "lanota",
|
||||
"_id": "5fd01460a6e343038b057d40",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "nijuusei",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "nijuusei",
|
||||
"_id": "5fd01460a6e343038b057d2a",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "rei",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "rei",
|
||||
"_id": "5fd01460a6e343038b057d4d",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "tonesphere",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "tonesphere",
|
||||
"_id": "5fd01460a6e343038b057d42",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "groovecoaster",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "groovecoaster",
|
||||
"_id": "5fd01460a6e343038b057d5b",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "zettai",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "zettai",
|
||||
"_id": "5fd01460a6e343038b057d4f",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500,
|
||||
"discount_from": 1583712000000,
|
||||
"discount_to": 1584316799000
|
||||
}, {
|
||||
"name": "chunithm",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "chunithm",
|
||||
"_id": "5fd01460a6e343038b057d39",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 300,
|
||||
"orig_price": 300
|
||||
}, {
|
||||
"name": "prelude",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "prelude",
|
||||
"_id": "5fd01460a6e343038b057d5c",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 400,
|
||||
"orig_price": 400
|
||||
}, {
|
||||
"name": "omatsuri",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "omatsuri",
|
||||
"_id": "5fd01460a6e343038b057d50",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500
|
||||
}, {
|
||||
"name": "vs",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "vs",
|
||||
"_id": "5fd01460a6e343038b057d3c",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 500,
|
||||
"orig_price": 500
|
||||
}, {
|
||||
"name": "extend",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "extend",
|
||||
"_id": "5fd01460a6e343038b057d5f",
|
||||
"is_available": true
|
||||
}],
|
||||
"price": 700,
|
||||
"orig_price": 700
|
||||
}, {
|
||||
"name": "alice",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "alice",
|
||||
"_id": "5fd01460a6e343038b057d55",
|
||||
"is_available": true
|
||||
}],
|
||||
"orig_price": 500,
|
||||
"price": 500
|
||||
}, {
|
||||
"name": "alice_append_1",
|
||||
"items": [{
|
||||
"type": "pack",
|
||||
"id": "alice_append_1",
|
||||
"_id": "5fd01460a6e343038b057d33",
|
||||
"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
0
latest version/log/error.log
Normal file
0
latest version/log/error.log
Normal file
File diff suppressed because it is too large
Load Diff
2
latest version/requirements.txt
Normal file
2
latest version/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
flask
|
||||
cryptography
|
||||
@@ -1 +1,2 @@
|
||||
python main.py
|
||||
cd /d %~dp0
|
||||
python -B main.py
|
||||
0
latest version/server/__init__.py
Normal file
0
latest version/server/__init__.py
Normal file
@@ -2,10 +2,12 @@ import os
|
||||
import hashlib
|
||||
from flask import url_for
|
||||
import sqlite3
|
||||
from server.sql import Connect
|
||||
import time
|
||||
from setting import Config
|
||||
|
||||
time_limit = 3000 # 每个玩家24小时下载次数限制
|
||||
time_gap_limit = 1000 # 下载链接有效秒数
|
||||
time_limit = Config.DOWNLOAD_TIMES_LIMIT # 每个玩家24小时下载次数限制
|
||||
time_gap_limit = Config.DOWNLOAD_TIME_GAP_LIMIT # 下载链接有效秒数
|
||||
|
||||
|
||||
def get_file_md5(file_path):
|
||||
@@ -13,17 +15,34 @@ def get_file_md5(file_path):
|
||||
if not os.path.isfile(file_path):
|
||||
return None
|
||||
myhash = hashlib.md5()
|
||||
f = open(file_path, 'rb')
|
||||
while True:
|
||||
b = f.read(8096)
|
||||
if not b:
|
||||
break
|
||||
myhash.update(b)
|
||||
f.close()
|
||||
with open(file_path, 'rb') as f:
|
||||
while True:
|
||||
b = f.read(8096)
|
||||
if not b:
|
||||
break
|
||||
myhash.update(b)
|
||||
|
||||
return myhash.hexdigest()
|
||||
|
||||
|
||||
def get_one_song(c, user_id, song_id, file_dir='./database/songs'):
|
||||
def get_url(file_path, **kwargs):
|
||||
# 获取下载地址
|
||||
|
||||
t = ''
|
||||
if 't' in kwargs:
|
||||
t = kwargs['t']
|
||||
|
||||
if Config.DOWNLOAD_LINK_PREFIX:
|
||||
prefix = Config.DOWNLOAD_LINK_PREFIX
|
||||
if prefix[-1] != '/':
|
||||
prefix += '/'
|
||||
|
||||
return prefix + file_path + '?t=' + t
|
||||
else:
|
||||
return url_for('download', file_path=file_path, t=t, _external=True)
|
||||
|
||||
|
||||
def get_one_song(c, user_id, song_id, file_dir='./database/songs', url_flag=True):
|
||||
# 获取一首歌的下载链接,返回字典
|
||||
dir_list = os.listdir(os.path.join(file_dir, song_id))
|
||||
re = {}
|
||||
@@ -32,96 +51,174 @@ def get_one_song(c, user_id, song_id, file_dir='./database/songs'):
|
||||
'a': user_id, 'b': song_id})
|
||||
|
||||
for i in dir_list:
|
||||
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg']:
|
||||
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
|
||||
token = hashlib.md5(
|
||||
(str(user_id) + song_id + i + str(now)).encode(encoding='UTF-8')).hexdigest()
|
||||
token = token[:8]
|
||||
|
||||
if i == 'base.ogg':
|
||||
re['audio'] = {"checksum": get_file_md5(os.path.join(file_dir, song_id, 'base.ogg')),
|
||||
"url": url_for('download', file_path=song_id+'/base.ogg', t=token, _external=True)}
|
||||
if 'audio' not in re:
|
||||
re['audio'] = {}
|
||||
|
||||
c.execute(
|
||||
'''select md5 from songfile where song_id=:a and file_type=-1''', {'a': song_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
checksum = x[0]
|
||||
else:
|
||||
checksum = get_file_md5(os.path.join(
|
||||
file_dir, song_id, 'base.ogg'))
|
||||
|
||||
re['audio']["checksum"] = checksum
|
||||
if url_flag:
|
||||
re['audio']["url"] = get_url(
|
||||
file_path=song_id+'/base.ogg', t=token)
|
||||
|
||||
elif i == '3.ogg':
|
||||
if 'audio' not in re:
|
||||
re['audio'] = {}
|
||||
|
||||
c.execute(
|
||||
'''select md5 from songfile where song_id=:a and file_type=-2''', {'a': song_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
checksum = x[0]
|
||||
else:
|
||||
checksum = get_file_md5(os.path.join(
|
||||
file_dir, song_id, '3.ogg'))
|
||||
|
||||
if url_flag:
|
||||
re['audio']['3'] = {"checksum": checksum, "url": get_url(
|
||||
file_path=song_id+'/3.ogg', t=token)}
|
||||
else:
|
||||
re['audio']['3'] = {"checksum": checksum}
|
||||
else:
|
||||
if 'chart' not in re:
|
||||
re['chart'] = {}
|
||||
re['chart'][i[0]] = {"checksum": get_file_md5(os.path.join(file_dir, song_id, i)),
|
||||
"url": url_for('download', file_path=song_id+'/'+i, t=token, _external=True)}
|
||||
c.execute(
|
||||
'''select md5 from songfile where song_id=:a and file_type=:b''', {'a': song_id, 'b': int(i[0])})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
checksum = x[0]
|
||||
else:
|
||||
checksum = get_file_md5(os.path.join(file_dir, song_id, i))
|
||||
|
||||
c.execute('''insert into download_token values(:a,:b,:c,:d,:e)''', {
|
||||
'a': user_id, 'b': song_id, 'c': i, 'd': token, 'e': now})
|
||||
if url_flag:
|
||||
re['chart'][i[0]] = {"checksum": checksum, "url": get_url(
|
||||
file_path=song_id+'/'+i, t=token)}
|
||||
else:
|
||||
re['chart'][i[0]] = {"checksum": checksum}
|
||||
|
||||
if url_flag:
|
||||
c.execute('''insert into download_token values(:a,:b,:c,:d,:e)''', {
|
||||
'a': user_id, 'b': song_id, 'c': i, 'd': token, 'e': now})
|
||||
|
||||
return {song_id: re}
|
||||
|
||||
|
||||
def get_all_songs(user_id, file_dir='./database/songs'):
|
||||
def get_all_songs(user_id, file_dir='./database/songs', url_flag=True):
|
||||
# 获取所有歌的下载链接,返回字典
|
||||
dir_list = os.listdir(file_dir)
|
||||
re = {}
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
for i in dir_list:
|
||||
if os.path.isdir(os.path.join(file_dir, i)):
|
||||
re.update(get_one_song(c, user_id, i))
|
||||
with Connect() as c:
|
||||
for i in dir_list:
|
||||
if os.path.isdir(os.path.join(file_dir, i)):
|
||||
re.update(get_one_song(c, user_id, i, file_dir, url_flag))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return re
|
||||
|
||||
|
||||
def get_some_songs(user_id, song_ids):
|
||||
# 获取一些歌的下载链接,返回字典
|
||||
re = {}
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
for song_id in song_ids:
|
||||
re.update(get_one_song(c, user_id, song_id))
|
||||
with Connect() as c:
|
||||
for song_id in song_ids:
|
||||
re.update(get_one_song(c, user_id, song_id))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return re
|
||||
|
||||
|
||||
def is_token_able_download(t):
|
||||
def is_token_able_download(t, path):
|
||||
# token是否可以下载,返回错误码,0即可以
|
||||
errorcode = 0
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select * from download_token where token = :t limit 1''',
|
||||
{'t': t})
|
||||
x = c.fetchone()
|
||||
now = int(time.time())
|
||||
if x and now - x[4] <= time_gap_limit:
|
||||
c.execute(
|
||||
'''select count(*) from user_download where user_id = :a''', {'a': x[0]})
|
||||
y = c.fetchone()
|
||||
if y and y[0] <= time_limit:
|
||||
c.execute('''insert into user_download values(:a,:b,:c)''', {
|
||||
'a': x[0], 'b': x[3], 'c': now})
|
||||
errorcode = 108
|
||||
with Connect() as c:
|
||||
c.execute('''select * from download_token where token = :t limit 1''',
|
||||
{'t': t})
|
||||
x = c.fetchone()
|
||||
now = int(time.time())
|
||||
if x and now - x[4] <= time_gap_limit and x[1]+'/'+x[2] == path:
|
||||
c.execute(
|
||||
'''select count(*) from user_download where user_id = :a''', {'a': x[0]})
|
||||
y = c.fetchone()
|
||||
if y and y[0] <= time_limit:
|
||||
c.execute('''insert into user_download values(:a,:b,:c)''', {
|
||||
'a': x[0], 'b': x[3], 'c': now})
|
||||
errorcode = 0
|
||||
else:
|
||||
errorcode = 903
|
||||
else:
|
||||
errorcode = 903
|
||||
else:
|
||||
errorcode = 108
|
||||
errorcode = 108
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return errorcode
|
||||
|
||||
|
||||
def is_able_download(user_id):
|
||||
# 是否可以下载,返回布尔值
|
||||
f = True
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
now = int(time.time())
|
||||
c.execute(
|
||||
'''delete from user_download where user_id = :a and time <= :b''', {'a': user_id, 'b': now - 24*3600})
|
||||
c.execute(
|
||||
'''select count(*) from user_download where user_id = :a''', {'a': user_id})
|
||||
y = c.fetchone()
|
||||
if y and y[0] <= time_limit:
|
||||
pass
|
||||
else:
|
||||
f = False
|
||||
f = False
|
||||
with Connect() as c:
|
||||
now = int(time.time())
|
||||
c.execute(
|
||||
'''delete from user_download where user_id = :a and time <= :b''', {'a': user_id, 'b': now - 24*3600})
|
||||
c.execute(
|
||||
'''select count(*) from user_download where user_id = :a''', {'a': user_id})
|
||||
y = c.fetchone()
|
||||
if y and y[0] <= time_limit:
|
||||
f = True
|
||||
else:
|
||||
f = False
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return f
|
||||
|
||||
|
||||
def initialize_one_songfile(c, song_id, file_dir='./database/songs'):
|
||||
# 计算并添加歌曲md5到表中,无返回
|
||||
dir_list = os.listdir(os.path.join(file_dir, song_id))
|
||||
for i in dir_list:
|
||||
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
|
||||
if i == 'base.ogg':
|
||||
c.execute('''insert into songfile values(:a,-1,:c)''', {
|
||||
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, 'base.ogg'))})
|
||||
elif i == '3.ogg':
|
||||
c.execute('''insert into songfile values(:a,-2,:c)''', {
|
||||
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, '3.ogg'))})
|
||||
else:
|
||||
c.execute('''insert into songfile values(:a,:b,:c)''', {
|
||||
'a': song_id, 'b': int(i[0]), 'c': get_file_md5(os.path.join(file_dir, song_id, i))})
|
||||
|
||||
return
|
||||
|
||||
|
||||
def initialize_songfile(file_dir='./database/songs'):
|
||||
# 初始化歌曲数据的md5表,返回错误信息
|
||||
error = None
|
||||
try:
|
||||
dir_list = os.listdir(file_dir)
|
||||
except:
|
||||
error = 'OS error!'
|
||||
return error
|
||||
try:
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
except:
|
||||
error = 'Database error!'
|
||||
return error
|
||||
try:
|
||||
c.execute('''delete from songfile''')
|
||||
for i in dir_list:
|
||||
if os.path.isdir(os.path.join(file_dir, i)):
|
||||
initialize_one_songfile(c, i, file_dir)
|
||||
except:
|
||||
error = 'Initialization error!'
|
||||
finally:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return error
|
||||
|
||||
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
|
||||
@@ -1,4 +1,8 @@
|
||||
import sqlite3
|
||||
from server.sql import Connect
|
||||
import server.item
|
||||
import server.character
|
||||
import time
|
||||
import json
|
||||
|
||||
|
||||
def int2b(x):
|
||||
@@ -9,28 +13,57 @@ def int2b(x):
|
||||
return True
|
||||
|
||||
|
||||
def get_item(c, type='pack'):
|
||||
def get_purchase(c, type='pack'):
|
||||
# 读取packs内容,返回字典列表
|
||||
c.execute('''select * from item where type = :a''', {'a': type})
|
||||
c.execute(
|
||||
'''select * from purchase where purchase_name in (select purchase_name from purchase_item where type = :a)''', {'a': type})
|
||||
x = c.fetchall()
|
||||
if not x:
|
||||
return []
|
||||
|
||||
re = []
|
||||
for i in x:
|
||||
r = {"name": i[0],
|
||||
"items": [{
|
||||
"type": i[1],
|
||||
"id": i[0],
|
||||
"is_available": int2b(i[2])
|
||||
}],
|
||||
"price": i[3],
|
||||
"orig_price": i[4]}
|
||||
items = []
|
||||
c.execute(
|
||||
'''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:
|
||||
if j[3]:
|
||||
amount = j[3]
|
||||
else:
|
||||
amount = 1
|
||||
if i[0] == j[0]:
|
||||
# 物品排序,否则客户端报错
|
||||
t = {
|
||||
"type": j[1],
|
||||
"id": j[0],
|
||||
"is_available": int2b(j[2]),
|
||||
'amount': amount
|
||||
}
|
||||
else:
|
||||
items.append({
|
||||
"type": j[1],
|
||||
"id": j[0],
|
||||
"is_available": int2b(j[2]),
|
||||
"amount": amount
|
||||
})
|
||||
|
||||
if i[5] > 0:
|
||||
r['discount_from'] = i[5]
|
||||
if i[6] > 0:
|
||||
r['discount_to'] = i[6]
|
||||
|
||||
if t is not None:
|
||||
# 放到列表头
|
||||
items = [t, items]
|
||||
|
||||
r = {"name": i[0],
|
||||
"items": items,
|
||||
"price": i[1],
|
||||
"orig_price": i[2]}
|
||||
|
||||
if i[3] > 0:
|
||||
r['discount_from'] = i[3]
|
||||
if i[4] > 0:
|
||||
r['discount_to'] = i[4]
|
||||
|
||||
re.append(r)
|
||||
|
||||
@@ -39,26 +72,17 @@ def get_item(c, type='pack'):
|
||||
|
||||
def get_single_purchase():
|
||||
# main里面没开数据库,这里写一下代替
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
re = get_item(c, type='single')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
re = []
|
||||
with Connect() as c:
|
||||
re = get_purchase(c, type='single')
|
||||
|
||||
return re
|
||||
|
||||
|
||||
def buy_pack(user_id, pack_id):
|
||||
# 曲包购买,返回字典
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select price from item where item_id = :a''', {'a': pack_id})
|
||||
price = c.fetchone()
|
||||
if price:
|
||||
price = price[0]
|
||||
else:
|
||||
price = 0
|
||||
|
||||
c.execute('''select ticket from user where user_id = :a''', {'a': user_id})
|
||||
def buy_item(c, user_id, price):
|
||||
# 购买接口,返回成功与否标识符和剩余源点数
|
||||
c.execute('''select ticket from user where user_id = :a''',
|
||||
{'a': user_id})
|
||||
ticket = c.fetchone()
|
||||
if ticket:
|
||||
ticket = ticket[0]
|
||||
@@ -66,75 +90,122 @@ def buy_pack(user_id, pack_id):
|
||||
ticket = 0
|
||||
|
||||
if ticket < price:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {
|
||||
"success": False
|
||||
}
|
||||
return False, ticket
|
||||
|
||||
c.execute('''update user set ticket = :b where user_id = :a''',
|
||||
{'a': user_id, 'b': ticket-price})
|
||||
c.execute('''insert into user_item values(:a,:b,'pack')''',
|
||||
{'a': user_id, 'b': pack_id})
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {
|
||||
"success": True
|
||||
}
|
||||
return True, ticket - price
|
||||
|
||||
|
||||
def buy_single(user_id, single_id):
|
||||
# 单曲购买,返回字典
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select price from item where item_id = :a''',
|
||||
{'a': single_id})
|
||||
price = c.fetchone()
|
||||
if price:
|
||||
price = price[0]
|
||||
else:
|
||||
def buy_thing(user_id, purchase_id):
|
||||
# 购买物品接口,返回字典
|
||||
success_flag = False
|
||||
ticket = 0
|
||||
packs = []
|
||||
singles = []
|
||||
characters = []
|
||||
|
||||
with Connect() as c:
|
||||
c.execute('''select price, orig_price, discount_from, discount_to from purchase where purchase_name=:a''',
|
||||
{'a': purchase_id})
|
||||
x = c.fetchone()
|
||||
price = 0
|
||||
flag = False
|
||||
if x:
|
||||
price = x[0]
|
||||
orig_price = x[1]
|
||||
discount_from = x[2]
|
||||
discount_to = x[3]
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"error_code": 501
|
||||
}
|
||||
|
||||
c.execute('''select ticket from user where user_id = :a''', {'a': user_id})
|
||||
ticket = c.fetchone()
|
||||
if ticket:
|
||||
ticket = ticket[0]
|
||||
else:
|
||||
ticket = 0
|
||||
c.execute(
|
||||
'''select item_id, type, amount from purchase_item where purchase_name=:a''', {'a': purchase_id})
|
||||
x = c.fetchall()
|
||||
if x:
|
||||
now = int(time.time() * 1000)
|
||||
if not(discount_from <= now <= discount_to):
|
||||
price = orig_price
|
||||
|
||||
if ticket < price:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {
|
||||
"success": False
|
||||
}
|
||||
flag, ticket = buy_item(c, user_id, price)
|
||||
|
||||
c.execute('''update user set ticket = :b where user_id = :a''',
|
||||
{'a': user_id, 'b': ticket-price})
|
||||
c.execute('''insert into user_item values(:a,:b,'single')''',
|
||||
{'a': user_id, 'b': single_id})
|
||||
if flag:
|
||||
for i in x:
|
||||
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:
|
||||
return {
|
||||
"success": False,
|
||||
"error_code": 501
|
||||
}
|
||||
|
||||
packs = server.item.get_user_items(c, user_id, 'pack')
|
||||
singles = server.item.get_user_items(c, user_id, 'single')
|
||||
characters = server.character.get_user_characters(c, user_id)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {
|
||||
"success": True
|
||||
"success": success_flag,
|
||||
"value": {'user_id': user_id,
|
||||
'ticket': ticket,
|
||||
'packs': packs,
|
||||
'singles': singles,
|
||||
'characters': characters
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_prog_boost(user_id):
|
||||
# 世界模式源韵强化,扣50源点,返回剩余源点数
|
||||
|
||||
ticket = -1
|
||||
with Connect() as c:
|
||||
flag, ticket = buy_item(c, user_id, 50)
|
||||
|
||||
if flag:
|
||||
c.execute('''update user set prog_boost = 1 where user_id = :a''', {
|
||||
'a': user_id})
|
||||
if ticket >= 0:
|
||||
return ticket, None
|
||||
else:
|
||||
return 0, 108
|
||||
|
||||
|
||||
def get_user_present(c, user_id):
|
||||
# 获取用户奖励,返回字典列表
|
||||
c.execute(
|
||||
'''select * from present where present_id in (select present_id from user_present where user_id=:a)''', {'a': user_id})
|
||||
x = c.fetchall()
|
||||
re = []
|
||||
now = int(time.time() * 1000)
|
||||
if x:
|
||||
for i in x:
|
||||
re.append({'expire_ts': i[1],
|
||||
'description': i[3],
|
||||
'present_id': i[0],
|
||||
'items': i[2]
|
||||
})
|
||||
if now <= int(i[1]):
|
||||
c.execute(
|
||||
'''select * from present_item where present_id=?''', (i[0],))
|
||||
y = c.fetchall()
|
||||
items = []
|
||||
if y:
|
||||
for j in y:
|
||||
if j is not None:
|
||||
items.append({
|
||||
"type": j[2],
|
||||
"id": j[1],
|
||||
"amount": j[3]
|
||||
})
|
||||
re.append({'expire_ts': i[1],
|
||||
'description': i[2],
|
||||
'present_id': i[0],
|
||||
'items': items
|
||||
})
|
||||
|
||||
return re
|
||||
|
||||
@@ -142,16 +213,70 @@ def get_user_present(c, user_id):
|
||||
def claim_user_present(user_id, present_id):
|
||||
# 确认并删除用户奖励,返回成功与否的布尔值
|
||||
flag = False
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute('''select exists(select * from user_present where user_id=:a and present_id=:b)''',
|
||||
{'a': user_id, 'b': present_id})
|
||||
if c.fetchone() == (1,):
|
||||
flag = True
|
||||
c.execute('''delete from user_present where user_id=:a and present_id=:b''',
|
||||
with Connect() as c:
|
||||
c.execute('''select exists(select * from user_present where user_id=:a and present_id=:b)''',
|
||||
{'a': user_id, 'b': present_id})
|
||||
if c.fetchone() == (1,):
|
||||
c.execute('''delete from user_present where user_id=:a and present_id=:b''',
|
||||
{'a': user_id, 'b': present_id})
|
||||
c.execute('''select * from present where present_id=:b''',
|
||||
{'b': present_id})
|
||||
x = c.fetchone()
|
||||
now = int(time.time() * 1000)
|
||||
if now <= int(x[1]):
|
||||
# 处理memory
|
||||
c.execute(
|
||||
'''select * from present_item where present_id=?''', (x[0],))
|
||||
y = c.fetchall()
|
||||
flag = True
|
||||
if y:
|
||||
for j in y:
|
||||
if j is not None:
|
||||
flag = flag and server.item.claim_user_item(
|
||||
c, user_id, j[1], j[2], j[3])
|
||||
|
||||
else:
|
||||
# 过期
|
||||
flag = False
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return flag
|
||||
|
||||
|
||||
def claim_user_redeem(user_id, code):
|
||||
# 处理兑换码,返回碎片数量和错误码
|
||||
fragment = 0
|
||||
error_code = 108
|
||||
with Connect() as c:
|
||||
c.execute('''select * from redeem where code=:a''', {'a': code})
|
||||
x = c.fetchone()
|
||||
if not x:
|
||||
return 0, 504
|
||||
|
||||
if x[1] == 0: # 一次性
|
||||
c.execute(
|
||||
'''select exists(select * from user_redeem where code=:a)''', {'a': code})
|
||||
if c.fetchone() == (1,):
|
||||
return 0, 505
|
||||
elif x[1] == 1: # 每个玩家一次
|
||||
c.execute('''select exists(select * from user_redeem where code=:a and user_id=:b)''',
|
||||
{'a': code, 'b': user_id})
|
||||
if c.fetchone() == (1,):
|
||||
return 0, 506
|
||||
|
||||
c.execute('''insert into user_redeem values(:b,:a)''',
|
||||
{'a': code, 'b': user_id})
|
||||
|
||||
c.execute('''select * from redeem_item where code=?''', (code,))
|
||||
x = c.fetchall()
|
||||
flag = True
|
||||
if x:
|
||||
for i in x:
|
||||
if i[2] == 'fragment':
|
||||
fragment += i[3]
|
||||
else:
|
||||
flag = flag and server.item.claim_user_item(
|
||||
c, user_id, i[1], i[2], i[3])
|
||||
if flag:
|
||||
error_code = None
|
||||
|
||||
return fragment, error_code
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,14 @@
|
||||
import json
|
||||
import sqlite3
|
||||
from server.sql import Connect
|
||||
from .config import Constant
|
||||
from setting import Config
|
||||
import server.item
|
||||
import server.character
|
||||
import server.info
|
||||
import server.arcpurchase
|
||||
import os
|
||||
import time
|
||||
import random
|
||||
|
||||
|
||||
def int2b(x):
|
||||
@@ -11,6 +19,23 @@ def int2b(x):
|
||||
return True
|
||||
|
||||
|
||||
def calc_stamina(max_stamina_ts, curr_stamina):
|
||||
# 计算体力,返回剩余体力数值
|
||||
|
||||
stamina = int(Constant.MAX_STAMINA - (max_stamina_ts -
|
||||
int(time.time()*1000)) / Constant.STAMINA_RECOVER_TICK)
|
||||
|
||||
if stamina >= Constant.MAX_STAMINA:
|
||||
if curr_stamina >= Constant.MAX_STAMINA:
|
||||
stamina = curr_stamina
|
||||
else:
|
||||
stamina = Constant.MAX_STAMINA
|
||||
if stamina < 0:
|
||||
stamina = 0
|
||||
|
||||
return stamina
|
||||
|
||||
|
||||
def get_world_name(file_dir='./database/map'):
|
||||
# 获取所有地图名称,返回列表
|
||||
L = []
|
||||
@@ -23,64 +48,17 @@ def get_world_name(file_dir='./database/map'):
|
||||
|
||||
def get_world_info(map_id):
|
||||
# 读取json文件内容,返回字典
|
||||
f = open('./database/map/'+map_id+'.json', 'r')
|
||||
world_info = json.load(f)
|
||||
f.close()
|
||||
world_info = {}
|
||||
with open('./database/map/'+map_id+'.json', 'r') as f:
|
||||
world_info = json.load(f)
|
||||
|
||||
return world_info
|
||||
|
||||
|
||||
def get_user_world_info(user_id, map_id):
|
||||
# 读取json文件内容,加上用户信息,返回字典
|
||||
info = get_world_info(map_id)
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select * from user_world where map_id = :a and user_id = :b''',
|
||||
{'a': map_id, 'b': user_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
info['curr_position'] = x[2]
|
||||
info['curr_capture'] = x[3]
|
||||
info['is_locked'] = int2b(x[4])
|
||||
else:
|
||||
c.execute('''insert into user_world values(:a,:b,0,0,0)''', {
|
||||
'a': user_id, 'b': map_id})
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return info
|
||||
|
||||
|
||||
def get_current_map(user_id):
|
||||
# 得到user的当前图,返回字符串
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select current_map from user where user_id = :a''',
|
||||
{'a': user_id})
|
||||
x = c.fetchone()
|
||||
re = ''
|
||||
if x:
|
||||
re = x[0]
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return re
|
||||
|
||||
|
||||
def get_world_all(user_id):
|
||||
# 读取所有地图信息并处理,返回字典列表
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
worlds = get_world_name()
|
||||
re = []
|
||||
for map_id in worlds:
|
||||
info = get_world_info(map_id)
|
||||
steps = info['steps']
|
||||
del info['steps']
|
||||
rewards = []
|
||||
for step in steps:
|
||||
if 'items' in step:
|
||||
rewards.append(
|
||||
{'items': step['items'], 'position': step['position']})
|
||||
info['rewards'] = rewards
|
||||
with Connect() as c:
|
||||
c.execute('''select * from user_world where map_id = :a and user_id = :b''',
|
||||
{'a': map_id, 'b': user_id})
|
||||
x = c.fetchone()
|
||||
@@ -89,81 +67,196 @@ def get_world_all(user_id):
|
||||
info['curr_capture'] = x[3]
|
||||
info['is_locked'] = int2b(x[4])
|
||||
else:
|
||||
c.execute('''insert into user_world values(:a,:b,0,0,0)''', {
|
||||
'a': user_id, 'b': map_id})
|
||||
info['curr_position'] = 0
|
||||
info['curr_capture'] = 0
|
||||
info['is_locked'] = True
|
||||
c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
|
||||
'a': user_id, 'b': map_id})
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def get_current_map(user_id):
|
||||
# 得到user的当前图,返回字符串
|
||||
re = ''
|
||||
with Connect() as c:
|
||||
c.execute('''select current_map from user where user_id = :a''',
|
||||
{'a': user_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
re = x[0]
|
||||
|
||||
return re
|
||||
|
||||
|
||||
def get_world_all(user_id):
|
||||
# 读取所有地图信息并处理,返回字典列表
|
||||
re = []
|
||||
worlds = get_world_name()
|
||||
with Connect() as c:
|
||||
for map_id in worlds:
|
||||
info = get_world_info(map_id)
|
||||
steps = info['steps']
|
||||
del info['steps']
|
||||
rewards = []
|
||||
for step in steps:
|
||||
if 'items' in step:
|
||||
rewards.append(
|
||||
{'items': step['items'], 'position': step['position']})
|
||||
info['rewards'] = rewards
|
||||
c.execute('''select * from user_world where map_id = :a and user_id = :b''',
|
||||
{'a': map_id, 'b': user_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
info['curr_position'] = x[2]
|
||||
info['curr_capture'] = x[3]
|
||||
info['is_locked'] = int2b(x[4])
|
||||
else:
|
||||
info['curr_position'] = 0
|
||||
info['curr_capture'] = 0
|
||||
info['is_locked'] = True
|
||||
c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
|
||||
'a': user_id, 'b': map_id})
|
||||
|
||||
re.append(info)
|
||||
|
||||
return re
|
||||
|
||||
|
||||
def get_available_maps():
|
||||
# 获取当前可用图(用户设定的),返回字典列表
|
||||
re = []
|
||||
for i in Config.AVAILABLE_MAP:
|
||||
info = get_world_info(i)
|
||||
del info['steps']
|
||||
try:
|
||||
del info['is_locked']
|
||||
del info['curr_position']
|
||||
del info['curr_capture']
|
||||
except:
|
||||
pass
|
||||
re.append(info)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return re
|
||||
|
||||
|
||||
def get_user_world(user_id, map_id):
|
||||
# 获取用户图信息,返回字典
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select * from user_world where map_id = :a and user_id = :b''',
|
||||
{'a': map_id, 'b': user_id})
|
||||
x = c.fetchone()
|
||||
re = {
|
||||
"user_id": user_id,
|
||||
"curr_position": 0,
|
||||
"curr_capture": 0,
|
||||
"is_locked": False,
|
||||
"map_id": map_id
|
||||
}
|
||||
if x:
|
||||
re['curr_position'] = x[2]
|
||||
re['curr_capture'] = x[3]
|
||||
re['is_locked'] = int2b(x[4])
|
||||
else:
|
||||
c.execute('''insert into user_world values(:a,:b,0,0,0)''', {
|
||||
'a': user_id, 'b': map_id})
|
||||
re = {}
|
||||
with Connect() as c:
|
||||
c.execute('''select * from user_world where map_id = :a and user_id = :b''',
|
||||
{'a': map_id, 'b': user_id})
|
||||
x = c.fetchone()
|
||||
re = {
|
||||
"user_id": user_id,
|
||||
"curr_position": 0,
|
||||
"curr_capture": 0,
|
||||
"is_locked": True,
|
||||
"map_id": map_id
|
||||
}
|
||||
if x:
|
||||
re['curr_position'] = x[2]
|
||||
re['curr_capture'] = x[3]
|
||||
re['is_locked'] = int2b(x[4])
|
||||
else:
|
||||
c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
|
||||
'a': user_id, 'b': map_id})
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return re
|
||||
|
||||
|
||||
def unlock_user_world(user_id, map_id):
|
||||
# 解锁用户的图,返回成功与否布尔值
|
||||
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select is_locked from user_world where map_id=? and user_id=?''', (map_id, user_id))
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
is_locked = x[0]
|
||||
else:
|
||||
is_locked = 1
|
||||
c.execute('''insert into user_world values(:a,:b,0,0,1)''', {
|
||||
'a': user_id, 'b': map_id})
|
||||
if is_locked == 1:
|
||||
map_info = get_world_info(map_id)
|
||||
if 'require_type' in map_info and map_info['require_type'] != '':
|
||||
if map_info['require_type'] in ['pack', 'single']:
|
||||
c.execute('''select exists(select * from user_item where user_id=? and item_id=? and type=?)''',
|
||||
(user_id, map_info['require_id'], map_info['require_type']))
|
||||
if c.fetchone() == (0,):
|
||||
return False
|
||||
|
||||
c.execute(
|
||||
'''update user_world set is_locked=0 where user_id=? and map_id=?''', (user_id, map_id))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def change_user_current_map(user_id, map_id):
|
||||
# 改变用户当前图
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''update user set current_map = :a where user_id=:b''', {
|
||||
'a': map_id, 'b': user_id})
|
||||
conn.commit()
|
||||
conn.close()
|
||||
with Connect() as c:
|
||||
c.execute('''update user set current_map = :a where user_id=:b''', {
|
||||
'a': map_id, 'b': user_id})
|
||||
return None
|
||||
|
||||
|
||||
def play_world_song(user_id, args):
|
||||
# 声明是世界模式的打歌,并且记录加成信息
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
# 声明是世界模式的打歌,并且记录加成信息,返回字典
|
||||
r = {}
|
||||
with Connect() as c:
|
||||
stamina_multiply = 1
|
||||
fragment_multiply = 100
|
||||
prog_boost_multiply = 0
|
||||
if 'stamina_multiply' in args:
|
||||
stamina_multiply = int(args['stamina_multiply'])
|
||||
if 'fragment_multiply' in args:
|
||||
fragment_multiply = int(args['fragment_multiply'])
|
||||
if 'prog_boost_multiply' in args:
|
||||
c.execute('''select prog_boost from user where user_id=:a''', {
|
||||
'a': user_id})
|
||||
x = c.fetchone()
|
||||
if x and x[0] == 1:
|
||||
prog_boost_multiply = 300
|
||||
|
||||
stamina_multiply = 1
|
||||
fragment_multiply = 100
|
||||
prog_boost_multiply = 0
|
||||
if 'stamina_multiply' in args:
|
||||
stamina_multiply = int(args['stamina_multiply'])
|
||||
if 'fragment_multiply' in args:
|
||||
fragment_multiply = int(args['fragment_multiply'])
|
||||
if 'prog_boost_multiply' in args:
|
||||
prog_boost_multiply = int(args['prog_boost_multiply'])
|
||||
c.execute('''delete from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
|
||||
'a': user_id, 'b': args['song_id'], 'c': args['difficulty']})
|
||||
c.execute('''insert into world_songplay values(:a,:b,:c,:d,:e,:f)''', {
|
||||
'a': user_id, 'b': args['song_id'], 'c': args['difficulty'], 'd': stamina_multiply, 'e': fragment_multiply, 'f': prog_boost_multiply})
|
||||
|
||||
c.execute('''delete from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
|
||||
'a': user_id, 'b': args['song_id'], 'c': args['difficulty']})
|
||||
c.execute('''insert into world_songplay values(:a,:b,:c,:d,:e,:f)''', {
|
||||
'a': user_id, 'b': args['song_id'], 'c': args['difficulty'], 'd': stamina_multiply, 'e': fragment_multiply, 'f': prog_boost_multiply})
|
||||
c.execute('''select current_map from user where user_id = :a''', {
|
||||
'a': user_id})
|
||||
map_id = c.fetchone()[0]
|
||||
info = get_world_info(map_id)
|
||||
# 体力计算
|
||||
c.execute(
|
||||
'''select max_stamina_ts, stamina from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
max_stamina_ts = x[0] if x and x[0] is not None else 0
|
||||
stamina = x[1] if x and x[1] is not None else 12
|
||||
now = int(time.time() * 1000)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
# 体力不足
|
||||
if calc_stamina(max_stamina_ts, stamina) < info['stamina_cost']:
|
||||
return {}
|
||||
stamina = calc_stamina(max_stamina_ts, stamina) - \
|
||||
info['stamina_cost'] * stamina_multiply
|
||||
max_stamina_ts = now + Constant.STAMINA_RECOVER_TICK * \
|
||||
(Constant.MAX_STAMINA - stamina)
|
||||
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
|
||||
(max_stamina_ts, stamina, user_id))
|
||||
r = {
|
||||
"stamina": stamina,
|
||||
"max_stamina_ts": max_stamina_ts,
|
||||
"token": "13145201919810"
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def climb_step(user_id, map_id, step, prev_capture, prev_position):
|
||||
# 爬梯子,返回奖励列表,台阶列表,当前的位置和坐标,图信息
|
||||
|
||||
info = get_world_info(map_id)
|
||||
step_count = int(info['step_count'])
|
||||
|
||||
@@ -257,3 +350,323 @@ def climb_step(user_id, map_id, step, prev_capture, prev_position):
|
||||
steps.append(x)
|
||||
|
||||
return rewards, steps, curr_position, curr_capture, info
|
||||
|
||||
|
||||
def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gauge, health, stamina_multiply=1, fragment_multiply=100, prog_boost_multiply=0):
|
||||
# 成绩上传后世界模式更新,返回字典
|
||||
|
||||
step_times = stamina_multiply * fragment_multiply / \
|
||||
100 * (prog_boost_multiply+100)/100
|
||||
exp_times = stamina_multiply * (prog_boost_multiply+100)/100
|
||||
if prog_boost_multiply != 0:
|
||||
c.execute('''update user set prog_boost = 0 where user_id = :a''', {
|
||||
'a': user_id})
|
||||
c.execute('''delete from world_songplay where user_id=:a and song_id=:b and difficulty=:c''', {
|
||||
'a': user_id, 'b': song_id, 'c': difficulty})
|
||||
|
||||
c.execute(
|
||||
'''select character_id, max_stamina_ts, stamina, is_skill_sealed, is_char_uncapped, is_char_uncapped_override from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
character_id = x[0] if x and x[0] is not None else 0
|
||||
max_stamina_ts = x[1] if x and x[1] is not None else 0
|
||||
stamina = x[2] if x and x[2] is not None else 12
|
||||
is_skill_sealed = x[3] if x and x[3] is not None else 1
|
||||
skill = False
|
||||
skill_uncap = False
|
||||
level = 1
|
||||
exp = 0
|
||||
frag = 50
|
||||
prog = 50
|
||||
overdrive = 50
|
||||
if not is_skill_sealed:
|
||||
if x:
|
||||
skill = True
|
||||
if x[4] is not None and x[4] == 1:
|
||||
skill_uncap = True
|
||||
if x[5] is not None and x[5] == 1:
|
||||
skill_uncap = False
|
||||
|
||||
c.execute('''select frag1,prog1,overdrive1,frag20,prog20,overdrive20,frag30,prog30,overdrive30,skill_id,skill_id_uncap from character where character_id=?''', (character_id,))
|
||||
x = c.fetchone()
|
||||
|
||||
if Config.CHARACTER_FULL_UNLOCK:
|
||||
c.execute('''select level, exp from user_char_full where user_id = :a and character_id = :b''', {
|
||||
'a': user_id, 'b': character_id})
|
||||
else:
|
||||
c.execute('''select level, exp from user_char where user_id = :a and character_id = :b''', {
|
||||
'a': user_id, 'b': character_id})
|
||||
y = c.fetchone()
|
||||
if y:
|
||||
level = y[0]
|
||||
exp = y[1]
|
||||
else:
|
||||
level = 1
|
||||
exp = 0
|
||||
|
||||
if x:
|
||||
frag = 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:
|
||||
frag = 0
|
||||
prog = 0
|
||||
overdrive = 0
|
||||
skill = None
|
||||
skill_uncap = None
|
||||
|
||||
skill_special = ''
|
||||
if skill_uncap is not None and skill_uncap and skill_uncap in ['eto_uncap', 'luna_uncap', 'ayu_uncap', 'skill_vita']:
|
||||
skill_special = skill_uncap
|
||||
elif skill is not None and skill and skill in ['eto_uncap', 'luna_uncap', 'ayu_uncap', 'skill_vita']:
|
||||
skill_special = skill
|
||||
|
||||
c.execute('''select current_map from user where user_id = :a''', {
|
||||
'a': user_id})
|
||||
map_id = c.fetchone()[0]
|
||||
|
||||
if beyond_gauge == 0: # 是否是beyond挑战
|
||||
prog_tempest = 0
|
||||
if not is_skill_sealed and character_id == 35:
|
||||
# 风暴对立
|
||||
if Config.CHARACTER_FULL_UNLOCK:
|
||||
prog_tempest = 60
|
||||
else:
|
||||
c.execute(
|
||||
'''select sum(level) from user_char where user_id=?''', (user_id,))
|
||||
prog_tempest = int(x[0]) / 10 if x else 0
|
||||
if prog_tempest > 60:
|
||||
prog_tempest = 60
|
||||
elif prog_tempest < 0:
|
||||
prog_tempest = 0
|
||||
|
||||
base_step = 2.5 + 2.45*rating**0.5
|
||||
step = base_step * (prog + prog_tempest) / 50 * step_times
|
||||
else:
|
||||
info = get_world_info(map_id)
|
||||
if clear_type == 0:
|
||||
base_step = 25/28 + (rating)**0.5 * 0.43
|
||||
else:
|
||||
base_step = 75/28 + (rating)**0.5 * 0.43
|
||||
|
||||
if character_id in info['character_affinity']:
|
||||
affinity_multiplier = info['affinity_multiplier'][info['character_affinity'].index(
|
||||
character_id)]
|
||||
else:
|
||||
affinity_multiplier = 1
|
||||
|
||||
if skill_special == 'skill_vita':
|
||||
# vita技能,overdrive随回忆率提升,提升量最多为10
|
||||
# 此处采用线性函数
|
||||
overdrive_extra = 0
|
||||
if 0 < health <= 100:
|
||||
overdrive_extra = health / 10
|
||||
|
||||
overdrive += overdrive_extra
|
||||
|
||||
step = base_step * overdrive / 50 * \
|
||||
step_times * affinity_multiplier
|
||||
|
||||
c.execute('''select * from user_world where user_id = :a and map_id =:b''',
|
||||
{'a': user_id, 'b': map_id})
|
||||
y = c.fetchone()
|
||||
if y[4] == 1: # 图不可用
|
||||
return {}
|
||||
|
||||
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||
user_id, map_id, step, y[3], y[2])
|
||||
|
||||
# Eto、Luna、Ayu的技能
|
||||
character_bonus_progress = None
|
||||
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 = Constant.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 = Constant.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 = Constant.AYU_UNCAP_BONUS_PROGRESS
|
||||
else:
|
||||
character_bonus_progress = -Constant.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
|
||||
item_id = j['id'] if 'id' in j else ''
|
||||
server.item.claim_user_item(c, user_id, item_id, j['type'], amount)
|
||||
|
||||
if 'step_type' in steps[-1]:
|
||||
if 'plusstamina' in steps[-1]['step_type'] and 'plus_stamina_value' in steps[-1]:
|
||||
# 体力格子
|
||||
max_stamina_ts, stamina = add_stamina(
|
||||
c, user_id, int(steps[-1]['plus_stamina_value']))
|
||||
# 角色升级
|
||||
if not Config.CHARACTER_FULL_UNLOCK:
|
||||
exp, level = server.character.calc_level_up(
|
||||
c, user_id, character_id, exp, exp_times*rating*6)
|
||||
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
||||
(level, exp, user_id, character_id))
|
||||
else:
|
||||
exp = Constant.LEVEL_STEPS[level]
|
||||
|
||||
re = {
|
||||
"rewards": rewards,
|
||||
"exp": exp,
|
||||
"level": level,
|
||||
"base_progress": base_step,
|
||||
"progress": step,
|
||||
"user_map": {
|
||||
"user_id": user_id,
|
||||
"curr_position": curr_position,
|
||||
"curr_capture": curr_capture,
|
||||
"is_locked": int2b(y[4]),
|
||||
"map_id": map_id,
|
||||
"prev_capture": y[3],
|
||||
"prev_position": y[2],
|
||||
"beyond_health": info['beyond_health']
|
||||
},
|
||||
"char_stats": {
|
||||
"character_id": character_id,
|
||||
"frag": frag,
|
||||
"prog": prog,
|
||||
"overdrive": overdrive
|
||||
},
|
||||
"current_stamina": calc_stamina(max_stamina_ts, stamina),
|
||||
"max_stamina_ts": max_stamina_ts
|
||||
}
|
||||
|
||||
if beyond_gauge == 0:
|
||||
re["user_map"]["steps"] = steps
|
||||
else:
|
||||
re["user_map"]["steps"] = len(steps)
|
||||
|
||||
if character_id == 35 and not is_skill_sealed:
|
||||
re['char_stats']['prog_tempest'] = prog_tempest
|
||||
re['char_stats']['prog'] += prog_tempest
|
||||
|
||||
if character_bonus_progress is not None:
|
||||
re['character_bonus_progress'] = character_bonus_progress
|
||||
|
||||
if stamina_multiply != 1:
|
||||
re['stamina_multiply'] = stamina_multiply
|
||||
if fragment_multiply != 100:
|
||||
re['fragment_multiply'] = fragment_multiply
|
||||
if prog_boost_multiply != 0:
|
||||
re['prog_boost_multiply'] = prog_boost_multiply
|
||||
|
||||
if curr_position == info['step_count']-1 and info['is_repeatable']: # 循环图判断
|
||||
curr_position = 0
|
||||
c.execute('''update user_world set curr_position=:a, curr_capture=:b where user_id=:c and map_id=:d''', {
|
||||
'a': curr_position, 'b': curr_capture, 'c': user_id, 'd': map_id})
|
||||
|
||||
return re
|
||||
|
||||
|
||||
def add_stamina(c, user_id, add_stamina):
|
||||
# 增添体力,返回max_stamina_ts和stamina
|
||||
|
||||
now = int(time.time() * 1000)
|
||||
c.execute(
|
||||
'''select max_stamina_ts, stamina from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
if x and x[0] is not None and x[1] is not None:
|
||||
stamina = calc_stamina(x[0], x[1]) + add_stamina
|
||||
max_stamina_ts = now - \
|
||||
(stamina-Constant.MAX_STAMINA) * \
|
||||
Constant.STAMINA_RECOVER_TICK
|
||||
else:
|
||||
max_stamina_ts = now
|
||||
stamina = Constant.MAX_STAMINA
|
||||
|
||||
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
|
||||
(max_stamina_ts, stamina, user_id))
|
||||
|
||||
return max_stamina_ts, stamina
|
||||
|
||||
|
||||
def buy_stamina_by_fragment(user_id):
|
||||
# 残片买体力,返回字典和错误码
|
||||
r = {}
|
||||
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select next_fragstam_ts from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
now = int(time.time() * 1000)
|
||||
next_fragstam_ts = x[0] if x[0] else 0
|
||||
|
||||
if now < next_fragstam_ts:
|
||||
return {}, 905
|
||||
|
||||
next_fragstam_ts = now + 24*3600*1000
|
||||
max_stamina_ts, stamina = add_stamina(c, user_id, 6)
|
||||
c.execute('''update user set next_fragstam_ts=?, max_stamina_ts=?, stamina=? where user_id=?''',
|
||||
(next_fragstam_ts, max_stamina_ts, stamina, user_id))
|
||||
|
||||
r = {
|
||||
"user_id": user_id,
|
||||
"stamina": stamina,
|
||||
"max_stamina_ts": max_stamina_ts,
|
||||
"next_fragstam_ts": next_fragstam_ts
|
||||
}
|
||||
|
||||
return r, None
|
||||
|
||||
|
||||
def buy_stamina_by_ticket(user_id):
|
||||
# 源点买体力,返回字典和错误码
|
||||
r = {}
|
||||
|
||||
with Connect() as c:
|
||||
flag, ticket = server.arcpurchase.buy_item(c, user_id, 50)
|
||||
if flag:
|
||||
max_stamina_ts, stamina = add_stamina(c, user_id, 6)
|
||||
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
|
||||
(max_stamina_ts, stamina, user_id))
|
||||
r = {
|
||||
"user_id": user_id,
|
||||
"stamina": stamina,
|
||||
"max_stamina_ts": max_stamina_ts,
|
||||
"ticket": ticket
|
||||
}
|
||||
else:
|
||||
return None, 501
|
||||
|
||||
return r, None
|
||||
|
||||
@@ -1,44 +1,87 @@
|
||||
import sqlite3
|
||||
import hashlib
|
||||
import time
|
||||
import server.arcworld
|
||||
from server.sql import Connect
|
||||
import functools
|
||||
from setting import Config
|
||||
from flask import jsonify
|
||||
|
||||
BAN_TIME = [1, 3, 7, 15, 31]
|
||||
|
||||
|
||||
def arc_login(name: str, password: str) -> str: # 登录判断
|
||||
# 查询数据库中的user表,验证账号密码,返回并记录token
|
||||
def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
|
||||
# 查询数据库中的user表,验证账号密码,返回并记录token和user_id,多返回个error code和extra
|
||||
# token采用user_id和时间戳连接后hash生成(真的是瞎想的,没用bear)
|
||||
# 密码和token的加密方式为 SHA-256
|
||||
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute('''select user_id from user where name = :name and password = :password''', {
|
||||
'name': name, 'password': hash_pwd})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
user_id = str(x[0])
|
||||
now = int(time.time() * 1000)
|
||||
token = hashlib.sha256((user_id + str(now)).encode("utf8")).hexdigest()
|
||||
c.execute(
|
||||
'''select exists(select * from login where user_id = :user_id)''', {"user_id": user_id})
|
||||
error_code = 108
|
||||
token = None
|
||||
user_id = None
|
||||
with Connect() as c:
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute('''select user_id, password, ban_flag from user where name = :name''', {
|
||||
'name': name})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
now = int(time.time() * 1000)
|
||||
if x[2] is not None and x[2] != '':
|
||||
# 自动封号检查
|
||||
ban_timestamp = int(x[2].split(':', 1)[1])
|
||||
if ban_timestamp > now:
|
||||
return None, 105, {'remaining_ts': ban_timestamp-now}
|
||||
if x[1] == '':
|
||||
# 账号封禁
|
||||
error_code = 106
|
||||
elif x[1] == hash_pwd:
|
||||
user_id = str(x[0])
|
||||
token = hashlib.sha256(
|
||||
(user_id + str(now)).encode("utf8")).hexdigest()
|
||||
c.execute(
|
||||
'''select login_device from login where user_id = :user_id''', {"user_id": user_id})
|
||||
y = c.fetchall()
|
||||
if y:
|
||||
device_list = []
|
||||
for i in y:
|
||||
if i[0]:
|
||||
device_list.append(i[0])
|
||||
else:
|
||||
device_list.append('')
|
||||
|
||||
if c.fetchone() == (1,): # 删掉多余token
|
||||
c.execute('''delete from login where user_id = :user_id''',
|
||||
{'user_id': user_id})
|
||||
should_delete_num = len(
|
||||
device_list) + 1 - Config.LOGIN_DEVICE_NUMBER_LIMIT
|
||||
|
||||
c.execute('''insert into login(access_token, user_id) values(:access_token, :user_id)''', {
|
||||
'user_id': user_id, 'access_token': token})
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return token
|
||||
if not Config.ALLOW_LOGIN_SAME_DEVICE:
|
||||
if device_id in device_list: # 对相同设备进行删除
|
||||
c.execute('''delete from login where login_device=:a and user_id=:b''', {
|
||||
'a': device_id, 'b': user_id})
|
||||
should_delete_num = len(
|
||||
device_list) + 1 - device_list.count(device_id) - Config.LOGIN_DEVICE_NUMBER_LIMIT
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
if should_delete_num >= 1: # 删掉多余token
|
||||
if not Config.ALLOW_LOGIN_SAME_DEVICE and Config.ALLOW_BAN_MULTIDEVICE_USER_AUTO: # 自动封号检查
|
||||
c.execute(
|
||||
'''select count(*) from login where user_id=? and login_time>?''', (user_id, now-86400000))
|
||||
if c.fetchone()[0] >= Config.LOGIN_DEVICE_NUMBER_LIMIT:
|
||||
remaining_ts = arc_auto_ban(c, user_id, now)
|
||||
return None, None, 105, {'remaining_ts': remaining_ts}
|
||||
|
||||
c.execute('''delete from login where rowid in (select rowid from login where user_id=:user_id limit :a);''',
|
||||
{'user_id': user_id, 'a': int(should_delete_num)})
|
||||
|
||||
c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
|
||||
'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
|
||||
error_code = None
|
||||
else:
|
||||
# 密码错误
|
||||
error_code = 104
|
||||
else:
|
||||
# 用户名错误
|
||||
error_code = 104
|
||||
|
||||
return token, user_id, error_code, None
|
||||
|
||||
|
||||
def arc_register(name: str, password: str): # 注册
|
||||
# 账号注册,只记录hash密码和用户名,生成user_id和user_code,自动登录返回token
|
||||
def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册
|
||||
# 账号注册,记录hash密码、用户名和邮箱,生成user_id和user_code,自动登录返回token
|
||||
# token和密码的处理同登录部分
|
||||
|
||||
def build_user_code(c):
|
||||
@@ -63,76 +106,128 @@ def arc_register(name: str, password: str): # 注册
|
||||
return 2000001
|
||||
|
||||
def insert_user_char(c, user_id):
|
||||
# 为用户添加所有可用角色
|
||||
c.execute('''select * from character''')
|
||||
# 为用户添加初始角色
|
||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||
(user_id, 0, 1, 0, 0, 0))
|
||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||
(user_id, 1, 1, 0, 0, 0))
|
||||
c.execute('''select character_id, max_level, is_uncapped from character''')
|
||||
x = c.fetchall()
|
||||
if x != []:
|
||||
if x:
|
||||
for i in x:
|
||||
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', {
|
||||
'a': user_id, 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]})
|
||||
exp = 25000 if i[1] == 30 else 10000
|
||||
c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
|
||||
(user_id, i[0], i[1], exp, i[2], 0))
|
||||
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute(
|
||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', 0)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
user_id = None
|
||||
token = None
|
||||
error_code = 108
|
||||
with Connect() as c:
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute(
|
||||
'''select exists(select * from user where name = :name)''', {'name': name})
|
||||
if c.fetchone() == (0,):
|
||||
c.execute(
|
||||
'''select exists(select * from user where email = :email)''', {'email': email})
|
||||
if c.fetchone() == (0,):
|
||||
user_code = build_user_code(c)
|
||||
user_id = build_user_id(c)
|
||||
now = int(time.time() * 1000)
|
||||
c.execute('''insert into user(user_id, name, password, join_date, user_code, rating_ptt,
|
||||
character_id, is_skill_sealed, is_char_uncapped, is_char_uncapped_override, is_hide_rating, favorite_character, max_stamina_notification_enabled, current_map, ticket, prog_boost, email)
|
||||
values(:user_id, :name, :password, :join_date, :user_code, 0, 0, 0, 0, 0, 0, -1, 0, '', :memories, 0, :email)
|
||||
''', {'user_code': user_code, 'user_id': user_id, 'join_date': now, 'name': name, 'password': hash_pwd, 'memories': Config.DEFAULT_MEMORIES, 'email': email})
|
||||
c.execute('''insert into recent30(user_id) values(:user_id)''', {
|
||||
'user_id': user_id})
|
||||
|
||||
token = hashlib.sha256(
|
||||
(str(user_id) + str(now)).encode("utf8")).hexdigest()
|
||||
c.execute('''insert into login(access_token, user_id) values(:access_token, :user_id)''', {
|
||||
'user_id': user_id, 'access_token': token})
|
||||
token = hashlib.sha256(
|
||||
(str(user_id) + str(now)).encode("utf8")).hexdigest()
|
||||
c.execute('''insert into login values(:access_token, :user_id, :time, :ip, :device_id)''', {
|
||||
'user_id': user_id, 'access_token': token, 'device_id': device_id, 'time': now, 'ip': ip})
|
||||
|
||||
insert_user_char(c, user_id)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return user_id, token, 0
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None, None, 101
|
||||
insert_user_char(c, user_id)
|
||||
error_code = 0
|
||||
else:
|
||||
error_code = 102
|
||||
else:
|
||||
error_code = 101
|
||||
|
||||
return user_id, token, error_code
|
||||
|
||||
|
||||
def token_get_id(token: str):
|
||||
# 用token获取id,没有考虑不同用户token相同情况,说不定会有bug
|
||||
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select user_id from login where access_token = :token''', {
|
||||
'token': token})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return x[0]
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
user_id = None
|
||||
with Connect() as c:
|
||||
c.execute('''select user_id from login where access_token = :token''', {
|
||||
'token': token})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
user_id = x[0]
|
||||
|
||||
return user_id
|
||||
|
||||
|
||||
def code_get_id(user_code):
|
||||
# 用user_code获取id
|
||||
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select user_id from user where user_code = :a''',
|
||||
{'a': user_code})
|
||||
user_id = None
|
||||
|
||||
with Connect() as c:
|
||||
c.execute('''select user_id from user where user_code = :a''',
|
||||
{'a': user_code})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
user_id = x[0]
|
||||
|
||||
return user_id
|
||||
|
||||
|
||||
def auth_required(request):
|
||||
# arcaea登录验证,写成了修饰器
|
||||
def decorator(view):
|
||||
@functools.wraps(view)
|
||||
def wrapped_view(*args, **kwargs):
|
||||
|
||||
user_id = None
|
||||
headers = request.headers
|
||||
|
||||
if 'AppVersion' in headers: # 版本检查
|
||||
if Config.ALLOW_APPVERSION:
|
||||
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||
return jsonify({"success": False, "error_code": 1203})
|
||||
|
||||
if 'Authorization' in headers:
|
||||
token = headers['Authorization']
|
||||
token = token[7:]
|
||||
user_id = token_get_id(token)
|
||||
|
||||
if user_id is not None:
|
||||
return view(user_id, *args, **kwargs)
|
||||
else:
|
||||
return jsonify({"success": False, "error_code": 108})
|
||||
|
||||
return wrapped_view
|
||||
return decorator
|
||||
|
||||
|
||||
def arc_auto_ban(c, user_id, now):
|
||||
# 多设备自动封号机制,返回封号时长
|
||||
c.execute('''delete from login where user_id=?''', (user_id, ))
|
||||
c.execute('''select ban_flag from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return x[0]
|
||||
if x and x[0] != '' and x[0] is not None:
|
||||
last_ban_time = int(x[0].split(':', 1)[0])
|
||||
i = 0
|
||||
while i < len(BAN_TIME) - 1 and BAN_TIME[i] <= last_ban_time:
|
||||
i += 1
|
||||
ban_time = BAN_TIME[i]
|
||||
else:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
ban_time = BAN_TIME[0]
|
||||
|
||||
ban_flag = ':'.join((str(ban_time), str(now + ban_time*24*60*60*1000)))
|
||||
c.execute('''update user set ban_flag=? where user_id=?''',
|
||||
(ban_flag, user_id))
|
||||
|
||||
return ban_time*24*60*60*1000
|
||||
|
||||
254
latest version/server/character.py
Normal file
254
latest version/server/character.py
Normal file
@@ -0,0 +1,254 @@
|
||||
from setting import Config
|
||||
from server.sql import Connect
|
||||
from .config import Constant
|
||||
import server.info
|
||||
import server.item
|
||||
import server.setme
|
||||
|
||||
|
||||
def int2b(x):
|
||||
# int与布尔值转换
|
||||
if x is None or x == 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def get_level_steps():
|
||||
# 返回level_steps字典数组
|
||||
return [{'level': i, 'level_exp': Constant.LEVEL_STEPS[i]} for i in Constant.LEVEL_STEPS]
|
||||
|
||||
|
||||
def calc_char_value(level, value1, value20, value30):
|
||||
# 计算搭档数值的核心函数,返回浮点数
|
||||
|
||||
def calc_char_value_20(level, stata, statb, lva=1, lvb=20):
|
||||
# 计算1~20级搭档数值的核心函数,返回浮点数,来自https://redive.estertion.win/arcaea/calc/
|
||||
|
||||
n = [0, 0, 0.0005831753900000081, 0.004665403120000065, 0.015745735529959858, 0.03732322495992008, 0.07289692374980007, 0.12596588423968, 0.2000291587694801, 0.29858579967923987, 0.42513485930893946,
|
||||
0.5748651406910605, 0.7014142003207574, 0.7999708412305152, 0.8740341157603029, 0.9271030762501818, 0.962676775040091, 0.9842542644700301, 0.9953345968799998, 0.9994168246100001, 1]
|
||||
e = n[lva] - n[lvb]
|
||||
a = stata - statb
|
||||
r = a / e
|
||||
d = stata - n[lva] * r
|
||||
|
||||
return d + r * n[level]
|
||||
|
||||
def calc_char_value_30(level, stata, statb, lva=20, lvb=30):
|
||||
# 计算21~30级搭档数值,返回浮点数
|
||||
|
||||
return (level - lva) * (statb - stata) / (lvb - lva) + stata
|
||||
|
||||
if level < 1 or level > 30:
|
||||
return 0
|
||||
elif 1 <= level <= 20:
|
||||
return calc_char_value_20(level, value1, value20)
|
||||
else:
|
||||
return calc_char_value_30(level, value20, value30)
|
||||
|
||||
|
||||
def get_char_core(c, character_id):
|
||||
# 得到对应角色觉醒所需的核心,返回字典列表
|
||||
r = []
|
||||
c.execute(
|
||||
'''select item_id, amount from char_item where character_id = ? and type="core"''', (character_id,))
|
||||
x = c.fetchall()
|
||||
if x:
|
||||
for i in x:
|
||||
r.append({'core_type': i[0], 'amount': i[1]})
|
||||
return r
|
||||
|
||||
|
||||
def get_user_characters(c, user_id):
|
||||
# 获取用户所拥有角色,返回列表
|
||||
|
||||
c.execute('''select character_id from user_char where user_id = :user_id''',
|
||||
{'user_id': user_id})
|
||||
|
||||
x = c.fetchall()
|
||||
characters = []
|
||||
|
||||
if x:
|
||||
for i in x:
|
||||
characters.append(i[0])
|
||||
|
||||
return characters
|
||||
|
||||
|
||||
def get_user_character(c, user_id):
|
||||
# 得到用户拥有的角色列表,返回列表
|
||||
|
||||
if Config.CHARACTER_FULL_UNLOCK:
|
||||
c.execute('''select * from user_char_full a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
|
||||
{'user_id': user_id})
|
||||
else:
|
||||
c.execute('''select * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
|
||||
{'user_id': user_id})
|
||||
x = c.fetchall()
|
||||
if not x and not Config.CHARACTER_FULL_UNLOCK:
|
||||
# 添加初始角色
|
||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||
(user_id, 0, 1, 0, 0, 0))
|
||||
c.execute('''insert into user_char values(?,?,?,?,?,?)''',
|
||||
(user_id, 1, 1, 0, 0, 0))
|
||||
c.execute('''select * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id''',
|
||||
{'user_id': user_id})
|
||||
x = c.fetchall()
|
||||
|
||||
if not x:
|
||||
return []
|
||||
r = []
|
||||
for i in x:
|
||||
|
||||
char = {
|
||||
"is_uncapped_override": int2b(i[5]),
|
||||
"is_uncapped": int2b(i[4]),
|
||||
"uncap_cores": get_char_core(c, i[1]),
|
||||
"char_type": i[22],
|
||||
"skill_id_uncap": i[21],
|
||||
"skill_requires_uncap": int2b(i[20]),
|
||||
"skill_unlock_level": i[19],
|
||||
"skill_id": i[18],
|
||||
"overdrive": calc_char_value(i[2], i[11], i[14], i[17]),
|
||||
"prog": calc_char_value(i[2], i[10], i[13], i[16]),
|
||||
"frag": calc_char_value(i[2], i[9], i[12], i[15]),
|
||||
"level_exp": Constant.LEVEL_STEPS[i[2]],
|
||||
"exp": i[3],
|
||||
"level": i[2],
|
||||
"name": i[7],
|
||||
"character_id": i[1]
|
||||
}
|
||||
if i[1] == 21:
|
||||
char["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
|
||||
r.append(char)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_one_character(c, user_id, character_id):
|
||||
# 得到用户某个拥有的角色列表,返回字典
|
||||
|
||||
if Config.CHARACTER_FULL_UNLOCK:
|
||||
c.execute('''select * from user_char_full a,character b where a.user_id = :user_id and a.character_id=b.character_id and a.character_id=:a''',
|
||||
{'user_id': user_id, 'a': character_id})
|
||||
else:
|
||||
c.execute('''select * from user_char a,character b where a.user_id = :user_id and a.character_id=b.character_id and a.character_id=:a''',
|
||||
{'user_id': user_id, 'a': character_id})
|
||||
x = c.fetchone()
|
||||
if not x:
|
||||
return {}
|
||||
r = {
|
||||
"is_uncapped_override": int2b(x[5]),
|
||||
"is_uncapped": int2b(x[4]),
|
||||
"uncap_cores": get_char_core(c, x[1]),
|
||||
"char_type": x[22],
|
||||
"skill_id_uncap": x[21],
|
||||
"skill_requires_uncap": int2b(x[20]),
|
||||
"skill_unlock_level": x[19],
|
||||
"skill_id": x[18],
|
||||
"overdrive": calc_char_value(x[2], x[11], x[14], x[17]),
|
||||
"prog": calc_char_value(x[2], x[10], x[13], x[16]),
|
||||
"frag": calc_char_value(x[2], x[9], x[12], x[15]),
|
||||
"level_exp": Constant.LEVEL_STEPS[x[2]],
|
||||
"exp": x[3],
|
||||
"level": x[2],
|
||||
"name": x[7],
|
||||
"character_id": x[1]
|
||||
}
|
||||
if x[1] == 21:
|
||||
r["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def calc_level_up(c, user_id, character_id, exp, exp_addition):
|
||||
# 计算角色升级,返回当前经验和等级
|
||||
|
||||
exp += exp_addition
|
||||
|
||||
if exp >= Constant.LEVEL_STEPS[20]: # 未觉醒溢出
|
||||
c.execute('''select is_uncapped from user_char where user_id=? and character_id=?''',
|
||||
(user_id, character_id))
|
||||
x = c.fetchone()
|
||||
if x and x[0] == 0:
|
||||
return Constant.LEVEL_STEPS[20], 20
|
||||
|
||||
a = []
|
||||
b = []
|
||||
for i in Constant.LEVEL_STEPS:
|
||||
a.append(i)
|
||||
b.append(Constant.LEVEL_STEPS[i])
|
||||
|
||||
if exp >= b[-1]: # 溢出
|
||||
return b[-1], a[-1]
|
||||
|
||||
if exp < b[0]: # 向下溢出,是异常状态
|
||||
return 0, 1
|
||||
|
||||
i = len(a) - 1
|
||||
while exp < b[i]:
|
||||
i -= 1
|
||||
|
||||
return exp, a[i]
|
||||
|
||||
|
||||
def char_use_core(user_id, character_id, amount):
|
||||
# 以太之滴升级,返回user_id,core状态,角色状态的字典
|
||||
r = None
|
||||
with Connect() as c:
|
||||
|
||||
c.execute(
|
||||
'''select amount from user_item where user_id=? and item_id="core_generic" and type="core"''', (user_id,))
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
pre_amount = x[0]
|
||||
else:
|
||||
pre_amount = 0
|
||||
|
||||
if amount <= pre_amount:
|
||||
c.execute(
|
||||
'''select exp from user_char where user_id=? and character_id=?''', (user_id, character_id))
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
exp, level = calc_level_up(
|
||||
c, user_id, character_id, x[0], amount*Config.CORE_EXP)
|
||||
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
||||
(level, exp, user_id, character_id))
|
||||
server.item.claim_user_item(
|
||||
c, user_id, 'core_generic', 'core', -amount)
|
||||
|
||||
r = {'character': [get_one_character(c, user_id, character_id)]}
|
||||
r['cores'] = server.item.get_user_cores(c, user_id)
|
||||
r['user_id'] = user_id
|
||||
return r
|
||||
|
||||
|
||||
def char_uncap(user_id, character_id):
|
||||
# 角色觉醒,返回user_id,core状态,角色状态的字典
|
||||
r = None
|
||||
with Connect() as c:
|
||||
c.execute('''select * from char_item where character_id=?''',
|
||||
(character_id,))
|
||||
x = c.fetchall()
|
||||
if not x:
|
||||
return None
|
||||
|
||||
success = True
|
||||
for i in x:
|
||||
c.execute(
|
||||
'''select amount from user_item where user_id=? and item_id=? and type=?''', (user_id, i[1], i[2]))
|
||||
y = c.fetchone()
|
||||
if not y or i[3] > y[0]:
|
||||
success = False
|
||||
break
|
||||
|
||||
if success:
|
||||
c.execute('''update user_char set is_uncapped=1, is_uncapped_override=0 where user_id=? and character_id=?''',
|
||||
(user_id, character_id))
|
||||
for i in x:
|
||||
server.item.claim_user_item(c, user_id, i[1], i[2], -i[3])
|
||||
|
||||
r = {'character': [get_one_character(c, user_id, character_id)]}
|
||||
r['cores'] = server.item.get_user_cores(c, user_id)
|
||||
r['user_id'] = user_id
|
||||
return r
|
||||
1140
latest version/server/config.py
Normal file
1140
latest version/server/config.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,12 @@
|
||||
import sqlite3
|
||||
from server.sql import Connect
|
||||
import server.arcworld
|
||||
import server.arcpurchase
|
||||
import server.arcdownload
|
||||
import server.character
|
||||
import server.item
|
||||
import time
|
||||
from setting import Config
|
||||
from .config import Constant
|
||||
|
||||
|
||||
def int2b(x):
|
||||
@@ -44,47 +49,6 @@ def get_recent_score(c, user_id):
|
||||
return []
|
||||
|
||||
|
||||
def get_user_character(c, user_id):
|
||||
# 得到用户拥有的角色列表,返回列表
|
||||
c.execute('''select * from user_char where user_id = :user_id''',
|
||||
{'user_id': user_id})
|
||||
x = c.fetchall()
|
||||
if x != []:
|
||||
s = []
|
||||
for i in x:
|
||||
char_name = ''
|
||||
c.execute(
|
||||
'''select name from character where character_id = :x''', {'x': i[1]})
|
||||
y = c.fetchone()
|
||||
if y is not None:
|
||||
char_name = y[0]
|
||||
char = {
|
||||
"is_uncapped_override": int2b(i[14]),
|
||||
"is_uncapped": int2b(i[13]),
|
||||
"uncap_cores": [],
|
||||
"char_type": i[12],
|
||||
"skill_id_uncap": i[11],
|
||||
"skill_requires_uncap": int2b(i[10]),
|
||||
"skill_unlock_level": i[9],
|
||||
"skill_id": i[8],
|
||||
"overdrive": i[7],
|
||||
"prog": i[6],
|
||||
"frag": i[5],
|
||||
"level_exp": i[4],
|
||||
"exp": i[3],
|
||||
"level": i[2],
|
||||
"name": char_name,
|
||||
"character_id": i[1]
|
||||
}
|
||||
if i[1] == 21:
|
||||
char["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
|
||||
s.append(char)
|
||||
|
||||
return s
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_user_friend(c, user_id):
|
||||
# 得到用户的朋友列表,返回列表
|
||||
c.execute('''select user_id_other from friend where user_id_me = :user_id''', {
|
||||
@@ -104,67 +68,105 @@ def get_user_friend(c, user_id):
|
||||
c.execute('''select * from user where user_id = :x''', {'x': i[0]})
|
||||
y = c.fetchone()
|
||||
if y is not None:
|
||||
character = y[6]
|
||||
is_char_uncapped = int2b(y[8])
|
||||
is_char_uncapped_override = int2b(y[9])
|
||||
if y[23] != -1:
|
||||
character = y[23]
|
||||
if not Config.CHARACTER_FULL_UNLOCK:
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id=:a and character_id=:b''', {
|
||||
'a': i[0], 'b': character})
|
||||
else:
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id=:a and character_id=:b''', {
|
||||
'a': i[0], 'b': character})
|
||||
z = c.fetchone()
|
||||
if z:
|
||||
is_char_uncapped = int2b(z[0])
|
||||
is_char_uncapped_override = int2b(z[1])
|
||||
|
||||
rating = y[5]
|
||||
if int2b(y[10]):
|
||||
rating = -1
|
||||
|
||||
s.append({
|
||||
"is_mutual": is_mutual,
|
||||
"is_char_uncapped_override": int2b(y[9]),
|
||||
"is_char_uncapped": int2b(y[8]),
|
||||
"is_char_uncapped_override": is_char_uncapped_override,
|
||||
"is_char_uncapped": is_char_uncapped,
|
||||
"is_skill_sealed": int2b(y[7]),
|
||||
"rating": y[5],
|
||||
"rating": rating,
|
||||
"join_date": int(y[3]),
|
||||
"character": y[6],
|
||||
"character": character,
|
||||
"recent_score": get_recent_score(c, i[0]),
|
||||
"name": y[1],
|
||||
"user_id": i[0]
|
||||
})
|
||||
|
||||
s.sort(key=lambda item: item["recent_score"][0]["time_played"] if len(
|
||||
item["recent_score"]) > 0 else 0, reverse=True)
|
||||
return s
|
||||
|
||||
|
||||
def get_user_singles(c, user_id):
|
||||
# 得到用户的单曲,返回列表
|
||||
c.execute('''select * from user_item where user_id = :user_id and type = "single"''',
|
||||
{'user_id': user_id})
|
||||
x = c.fetchall()
|
||||
if not x:
|
||||
return []
|
||||
|
||||
re = []
|
||||
for i in x:
|
||||
re.append(i[1])
|
||||
return re
|
||||
|
||||
|
||||
def get_user_packs(c, user_id):
|
||||
# 得到用户的曲包,返回列表
|
||||
c.execute('''select * from user_item where user_id = :user_id and type = "pack"''',
|
||||
{'user_id': user_id})
|
||||
x = c.fetchall()
|
||||
if not x:
|
||||
return []
|
||||
|
||||
re = []
|
||||
for i in x:
|
||||
re.append(i[1])
|
||||
return re
|
||||
|
||||
|
||||
def get_value_0(c, user_id):
|
||||
# 构造value id=0的数据,返回字典
|
||||
def get_user_me(c, user_id):
|
||||
# 构造user/me的数据,返回字典
|
||||
c.execute('''select * from user where user_id = :x''', {'x': user_id})
|
||||
x = c.fetchone()
|
||||
r = {}
|
||||
if x is not None:
|
||||
user_character = get_user_character(c, user_id)
|
||||
user_character = server.character.get_user_character(c, user_id)
|
||||
# 下面没有使用get_user_characters函数是为了节省一次查询
|
||||
characters = []
|
||||
for i in user_character:
|
||||
characters.append(i['character_id'])
|
||||
|
||||
r = {"is_aprilfools": False,
|
||||
"curr_available_maps": [],
|
||||
character_id = x[6]
|
||||
if character_id not in characters:
|
||||
character_id = 0
|
||||
c.execute(
|
||||
'''update user set character_id=0 where user_id=?''', (user_id,))
|
||||
|
||||
favorite_character_id = x[23]
|
||||
if favorite_character_id not in characters: # 这是考虑有可能favourite_character设置了用户未拥有的角色
|
||||
favorite_character_id = -1
|
||||
c.execute(
|
||||
'''update user set favorite_character=-1 where user_id=?''', (user_id,))
|
||||
|
||||
# 计算世界排名
|
||||
c.execute(
|
||||
'''select world_rank_score from user where user_id=?''', (user_id,))
|
||||
y = c.fetchone()
|
||||
if y and y[0]:
|
||||
c.execute(
|
||||
'''select count(*) from user where world_rank_score > ?''', (y[0],))
|
||||
y = c.fetchone()
|
||||
if y and y[0] + 1 <= Config.WORLD_RANK_MAX:
|
||||
world_rank = y[0] + 1
|
||||
else:
|
||||
world_rank = 0
|
||||
else:
|
||||
world_rank = 0
|
||||
|
||||
# 源点强化
|
||||
prog_boost = 0
|
||||
if x[27] and x[27] != 0:
|
||||
prog_boost = 300
|
||||
|
||||
# 体力计算
|
||||
next_fragstam_ts = -1
|
||||
max_stamina_ts = 1586274871917
|
||||
stamina = 12
|
||||
if x[31]:
|
||||
next_fragstam_ts = x[31]
|
||||
if x[32]:
|
||||
max_stamina_ts = x[32]
|
||||
if x[33]:
|
||||
stamina = x[33]
|
||||
|
||||
r = {"is_aprilfools": Config.IS_APRILFOOLS,
|
||||
"curr_available_maps": server.arcworld.get_available_maps(),
|
||||
"character_stats": user_character,
|
||||
"friends": get_user_friend(c, user_id),
|
||||
"settings": {
|
||||
"favorite_character": x[23],
|
||||
"favorite_character": favorite_character_id,
|
||||
"is_hide_rating": int2b(x[10]),
|
||||
"max_stamina_notification_enabled": int2b(x[24])
|
||||
},
|
||||
@@ -173,174 +175,114 @@ def get_value_0(c, user_id):
|
||||
"user_code": x[4],
|
||||
"display_name": x[1],
|
||||
"ticket": x[26],
|
||||
"character": x[6],
|
||||
"character": character_id,
|
||||
"is_locked_name_duplicate": False,
|
||||
"is_skill_sealed": int2b(x[7]),
|
||||
"current_map": x[25],
|
||||
"prog_boost": 0,
|
||||
"next_fragstam_ts": -1,
|
||||
"max_stamina_ts": 1586274871917,
|
||||
"stamina": 12,
|
||||
"world_unlocks": ["scenery_chap1", "scenery_chap2", "scenery_chap3", "scenery_chap4", "scenery_chap5"],
|
||||
"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"],
|
||||
"singles": get_user_singles(c, user_id), # ["dataerror", "yourvoiceso", "crosssoul", "impurebird", "auxesia", "modelista", "yozakurafubuki", "surrender", "metallicpunisher", "carminescythe", "bethere", "callmyname", "fallensquare", "dropdead", "alexandrite", "astraltale", "phantasia", "empireofwinter", "libertas", "dottodot", "dreadnought", "mirzam", "heavenlycaress", "filament", "avantraze", "battlenoone", "saikyostronger", "izana", "einherjar", "laqryma", "amygdata", "altale", "feelssoright", "scarletcage", "teriqma", "mahoroba", "badtek", "maliciousmischance", "buchigireberserker", "galaxyfriends", "xeraphinite", "xanatos"]
|
||||
"packs": get_user_packs(c, user_id),
|
||||
# ["vs", "extend", "dynamix", "prelude", "core", "yugamu", "omatsuri", "zettai", "mirai", "shiawase", "chunithm", "nijuusei", "groovecoaster", "rei", "tonesphere", "lanota"]
|
||||
"prog_boost": prog_boost,
|
||||
"next_fragstam_ts": next_fragstam_ts,
|
||||
"max_stamina_ts": max_stamina_ts,
|
||||
"stamina": server.arcworld.calc_stamina(max_stamina_ts, stamina),
|
||||
"world_unlocks": server.item.get_user_items(c, user_id, 'world_unlock'),
|
||||
"world_songs": server.item.get_user_items(c, user_id, 'world_song'),
|
||||
"singles": server.item.get_user_items(c, user_id, 'single'),
|
||||
"packs": server.item.get_user_items(c, user_id, 'pack'),
|
||||
"characters": characters,
|
||||
"cores": [],
|
||||
"cores": server.item.get_user_cores(c, user_id),
|
||||
"recent_score": get_recent_score(c, user_id),
|
||||
"max_friend": 50,
|
||||
"rating": x[5],
|
||||
"join_date": int(x[3])
|
||||
"join_date": int(x[3]),
|
||||
"global_rank": world_rank
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_user_me_c(user_id):
|
||||
# user/me调用,上边没开数据库这里开一下
|
||||
with Connect() as c:
|
||||
return get_user_me(c, user_id)
|
||||
|
||||
|
||||
def get_purchase_pack(user_id):
|
||||
# 返回曲包数据
|
||||
with Connect() as c:
|
||||
return server.arcpurchase.get_purchase(c, 'pack')
|
||||
|
||||
|
||||
def get_game_info():
|
||||
# 返回游戏基本信息
|
||||
r = {
|
||||
"max_stamina": Constant.MAX_STAMINA,
|
||||
"stamina_recover_tick": Constant.STAMINA_RECOVER_TICK,
|
||||
"core_exp": Constant.CORE_EXP,
|
||||
"curr_ts": int(time.time()*1000),
|
||||
"level_steps": server.character.get_level_steps(),
|
||||
"world_ranking_enabled": True,
|
||||
"is_byd_chapter_unlocked": True
|
||||
}
|
||||
return r
|
||||
|
||||
|
||||
def get_user_present(user_id):
|
||||
# 返回奖励信息
|
||||
with Connect() as c:
|
||||
return server.arcpurchase.get_user_present(c, user_id)
|
||||
|
||||
|
||||
def arc_aggregate_small(user_id):
|
||||
# 返回用户数据
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
r = {"success": True,
|
||||
"value": [{
|
||||
"id": 0,
|
||||
"value": get_value_0(c, user_id)
|
||||
}]}
|
||||
r = {"success": False}
|
||||
with Connect() as c:
|
||||
r = {"success": True,
|
||||
"value": [{
|
||||
"id": 0,
|
||||
"value": get_user_me(c, user_id)
|
||||
}]}
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return r
|
||||
|
||||
|
||||
def arc_aggregate_big(user_id):
|
||||
# 返回用户数据和地图歌曲信息
|
||||
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
r = {"success": True,
|
||||
"value": [{
|
||||
"id": 0,
|
||||
"value": get_value_0(c, user_id)
|
||||
}, {
|
||||
"id": 1,
|
||||
"value": server.arcpurchase.get_item(c, 'pack')
|
||||
}, {
|
||||
"id": 2,
|
||||
"value": {}
|
||||
}, {
|
||||
"id": 3,
|
||||
"value": {
|
||||
"max_stamina": 12,
|
||||
"stamina_recover_tick": 1800000,
|
||||
"core_exp": 250,
|
||||
"curr_ts": int(time.time())*1000,
|
||||
"level_steps": [{
|
||||
"level": 1,
|
||||
"level_exp": 0
|
||||
}, {
|
||||
"level": 2,
|
||||
"level_exp": 50
|
||||
}, {
|
||||
"level": 3,
|
||||
"level_exp": 100
|
||||
}, {
|
||||
"level": 4,
|
||||
"level_exp": 150
|
||||
}, {
|
||||
"level": 5,
|
||||
"level_exp": 200
|
||||
}, {
|
||||
"level": 6,
|
||||
"level_exp": 300
|
||||
}, {
|
||||
"level": 7,
|
||||
"level_exp": 450
|
||||
}, {
|
||||
"level": 8,
|
||||
"level_exp": 650
|
||||
}, {
|
||||
"level": 9,
|
||||
"level_exp": 900
|
||||
}, {
|
||||
"level": 10,
|
||||
"level_exp": 1200
|
||||
}, {
|
||||
"level": 11,
|
||||
"level_exp": 1600
|
||||
}, {
|
||||
"level": 12,
|
||||
"level_exp": 2100
|
||||
}, {
|
||||
"level": 13,
|
||||
"level_exp": 2700
|
||||
}, {
|
||||
"level": 14,
|
||||
"level_exp": 3400
|
||||
}, {
|
||||
"level": 15,
|
||||
"level_exp": 4200
|
||||
}, {
|
||||
"level": 16,
|
||||
"level_exp": 5100
|
||||
}, {
|
||||
"level": 17,
|
||||
"level_exp": 6100
|
||||
}, {
|
||||
"level": 18,
|
||||
"level_exp": 7200
|
||||
}, {
|
||||
"level": 19,
|
||||
"level_exp": 8500
|
||||
}, {
|
||||
"level": 20,
|
||||
"level_exp": 10000
|
||||
}, {
|
||||
"level": 21,
|
||||
"level_exp": 11500
|
||||
}, {
|
||||
"level": 22,
|
||||
"level_exp": 13000
|
||||
}, {
|
||||
"level": 23,
|
||||
"level_exp": 14500
|
||||
}, {
|
||||
"level": 24,
|
||||
"level_exp": 16000
|
||||
}, {
|
||||
"level": 25,
|
||||
"level_exp": 17500
|
||||
}, {
|
||||
"level": 26,
|
||||
"level_exp": 19000
|
||||
}, {
|
||||
"level": 27,
|
||||
"level_exp": 20500
|
||||
}, {
|
||||
"level": 28,
|
||||
"level_exp": 22000
|
||||
}, {
|
||||
"level": 29,
|
||||
"level_exp": 23500
|
||||
}, {
|
||||
"level": 30,
|
||||
"level_exp": 25000
|
||||
}],
|
||||
"world_ranking_enabled": False,
|
||||
"is_byd_chapter_unlocked": True
|
||||
# 返回比较全的用户数据
|
||||
r = {"success": False}
|
||||
with Connect() as c:
|
||||
# 防止数据库锁
|
||||
id_2 = server.arcdownload.get_all_songs(user_id, url_flag=False)
|
||||
id_5 = {
|
||||
"current_map": server.arcworld.get_current_map(user_id),
|
||||
"user_id": user_id,
|
||||
"maps": server.arcworld.get_world_all(user_id)
|
||||
}
|
||||
r = {"success": True,
|
||||
"value": [{
|
||||
"id": 0,
|
||||
"value": get_user_me(c, user_id)
|
||||
}, {
|
||||
"id": 1,
|
||||
"value": server.arcpurchase.get_purchase(c, 'pack')
|
||||
}, {
|
||||
"id": 2,
|
||||
"value": id_2
|
||||
}, {
|
||||
"id": 3,
|
||||
"value": {
|
||||
"max_stamina": Constant.MAX_STAMINA,
|
||||
"stamina_recover_tick": Constant.STAMINA_RECOVER_TICK,
|
||||
"core_exp": Constant.CORE_EXP,
|
||||
"curr_ts": int(time.time()*1000),
|
||||
"level_steps": server.character.get_level_steps(),
|
||||
"world_ranking_enabled": True,
|
||||
"is_byd_chapter_unlocked": True
|
||||
}
|
||||
}, {
|
||||
"id": 4,
|
||||
"value": server.arcpurchase.get_user_present(c, user_id)
|
||||
}, {
|
||||
"id": 5,
|
||||
"value": id_5
|
||||
}
|
||||
}, {
|
||||
"id": 4,
|
||||
"value": server.arcpurchase.get_user_present(c, user_id)
|
||||
}, {
|
||||
"id": 5,
|
||||
"value": {
|
||||
"current_map": server.arcworld.get_current_map(user_id),
|
||||
"user_id": user_id,
|
||||
"maps": server.arcworld.get_world_all(user_id)
|
||||
}
|
||||
}
|
||||
]}
|
||||
]}
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return r
|
||||
|
||||
98
latest version/server/init.py
Normal file
98
latest version/server/init.py
Normal file
@@ -0,0 +1,98 @@
|
||||
import os
|
||||
from shutil import copy, copy2
|
||||
from server.sql import Connect
|
||||
from database.database_initialize import main, ARCAEA_SERVER_VERSION
|
||||
from web.system import update_database
|
||||
|
||||
|
||||
def try_rename(path, new_path):
|
||||
# 尝试重命名文件,并尝试避免命名冲突,返回最终路径
|
||||
final_path = new_path
|
||||
if os.path.exists(new_path):
|
||||
i = 1
|
||||
while os.path.exists(new_path + str(i)):
|
||||
i += 1
|
||||
os.rename(path, new_path + str(i))
|
||||
final_path = new_path + str(i)
|
||||
else:
|
||||
os.rename(path, new_path)
|
||||
|
||||
return final_path
|
||||
|
||||
|
||||
def check_before_run(app):
|
||||
# 运行前检查关键文件,返回布尔值,其实是因为有人经常忘了
|
||||
|
||||
f = True
|
||||
|
||||
if not os.path.exists('setting.py'):
|
||||
app.logger.warning('File `setting.py` is missing.')
|
||||
f = False
|
||||
|
||||
if not os.path.exists('database'):
|
||||
app.logger.warning('Folder `database` is missing.')
|
||||
f = False
|
||||
|
||||
if not os.path.exists('database/songs'):
|
||||
app.logger.warning('Folder `database/songs` is missing.')
|
||||
f = False
|
||||
|
||||
if not os.path.exists('database/arcaea_database.db'):
|
||||
app.logger.warning('File `database/arcaea_database.db` is missing.')
|
||||
f = False
|
||||
try:
|
||||
app.logger.info(
|
||||
'Try to new the file `database/arcaea_database.db`.')
|
||||
main('./database/')
|
||||
app.logger.info(
|
||||
'Success to new the file `database/arcaea_database.db`.')
|
||||
f = True
|
||||
except:
|
||||
app.logger.warning(
|
||||
'Fail to new the file `database/arcaea_database.db`.')
|
||||
|
||||
else:
|
||||
with Connect() as c:
|
||||
try:
|
||||
c.execute('''select value from config where id="version"''')
|
||||
x = c.fetchone()
|
||||
except:
|
||||
x = None
|
||||
|
||||
# 数据库自动更新,不强求
|
||||
if not x or x[0] != ARCAEA_SERVER_VERSION:
|
||||
app.logger.warning(
|
||||
'Maybe the file `database/arcaea_database.db` is an old version.')
|
||||
try:
|
||||
app.logger.info(
|
||||
'Try to update the file `database/arcaea_database.db`.')
|
||||
|
||||
path = try_rename('database/arcaea_database.db',
|
||||
'database/arcaea_database.db.bak')
|
||||
|
||||
try:
|
||||
copy2(path, 'database/arcaea_database.db')
|
||||
except:
|
||||
copy(path, 'database/arcaea_database.db')
|
||||
|
||||
if os.path.isfile("database/old_arcaea_database.db"):
|
||||
os.remove('database/old_arcaea_database.db')
|
||||
|
||||
try_rename('database/arcaea_database.db',
|
||||
'database/old_arcaea_database.db')
|
||||
|
||||
main('./database/')
|
||||
update_database()
|
||||
|
||||
app.logger.info(
|
||||
'Success to update the file `database/arcaea_database.db`.')
|
||||
|
||||
except:
|
||||
app.logger.warning(
|
||||
'Fail to update the file `database/arcaea_database.db`.')
|
||||
|
||||
if not os.path.exists('database/arcsong.db'):
|
||||
app.logger.warning('File `database/arcsong.db` is missing.')
|
||||
f = False
|
||||
|
||||
return f
|
||||
106
latest version/server/item.py
Normal file
106
latest version/server/item.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from setting import Config
|
||||
|
||||
|
||||
def get_user_items(c, user_id, item_type):
|
||||
# 得到用户的物品,返回列表,不包含数量信息
|
||||
|
||||
if item_type == 'world_song' and Config.WORLD_SONG_FULL_UNLOCK or item_type == 'world_unlock' and Config.WORLD_SCENERY_FULL_UNLOCK:
|
||||
c.execute('''select item_id from item where type=?''', (item_type,))
|
||||
else:
|
||||
c.execute('''select item_id from user_item where user_id = :user_id and type = :item_type''',
|
||||
{'user_id': user_id, 'item_type': item_type})
|
||||
x = c.fetchall()
|
||||
if not x:
|
||||
return []
|
||||
|
||||
re = []
|
||||
for i in x:
|
||||
re.append(i[0])
|
||||
return re
|
||||
|
||||
|
||||
def get_user_cores(c, user_id):
|
||||
# 得到用户的core,返回字典列表
|
||||
r = []
|
||||
c.execute(
|
||||
'''select item_id, amount from user_item where user_id = ? and type="core"''', (user_id,))
|
||||
x = c.fetchall()
|
||||
if x:
|
||||
for i in x:
|
||||
if i[1]:
|
||||
amount = i[1]
|
||||
else:
|
||||
amount = 0
|
||||
r.append({'core_type': i[0], 'amount': amount})
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def claim_user_item(c, user_id, item_id, item_type, amount=1):
|
||||
# 处理用户物品,包括添加和删除操作,返回成功与否布尔值
|
||||
# 这个接口允许memory变成负数,所以不能用来购买
|
||||
|
||||
try:
|
||||
amount = int(amount)
|
||||
except:
|
||||
return False
|
||||
|
||||
if item_type not in ['memory', 'character']:
|
||||
c.execute('''select is_available from item where item_id=? and type=?''',
|
||||
(item_id, item_type))
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
if x[0] == 0:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
if item_type == 'core':
|
||||
c.execute(
|
||||
'''select amount from user_item where user_id=? and item_id=? and type="core"''', (user_id, item_id))
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
if x[0] + amount < 0: # 数量不足
|
||||
return False
|
||||
c.execute('''update user_item set amount=? where user_id=? and item_id=? and type="core"''',
|
||||
(x[0]+amount, user_id, item_id))
|
||||
else:
|
||||
if amount < 0: # 添加数量错误
|
||||
return False
|
||||
c.execute('''insert into user_item values(?,?,"core",?)''',
|
||||
(user_id, item_id, amount))
|
||||
|
||||
elif item_type == 'memory':
|
||||
c.execute('''select ticket from user where user_id=?''', (user_id,))
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
c.execute('''update user set ticket=? where user_id=?''',
|
||||
(x[0]+amount, user_id))
|
||||
else:
|
||||
return False
|
||||
|
||||
elif item_type == 'character':
|
||||
if not item_id.isdigit():
|
||||
c.execute(
|
||||
'''select character_id from character where name=?''', (item_id,))
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
character_id = x[0]
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
character_id = int(item_id)
|
||||
c.execute(
|
||||
'''select exists(select * from user_char where user_id=? and character_id=?)''', (user_id, character_id))
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into user_char values(?,?,1,0,0,0)''',
|
||||
(user_id, character_id))
|
||||
|
||||
elif item_type in ['world_song', 'world_unlock', 'single', 'pack']:
|
||||
c.execute('''select exists(select * from user_item where user_id=? and item_id=? and type=?)''',
|
||||
(user_id, item_id, item_type))
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into user_item values(:a,:b,:c,1)''',
|
||||
{'a': user_id, 'b': item_id, 'c': item_type})
|
||||
|
||||
return True
|
||||
@@ -1,5 +1,8 @@
|
||||
import sqlite3
|
||||
from server.sql import Connect
|
||||
from setting import Config
|
||||
from .config import Constant
|
||||
import server.info
|
||||
import server.character
|
||||
|
||||
|
||||
def b2int(x):
|
||||
@@ -20,98 +23,105 @@ def int2b(x):
|
||||
|
||||
def change_char(user_id, character_id, skill_sealed):
|
||||
# 角色改变,包括技能封印的改变,返回成功与否的布尔值
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
|
||||
{'a': user_id, 'b': character_id})
|
||||
x = c.fetchone()
|
||||
if x is not None:
|
||||
re = False
|
||||
|
||||
with Connect() as c:
|
||||
if Config.CHARACTER_FULL_UNLOCK:
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
|
||||
{'a': user_id, 'b': character_id})
|
||||
else:
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
|
||||
{'a': user_id, 'b': character_id})
|
||||
x = c.fetchone()
|
||||
if x:
|
||||
is_uncapped = x[0]
|
||||
is_uncapped_override = x[1]
|
||||
else:
|
||||
return False
|
||||
|
||||
if skill_sealed == 'false':
|
||||
skill_sealed = False
|
||||
else:
|
||||
skill_sealed = True
|
||||
c.execute('''update user set is_skill_sealed = :a, character_id = :b, is_char_uncapped = :c, is_char_uncapped_override = :d where user_id = :e''', {
|
||||
'a': b2int(skill_sealed), 'b': character_id, 'c': x[0], 'd': x[1], 'e': user_id})
|
||||
'a': b2int(skill_sealed), 'b': character_id, 'c': is_uncapped, 'd': is_uncapped_override, 'e': user_id})
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return True
|
||||
re = True
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return False
|
||||
return re
|
||||
|
||||
|
||||
def change_char_uncap(user_id, character_id):
|
||||
# 角色觉醒改变,返回字典
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
|
||||
{'a': user_id, 'b': character_id})
|
||||
x = c.fetchone()
|
||||
r = None
|
||||
if x is not None and x[0] == 1:
|
||||
c.execute('''update user set is_char_uncapped_override = :a where user_id = :b''', {
|
||||
'a': b2int(x[1] == 0), 'b': user_id})
|
||||
c.execute('''update user_char set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
|
||||
'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id})
|
||||
c.execute('''select * from user_char where user_id = :a and character_id = :b''',
|
||||
{'a': user_id, 'b': character_id})
|
||||
y = c.fetchone()
|
||||
c.execute(
|
||||
'''select name from character where character_id = :x''', {'x': y[1]})
|
||||
z = c.fetchone()
|
||||
if z is not None:
|
||||
char_name = z[0]
|
||||
if y is not None:
|
||||
r = {
|
||||
"is_uncapped_override": int2b(y[14]),
|
||||
"is_uncapped": int2b(y[13]),
|
||||
"uncap_cores": [],
|
||||
"char_type": y[12],
|
||||
"skill_id_uncap": y[11],
|
||||
"skill_requires_uncap": int2b(y[10]),
|
||||
"skill_unlock_level": y[9],
|
||||
"skill_id": y[8],
|
||||
"overdrive": y[7],
|
||||
"prog": y[6],
|
||||
"frag": y[5],
|
||||
"level_exp": y[4],
|
||||
"exp": y[3],
|
||||
"level": y[2],
|
||||
"name": char_name,
|
||||
"character_id": y[1]
|
||||
}
|
||||
with Connect() as c:
|
||||
if not Config.CHARACTER_FULL_UNLOCK:
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char where user_id = :a and character_id = :b''',
|
||||
{'a': user_id, 'b': character_id})
|
||||
else:
|
||||
c.execute('''select is_uncapped, is_uncapped_override from user_char_full where user_id = :a and character_id = :b''',
|
||||
{'a': user_id, 'b': character_id})
|
||||
x = c.fetchone()
|
||||
|
||||
if x is not None and x[0] == 1:
|
||||
c.execute('''update user set is_char_uncapped_override = :a where user_id = :b''', {
|
||||
'a': b2int(x[1] == 0), 'b': user_id})
|
||||
|
||||
if not Config.CHARACTER_FULL_UNLOCK:
|
||||
c.execute('''update user_char set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
|
||||
'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id})
|
||||
c.execute(
|
||||
'''select * from user_char a,character b where a.user_id=? and a.character_id=b.character_id and a.character_id=?''', (user_id, character_id))
|
||||
else:
|
||||
c.execute('''update user_char_full set is_uncapped_override = :a where user_id = :b and character_id = :c''', {
|
||||
'a': b2int(x[1] == 0), 'b': user_id, 'c': character_id})
|
||||
c.execute(
|
||||
'''select * from user_char_full a,character b where a.user_id=? and a.character_id=b.character_id and a.character_id=?''', (user_id, character_id))
|
||||
y = c.fetchone()
|
||||
if y is not None:
|
||||
r = {
|
||||
"is_uncapped_override": int2b(y[5]),
|
||||
"is_uncapped": int2b(y[4]),
|
||||
"uncap_cores": server.character.get_char_core(c, y[1]),
|
||||
"char_type": y[22],
|
||||
"skill_id_uncap": y[21],
|
||||
"skill_requires_uncap": int2b(y[20]),
|
||||
"skill_unlock_level": y[19],
|
||||
"skill_id": y[18],
|
||||
"overdrive": server.character.calc_char_value(y[2], y[11], y[14], y[17]),
|
||||
"prog": server.character.calc_char_value(y[2], y[10], y[13], y[16]),
|
||||
"frag": server.character.calc_char_value(y[2], y[9], y[12], y[15]),
|
||||
"level_exp": Constant.LEVEL_STEPS[y[2]],
|
||||
"exp": y[3],
|
||||
"level": y[2],
|
||||
"name": y[7],
|
||||
"character_id": y[1]
|
||||
}
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return r
|
||||
|
||||
|
||||
def arc_sys_set(user_id, value, set_arg):
|
||||
# 三个设置,PTT隐藏、体力满通知、最爱角色,无返回
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
if 'favorite_character' in set_arg:
|
||||
value = int(value)
|
||||
c.execute('''update user set favorite_character = :a where user_id = :b''', {
|
||||
'a': value, 'b': user_id})
|
||||
with Connect() as c:
|
||||
if 'favorite_character' in set_arg:
|
||||
value = int(value)
|
||||
c.execute('''update user set favorite_character = :a where user_id = :b''', {
|
||||
'a': value, 'b': user_id})
|
||||
|
||||
else:
|
||||
if value == 'false':
|
||||
value = False
|
||||
else:
|
||||
value = True
|
||||
if value == 'false':
|
||||
value = False
|
||||
else:
|
||||
value = True
|
||||
|
||||
if 'is_hide_rating' in set_arg:
|
||||
c.execute('''update user set is_hide_rating = :a where user_id = :b''', {
|
||||
'a': b2int(value), 'b': user_id})
|
||||
if 'max_stamina_notification_enabled' in set_arg:
|
||||
c.execute('''update user set max_stamina_notification_enabled = :a where user_id = :b''', {
|
||||
'a': b2int(value), 'b': user_id})
|
||||
if 'is_hide_rating' in set_arg:
|
||||
c.execute('''update user set is_hide_rating = :a where user_id = :b''', {
|
||||
'a': b2int(value), 'b': user_id})
|
||||
if 'max_stamina_notification_enabled' in set_arg:
|
||||
c.execute('''update user set max_stamina_notification_enabled = :a where user_id = :b''', {
|
||||
'a': b2int(value), 'b': user_id})
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
|
||||
@@ -119,37 +129,32 @@ def arc_add_friend(user_id, friend_id):
|
||||
# 加好友,返回好友列表,或者是错误码602、604
|
||||
if user_id == friend_id:
|
||||
return 604
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
|
||||
{'x': user_id, 'y': friend_id})
|
||||
|
||||
r = None
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into friend values(:a, :b)''',
|
||||
{'a': user_id, 'b': friend_id})
|
||||
r = server.info.get_user_friend(c, user_id)
|
||||
with Connect() as c:
|
||||
c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
|
||||
{'x': user_id, 'y': friend_id})
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into friend values(:a, :b)''',
|
||||
{'a': user_id, 'b': friend_id})
|
||||
r = server.info.get_user_friend(c, user_id)
|
||||
else:
|
||||
r = 602
|
||||
|
||||
else:
|
||||
return 602
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return r
|
||||
|
||||
|
||||
def arc_delete_friend(user_id, friend_id):
|
||||
# 删好友,返回好友列表
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
|
||||
{'x': user_id, 'y': friend_id})
|
||||
r = None
|
||||
if c.fetchone() == (1,):
|
||||
c.execute('''delete from friend where user_id_me = :x and user_id_other = :y''',
|
||||
with Connect() as c:
|
||||
|
||||
c.execute('''select exists(select * from friend where user_id_me = :x and user_id_other = :y)''',
|
||||
{'x': user_id, 'y': friend_id})
|
||||
if c.fetchone() == (1,):
|
||||
c.execute('''delete from friend where user_id_me = :x and user_id_other = :y''',
|
||||
{'x': user_id, 'y': friend_id})
|
||||
|
||||
r = server.info.get_user_friend(c, user_id)
|
||||
r = server.info.get_user_friend(c, user_id)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return r
|
||||
|
||||
83
latest version/server/sql.py
Normal file
83
latest version/server/sql.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import sqlite3
|
||||
from flask import current_app
|
||||
import traceback
|
||||
|
||||
|
||||
class Connect():
|
||||
# 数据库连接类,上下文管理
|
||||
|
||||
def __init__(self, file_path='./database/arcaea_database.db'):
|
||||
"""
|
||||
数据库连接,默认连接arcaea_database.db
|
||||
接受:文件路径
|
||||
返回:sqlite3连接操作对象
|
||||
"""
|
||||
self.file_path = file_path
|
||||
|
||||
def __enter__(self):
|
||||
self.conn = sqlite3.connect(self.file_path)
|
||||
self.c = self.conn.cursor()
|
||||
return self.c
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self.conn:
|
||||
self.conn.commit()
|
||||
self.conn.close()
|
||||
|
||||
if exc_type is not None:
|
||||
current_app.logger.error(
|
||||
traceback.format_exception(exc_type, exc_val, exc_tb))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class Sql():
|
||||
|
||||
@staticmethod
|
||||
def select(c, table_name, target_column=[], query=None):
|
||||
# 执行查询单句sql语句,返回fetchall数据
|
||||
# 使用准确查询,且在单表内
|
||||
|
||||
sql = 'select '
|
||||
sql_dict = {}
|
||||
if len(target_column) >= 2:
|
||||
sql += target_column[0]
|
||||
for i in range(1, len(target_column)):
|
||||
sql += ',' + target_column[i]
|
||||
sql += ' from ' + table_name
|
||||
elif len(target_column) == 1:
|
||||
sql += target_column[0] + ' from ' + table_name
|
||||
else:
|
||||
sql += '* from ' + table_name
|
||||
|
||||
where_field = []
|
||||
where_value = []
|
||||
if query:
|
||||
for i in query.query:
|
||||
where_field.append(i)
|
||||
where_value.append(query.query[i])
|
||||
|
||||
if where_field and where_value:
|
||||
sql += ' where '
|
||||
sql += where_field[0] + '=:' + where_field[0]
|
||||
sql_dict[where_field[0]] = where_value[0]
|
||||
if len(where_field) >= 2:
|
||||
for i in range(1, len(where_field)):
|
||||
sql_dict[where_field[i]] = where_value[i]
|
||||
sql += ' and ' + where_field[i] + '=:' + where_field[i]
|
||||
|
||||
if query and query.sort:
|
||||
sql += ' order by ' + \
|
||||
query.sort[0]['column'] + ' ' + query.sort[0]['order']
|
||||
for i in range(1, len(query.sort)):
|
||||
sql += ', ' + query.sort[i]['column'] + \
|
||||
' ' + query.sort[i]['order']
|
||||
|
||||
if query and query.limit >= 0:
|
||||
sql += ' limit :limit offset :offset'
|
||||
sql_dict['limit'] = query.limit
|
||||
sql_dict['offset'] = query.offset
|
||||
|
||||
c.execute(sql, sql_dict)
|
||||
|
||||
return c.fetchall()
|
||||
@@ -1,7 +0,0 @@
|
||||
[CONFIG]
|
||||
HOST = 192.168.1.113
|
||||
PORT = 80
|
||||
|
||||
[WEB]
|
||||
USERNAME = admin
|
||||
PASSWORD = admin
|
||||
264
latest version/setting.py
Normal file
264
latest version/setting.py
Normal file
@@ -0,0 +1,264 @@
|
||||
class Config():
|
||||
'''
|
||||
This is the setting file. You can change some parameters here.
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
主机的地址和端口号
|
||||
Host and port of your server
|
||||
'''
|
||||
HOST = '0.0.0.0'
|
||||
PORT = '80'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
游戏API地址前缀
|
||||
Game API's URL prefix
|
||||
'''
|
||||
GAME_API_PREFIX = '/bridge/18'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
允许使用的游戏版本,若为空,则默认全部允许
|
||||
Allowed game versions
|
||||
If it is blank, all are allowed.
|
||||
'''
|
||||
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.12.0', '3.12.0c']
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
联机功能的端口号,若为空,则默认不开启联机功能
|
||||
Port of your link play server
|
||||
If it is blank, link play will be unavailable.
|
||||
'''
|
||||
UDP_PORT = '10900'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
联机功能地址,留空则自动获取
|
||||
Link Play address
|
||||
If left blank, it will be obtained automatically.
|
||||
'''
|
||||
LINK_PLAY_HOST = '' # ***.com
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
SSL证书路径
|
||||
留空则使用HTTP
|
||||
SSL certificate path
|
||||
If left blank, use HTTP.
|
||||
'''
|
||||
SSL_CERT = '' # *.pem
|
||||
SSL_KEY = '' # *.key
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
愚人节模式开关
|
||||
Switch of April Fool's Day
|
||||
'''
|
||||
IS_APRILFOOLS = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
世界排名的最大显示数量
|
||||
The largest number of global rank
|
||||
'''
|
||||
WORLD_RANK_MAX = 200
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
世界模式当前活动图设置
|
||||
Current available maps in world mode
|
||||
'''
|
||||
AVAILABLE_MAP = [] # Ex. ['test', 'test2']
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
Web后台管理页面的用户名和密码
|
||||
Username and password of web background management page
|
||||
'''
|
||||
USERNAME = 'admin'
|
||||
PASSWORD = 'admin'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
Web后台管理页面的session秘钥,如果不知道是什么,请不要修改
|
||||
Session key of web background management page
|
||||
If you don't know what it is, please don't modify it.
|
||||
'''
|
||||
SECRET_KEY = '1145141919810'
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
API接口完全控制权限Token,留空则不使用
|
||||
API interface full control permission Token
|
||||
If you don't want to use it, leave it blank.
|
||||
'''
|
||||
API_TOKEN = ''
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
歌曲下载地址前缀,留空则自动获取
|
||||
Song download address prefix
|
||||
If left blank, it will be obtained automatically.
|
||||
'''
|
||||
DOWNLOAD_LINK_PREFIX = '' # http://***.com/download/
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
玩家歌曲下载的24小时次数限制,每个文件算一次
|
||||
Player's song download limit times in 24 hours, once per file
|
||||
'''
|
||||
DOWNLOAD_TIMES_LIMIT = 3000
|
||||
'''
|
||||
歌曲下载链接的有效时长,单位:秒
|
||||
Effective duration of song download link, unit: seconds
|
||||
'''
|
||||
DOWNLOAD_TIME_GAP_LIMIT = 1000
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
Arcaea登录的最大允许设备数量,最小值为1
|
||||
The maximum number of devices allowed to log in Arcaea, minimum: 1
|
||||
'''
|
||||
LOGIN_DEVICE_NUMBER_LIMIT = 1
|
||||
'''
|
||||
是否允许同设备多应用共存登录
|
||||
请注意,这个选项设置为True时,下一个选项将自动变为False
|
||||
If logging in from multiple applications on the same device is allowed
|
||||
Note that when this option is set to True, the next option automatically becomes False
|
||||
'''
|
||||
ALLOW_LOGIN_SAME_DEVICE = False
|
||||
'''
|
||||
24小时内登陆设备数超过最大允许设备数量时,是否自动封号(1天、3天、7天、15天、31天)
|
||||
When the number of login devices exceeds the maximum number of devices allowed to log in Arcaea within 24 hours, whether the account will be automatically banned (1 day, 3 days, 7 days, 15 days, 31 days)
|
||||
'''
|
||||
ALLOW_BAN_MULTIDEVICE_USER_AUTO = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否记录详细的服务器日志
|
||||
If recording detailed server logs is enabled
|
||||
'''
|
||||
ALLOW_LOG_INFO = False
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
用户注册时的默认记忆源点数量
|
||||
The default amount of memories at the time of user registration
|
||||
'''
|
||||
DEFAULT_MEMORIES = 0
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
数据库更新时,是否采用最新的角色数据,如果你想采用最新的官方角色数据
|
||||
注意:如果是,旧的数据将丢失;如果否,某些角色的数据变动将无法同步
|
||||
If using the latest character data when updating database. If you want to only keep newest official character data, please set it `True`.
|
||||
Note: If `True`, the old data will be lost; If `False`, the data changes of some characters will not be synchronized.
|
||||
'''
|
||||
UPDATE_WITH_NEW_CHARACTER_DATA = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否全解锁搭档
|
||||
If unlocking all partners is enabled
|
||||
'''
|
||||
CHARACTER_FULL_UNLOCK = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否全解锁世界歌曲
|
||||
If unlocking all world songs is enabled
|
||||
'''
|
||||
WORLD_SONG_FULL_UNLOCK = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否全解锁世界场景
|
||||
If unlocking all world sceneries is enabled
|
||||
'''
|
||||
WORLD_SCENERY_FULL_UNLOCK = True
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否强制使用全解锁云端存档
|
||||
If forcing full unlocked cloud save is enabled
|
||||
'''
|
||||
SAVE_FULL_UNLOCK = False
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
|
||||
'''
|
||||
--------------------
|
||||
是否使用最好的 10 条记录(而不是最近的 30 条记录中较好的 10 条)来计算 PTT
|
||||
Calculate PTT with best 10 instead of recent best 10
|
||||
'''
|
||||
USE_B10_AS_R10 = False
|
||||
'''
|
||||
--------------------
|
||||
'''
|
||||
@@ -20,4 +20,4 @@
|
||||
{% endfor %} {% block content %}{% endblock %}
|
||||
</section>
|
||||
|
||||
<footer id="footer" class="footer">Made by Lost@2020</footer>
|
||||
<footer id="footer" class="footer">Made by Lost@2020-2022</footer>
|
||||
@@ -16,26 +16,54 @@
|
||||
<span class="char-name">{{char['name']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Level: </span>
|
||||
<span class="char-num">{{char['level']}}</span>
|
||||
<span>Max level: </span>
|
||||
<span class="char-num">{{char['max_level']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Frag: </span>
|
||||
<span class="char-num">{{char['frag']}}</span>
|
||||
<span>Frag-level 1: </span>
|
||||
<span class="char-num">{{char['frag1']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Prog: </span>
|
||||
<span class="char-num">{{char['prog']}}</span>
|
||||
<span>Prog-level 1: </span>
|
||||
<span class="char-num">{{char['prog1']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Overdrive: </span>
|
||||
<span class="char-num">{{char['overdrive']}}</span>
|
||||
<span>Overdrive-level 1: </span>
|
||||
<span class="char-num">{{char['overdrive1']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Frag-level 20: </span>
|
||||
<span class="char-num">{{char['frag20']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Prog-level 20: </span>
|
||||
<span class="char-num">{{char['prog20']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Overdrive-level 20: </span>
|
||||
<span class="char-num">{{char['overdrive20']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Frag-level 30: </span>
|
||||
<span class="char-num">{{char['frag30']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Prog-level 30: </span>
|
||||
<span class="char-num">{{char['prog30']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Overdrive-level 30: </span>
|
||||
<span class="char-num">{{char['overdrive30']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Skill id: </span>
|
||||
<span class="char-num">{{char['skill_id']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Skill unlock level: </span>
|
||||
<span class="char-num">{{char['skill_unlock_level']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Uncapped skill id: </span>
|
||||
<span class="char-num">{{char['skill_id_uncap']}}</span>
|
||||
<br />
|
||||
|
||||
@@ -20,24 +20,6 @@
|
||||
<span class="char-num">{{item['is_available']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Price: </span>
|
||||
<span class="char-num">{{item['price']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Original price: </span>
|
||||
<span class="char-num">{{item['orig_price']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Discount from: </span>
|
||||
<span class="char-num">{{item['discount_from']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Discount to: </span>
|
||||
<span class="char-num">{{item['discount_to']}}</span>
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
{% if not loop.last %}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
<div class="name">{{user['name']}}
|
||||
<span class="rank">UID: {{user['user_id']}}</span>
|
||||
<span class="rank">User code: {{user['user_code']}}</span>
|
||||
{% if user['ban_flag'] %}
|
||||
<span class="rank">Banned</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="join-date">注册于 Registered in: {{user['join_date']}}</div>
|
||||
<div class="ptt">PTT: {{'%0.2f'|format(user['rating_ptt']/100|float)}}</div>
|
||||
|
||||
55
latest version/templates/web/allpresent.html
Normal file
55
latest version/templates/web/allpresent.html
Normal file
@@ -0,0 +1,55 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}All presents{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if posts %}<br />
|
||||
{% for present in posts %}
|
||||
|
||||
<div class="char-present">
|
||||
<span>Id: </span>
|
||||
<span class="name">{{present['present_id']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Expire time: </span>
|
||||
<span class="char-num">{{present['expire_ts']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Description: </span>
|
||||
<span class="char-num">{{present['description']}}</span>
|
||||
<br />
|
||||
|
||||
{% if present['items'] %}<br />
|
||||
{% for x in present['items'] %}
|
||||
<span>Item id: </span>
|
||||
<span class="char-num">{{x['item_id']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Item type: </span>
|
||||
<span class="char-num">{{x['type']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Amount: </span>
|
||||
<span class="char-num">{{x['amount']}}</span>
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
{% if not loop.last %}
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
</div>
|
||||
{% if not loop.last %}
|
||||
<br />
|
||||
<hr />
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
61
latest version/templates/web/allpurchase.html
Normal file
61
latest version/templates/web/allpurchase.html
Normal file
@@ -0,0 +1,61 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}All purchases{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if posts %}<br />
|
||||
{% for item in posts %}
|
||||
|
||||
<div class="char-item">
|
||||
<span>Name: </span>
|
||||
<span class="name">{{item['purchase_name']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Price: </span>
|
||||
<span class="char-num">{{item['price']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Original price: </span>
|
||||
<span class="char-num">{{item['orig_price']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Discount from: </span>
|
||||
<span class="char-num">{{item['discount_from']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Discount to: </span>
|
||||
<span class="char-num">{{item['discount_to']}}</span>
|
||||
<br />
|
||||
{% if item['items'] %}<br />
|
||||
{% for x in item['items'] %}
|
||||
<span>Item id: </span>
|
||||
<span class="char-num">{{x['item_id']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Item type: </span>
|
||||
<span class="char-num">{{x['type']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Amount: </span>
|
||||
<span class="char-num">{{x['amount']}}</span>
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
{% if not loop.last %}
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% if not loop.last %}
|
||||
<br />
|
||||
<hr />
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
58
latest version/templates/web/allredeem.html
Normal file
58
latest version/templates/web/allredeem.html
Normal file
@@ -0,0 +1,58 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}All redeem codes{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if posts %}<br />
|
||||
{% for redeem in posts %}
|
||||
|
||||
<div class="char-redeem">
|
||||
<span>Code: </span>
|
||||
<span class="name">{{redeem['code']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Type: </span>
|
||||
<span class="char-num">
|
||||
{% if redeem['type'] == 0 %}
|
||||
仅可使用一次 It can only be used once.
|
||||
{% elif redeem['type'] == 1 %}
|
||||
每个用户仅可以使用一次 Each user can only use it once.
|
||||
{% endif %}
|
||||
</span>
|
||||
<br />
|
||||
|
||||
{% if redeem['items'] %}<br />
|
||||
{% for x in redeem['items'] %}
|
||||
<span>Item id: </span>
|
||||
<span class="char-num">{{x['item_id']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Item type: </span>
|
||||
<span class="char-num">{{x['type']}}</span>
|
||||
<br />
|
||||
|
||||
<span>Amount: </span>
|
||||
<span class="char-num">{{x['amount']}}</span>
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
{% if not loop.last %}
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<div><a href="/web/redeem/{{redeem['code']}}">使用者 Players who have used it.</a></div>
|
||||
|
||||
</div>
|
||||
{% if not loop.last %}
|
||||
<br />
|
||||
<hr />
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
37
latest version/templates/web/banuser.html
Normal file
37
latest version/templates/web/banuser.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Ban the user{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/banuser" method="post">
|
||||
<div class="title">Ban the user</div>
|
||||
<label for="name">Arcaea Username</label>
|
||||
<input name="name" id="name">
|
||||
or<br />
|
||||
<label for="user_code">Arcaea User Code</label>
|
||||
<input name="user_code" id="user_code">
|
||||
|
||||
<br />
|
||||
<div class="content">提示:重设用户密码即可解封账号</div>
|
||||
<div class="content">Tip: Reset the user password to unseal the account.</div>
|
||||
<br />
|
||||
<input type="submit" value="Execute">
|
||||
</form>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/banuser/deleteuserscore" method="post">
|
||||
<div class="title">Delete all of the user's scores</div>
|
||||
<label for="name">Arcaea Username</label>
|
||||
<input name="name" id="name">
|
||||
or<br />
|
||||
<label for="user_code">Arcaea User Code</label>
|
||||
<input name="user_code" id="user_code">
|
||||
|
||||
<br />
|
||||
<div class="content">警告!这个操作具有破坏性,且不可恢复。</div>
|
||||
<div class="content">Warning! This operation is destructive and unrecoverable.</div>
|
||||
<br />
|
||||
<input type="submit" value="Execute">
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -12,15 +12,6 @@
|
||||
<label for="level">Level</label>
|
||||
<input name="level" id="level">
|
||||
|
||||
<label for="frag">Frag</label>
|
||||
<input name="frag" id="frag">
|
||||
|
||||
<label for="prog">Prog</label>
|
||||
<input name="prog" id="prog">
|
||||
|
||||
<label for="overdrive">Overdrive</label>
|
||||
<input name="overdrive" id="overdrive">
|
||||
|
||||
<div>
|
||||
<select name="skill_id">
|
||||
<option value="">Skill id</option>
|
||||
|
||||
@@ -5,37 +5,43 @@
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/changeitem" method="post">
|
||||
<div class="title">Change the item</div>
|
||||
<div class="title">Add the item</div>
|
||||
<label for="item_id">ID of the item</label>
|
||||
<input name="item_id" id="item_id" required>
|
||||
<label for="type">Type</label>
|
||||
<div>
|
||||
<select name="type">
|
||||
<option value='single'>Single</option>
|
||||
<option value='single' selected>Single</option>
|
||||
<option value='pack'>Pack</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='character'>Character</option>
|
||||
</select>
|
||||
</div>
|
||||
<br />
|
||||
<div class="content">可修改项 Modifiable items:</div>
|
||||
<div><label for="is_available">是否可用 Is available:</label>
|
||||
<br />
|
||||
<br />
|
||||
<label><input type="radio" name="is_available" value=1>True</label>
|
||||
<label><input type="radio" name="is_available" checked value=1>True</label>
|
||||
<br />
|
||||
<label><input type="radio" name="is_available" value=0>False</label>
|
||||
</div>
|
||||
<label for="price">Price</label>
|
||||
<input name="price" id="price">
|
||||
<label for="orig_price">Original price</label>
|
||||
<input name="orig_price" id="orig_price">
|
||||
<label for="discount_from">Discount from</label>
|
||||
<input type="datetime-local" name="discount_from" id="discount_from">
|
||||
<label for="discount_to">Discount to</label>
|
||||
<input type="datetime-local" name="discount_to" id="discount_to">
|
||||
<div class="content">如果某些不需要修改,留空即可</div>
|
||||
<div class="content">If some things do not need to be modified, leave it blank.</div>
|
||||
<div class="content">时间填写是一个HTML5控件</div>
|
||||
<div class="content">Time filling is an HTML5 control.</div>
|
||||
<input type="submit" value="Edit">
|
||||
<br />
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
<form action="/web/changeitem/delete" method="post">
|
||||
<div class="title">Delete the item</div>
|
||||
<label for="item_id">ID of the item</label>
|
||||
<input name="item_id" id="item_id" required>
|
||||
<label for="type">Type</label>
|
||||
<div>
|
||||
<select name="type">
|
||||
<option value='single' selected>Single</option>
|
||||
<option value='pack'>Pack</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='character'>Character</option>
|
||||
</select>
|
||||
</div>
|
||||
<br />
|
||||
<input type="submit" value="Delete">
|
||||
</form>
|
||||
{% endblock %}
|
||||
49
latest version/templates/web/changepresent.html
Normal file
49
latest version/templates/web/changepresent.html
Normal file
@@ -0,0 +1,49 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Change the presents{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/changepresent/addpresent" method="post">
|
||||
<div class="title">Add the present</div>
|
||||
<label for="present_id">ID of the present</label>
|
||||
<input name="present_id" id="present_id" required>
|
||||
<label for="description">Description of the present</label>
|
||||
<input name="description" id="description" required>
|
||||
|
||||
<label for="expire_ts">Expire time</label>
|
||||
<input type="datetime-local" name="expire_ts" id="expire_ts" required>
|
||||
<br />
|
||||
|
||||
<label for="item_id">ID of the item</label>
|
||||
<input name="item_id" id="item_id" required>
|
||||
<label for="type">Type</label>
|
||||
<div>
|
||||
<select name="type">
|
||||
<option value='single' selected>Single song</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='world_unlock'>World unlock scene</option>
|
||||
<option value='character'>Character</option>
|
||||
<option value='core'>Character core</option>
|
||||
<option value='fragment'>Fragment</option>
|
||||
<option value='memory'>Memory</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="amount">Amount</label>
|
||||
<input name="amount" id="amount" value="1" required>
|
||||
<br />
|
||||
|
||||
<div class="content">时间填写是一个HTML5控件</div>
|
||||
<div class="content">Time filling is an HTML5 control.</div>
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/changepresent/deletepresent" method="post">
|
||||
<div class="title">Delete the present</div>
|
||||
<label for="present_id">ID of the present</label>
|
||||
<input name="present_id" id="present_id" required>
|
||||
|
||||
<input type="submit" value="Delete">
|
||||
</form>
|
||||
{% endblock %}
|
||||
32
latest version/templates/web/changepurchase.html
Normal file
32
latest version/templates/web/changepurchase.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Change the purchases{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/changepurchase" method="post">
|
||||
<div class="title">Add the purchase</div>
|
||||
<label for="purchase_name">Purchase name</label>
|
||||
<input name="purchase_name" id="purchase_name" required>
|
||||
<label for="price">Price</label>
|
||||
<input name="price" id="price">
|
||||
<label for="orig_price">Original price</label>
|
||||
<input name="orig_price" id="orig_price">
|
||||
<label for="discount_from">Discount from</label>
|
||||
<input type="datetime-local" name="discount_from" id="discount_from">
|
||||
<label for="discount_to">Discount to</label>
|
||||
<input type="datetime-local" name="discount_to" id="discount_to">
|
||||
<div class="content">时间填写是一个HTML5控件</div>
|
||||
<div class="content">Time filling is an HTML5 control.</div>
|
||||
<br />
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
|
||||
<form action="/web/changepurchase/delete" method="post">
|
||||
<div class="title">Delete the purchase</div>
|
||||
<label for="purchase_name">Purchase name</label>
|
||||
<input name="purchase_name" id="purchase_name" required>
|
||||
<br />
|
||||
<input type="submit" value="Delete">
|
||||
</form>
|
||||
{% endblock %}
|
||||
48
latest version/templates/web/changepurchaseitem.html
Normal file
48
latest version/templates/web/changepurchaseitem.html
Normal file
@@ -0,0 +1,48 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Change the purchase's items{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/changepurchaseitem" method="post">
|
||||
<div class="title">Add the purchase's item</div>
|
||||
<label for="purchase_name">Purchase name</label>
|
||||
<input name="purchase_name" id="purchase_name" required>
|
||||
<label for="item_id">ID of the item</label>
|
||||
<input name="item_id" id="item_id" required>
|
||||
<label for="type">Type</label>
|
||||
<div>
|
||||
<select name="type">
|
||||
<option value='single' selected>Single song</option>
|
||||
<option value='pack'>Song pack</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='character'>Character</option>
|
||||
<option value='core'>Core</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="amount">Amount</label>
|
||||
<input name="amount" id="amount" value="1" required>
|
||||
<br />
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
|
||||
<form action="/web/changepurchaseitem/delete" method="post">
|
||||
<div class="title">Delete the purchase's item</div>
|
||||
<label for="purchase_name">Purchase name</label>
|
||||
<input name="purchase_name" id="purchase_name" required>
|
||||
<label for="item_id">ID of the item</label>
|
||||
<input name="item_id" id="item_id" required>
|
||||
<label for="type">Type</label>
|
||||
<div>
|
||||
<select name="type">
|
||||
<option value='single' selected>Single</option>
|
||||
<option value='pack'>Pack</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='character'>Character</option>
|
||||
<option value='core'>Core</option>
|
||||
</select>
|
||||
</div>
|
||||
<br />
|
||||
<input type="submit" value="Delete">
|
||||
</form>
|
||||
{% endblock %}
|
||||
63
latest version/templates/web/changeredeem.html
Normal file
63
latest version/templates/web/changeredeem.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Change the redeem codes{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/changeredeem/addredeem" method="post">
|
||||
<div class="title">Add the redeem</div>
|
||||
<div>指定生成 Specify generation</div>
|
||||
<label for="code">The redeem code</label>
|
||||
<input name="code" id="code">
|
||||
<div>Or</div>
|
||||
<div>随机批量生成 Batch generation at random</div>
|
||||
<label for="redeem_amount">Redeem code amount</label>
|
||||
<input name="redeem_amount" id="redeem_amount">
|
||||
<br />
|
||||
<div>Redeem code type:
|
||||
<br />
|
||||
<br />
|
||||
<label><input type="radio" name="redeem_type" value="0" checked>仅可使用一次 It can only be used once.</label>
|
||||
<br />
|
||||
<label><input type="radio" name="redeem_type" value="1">每个用户仅可以使用一次 Each user can only use it once.</label>
|
||||
</div>
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<label for="item_id">ID of the item</label>
|
||||
<input name="item_id" id="item_id" required>
|
||||
<label for="type">Type</label>
|
||||
<div>
|
||||
<select name="type">
|
||||
<option value='single' selected>Single song</option>
|
||||
<option value='pack'>Song pack</option>
|
||||
<option value='world_song'>World song</option>
|
||||
<option value='world_unlock'>World unlock scene</option>
|
||||
<option value='character'>Character</option>
|
||||
<option value='core'>Character core</option>
|
||||
<option value='fragment'>Fragment</option>
|
||||
<option value='memory'>Memory</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="amount">Amount</label>
|
||||
<input name="amount" id="amount" value="1" required>
|
||||
<br />
|
||||
|
||||
|
||||
<div class="content">兑换码长度为10~20。</div>
|
||||
<div class="content">The length of redeem code is 10 ~ 20.</div>
|
||||
<div class="content">随机生成数量不得超过1000。</div>
|
||||
<div class="content">The amount of batch generation should not be more than 1000.</div>
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/changeredeem/deleteredeem" method="post">
|
||||
<div class="title">Delete the redeem</div>
|
||||
<label for="code">The redeem code</label>
|
||||
<input name="code" id="code" required>
|
||||
|
||||
<input type="submit" value="Delete">
|
||||
</form>
|
||||
{% endblock %}
|
||||
38
latest version/templates/web/changescore.html
Normal file
38
latest version/templates/web/changescore.html
Normal file
@@ -0,0 +1,38 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Change the scores{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/changescore/delete" method="post">
|
||||
<div class="title">Delete the scores</div>
|
||||
<label for="sid">SID of the song</label>
|
||||
<input name="sid" id="sid">
|
||||
|
||||
<label for="difficulty">Difficulty</label>
|
||||
<select name='difficulty' id='difficulty'>
|
||||
<option value="-1" selected>All</option>
|
||||
<option value="0">Past</option>
|
||||
<option value="1">Present</option>
|
||||
<option value="2">Future</option>
|
||||
<option value="3">Beyond</option>
|
||||
</select>
|
||||
<br />
|
||||
|
||||
<label for="name">Arcaea Username</label>
|
||||
<input name="name" id="name">
|
||||
or<br />
|
||||
<label for="user_code">Arcaea User Code</label>
|
||||
<input name="user_code" id="user_code">
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<div class="content">上述三项是查询参数,如果留空,则被认为是全部,都不填则认为是错误的。</div>
|
||||
<div class="content">The above three are query parameters. If they are left blank, they are considered all. However,
|
||||
if you don't fill in all of them, it's wrong.</div>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<input type="submit" value="Delete">
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -19,7 +19,7 @@
|
||||
<input name="rating_ftr" id="rating_ftr" required>
|
||||
<label for="rating_byd">Beyond chart const</label>
|
||||
<input name="rating_byd" id="rating_byd" required>
|
||||
<div class="content">如果没有某个铺面,应该填入-1。</div>
|
||||
<div class="content">如果没有某个谱面,应该填入-1。</div>
|
||||
<div class="content">If there is no some chart, fill in -1 please.</div>
|
||||
<input type="submit" value="Add">
|
||||
</form>
|
||||
|
||||
24
latest version/templates/web/changeuserpwd.html
Normal file
24
latest version/templates/web/changeuserpwd.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Change user password{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/changeuserpwd" method="post">
|
||||
<div class="title">Edit the user's password</div>
|
||||
<label for="name">Arcaea Username</label>
|
||||
<input name="name" id="name">
|
||||
or<br />
|
||||
<label for="user_code">Arcaea User Code</label>
|
||||
<input name="user_code" id="user_code">
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<label for="pwd">Password</label>
|
||||
<input type="password" name="pwd" id="pwd" required>
|
||||
<label for="pwd2">Enter again</label>
|
||||
<input type="password" name="pwd2" id="pwd2" required>
|
||||
|
||||
<input type="submit" value="Edit">
|
||||
</form>
|
||||
{% endblock %}
|
||||
32
latest version/templates/web/deliverpresent.html
Normal file
32
latest version/templates/web/deliverpresent.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Deliver presents{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="/web/deliverpresent" method="post">
|
||||
<div class="title">Deliver to the user</div>
|
||||
<label for="name">Arcaea Username</label>
|
||||
<input name="name" id="name">
|
||||
or<br />
|
||||
<label for="user_code">Arcaea User Code</label>
|
||||
<input name="user_code" id="user_code">
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<label for="present_id">ID of the present</label>
|
||||
<input name="present_id" id="present_id" required>
|
||||
|
||||
<br />
|
||||
<input type="submit" value="Edit">
|
||||
</form>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/deliverpresent" method="post">
|
||||
<div class="title">Deliver to all the users</div>
|
||||
<label for="present_id">ID of the present</label>
|
||||
<input name="present_id" id="present_id" required>
|
||||
|
||||
<input type="submit" value="Edit all">
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -13,19 +13,31 @@
|
||||
<a href="{{ url_for('index.single_player_score') }}">单个玩家成绩查询 Single player score</a></br></br>
|
||||
<a href="{{ url_for('index.single_player_ptt') }}">单个玩家PTT详情查询 Single player ptt</a></br></br>
|
||||
<a href="{{ url_for('index.all_player') }}">所有玩家信息查询 All players</a></br></br>
|
||||
<a href="{{ url_for('index.all_song') }}">铺面信息查询 All songs</a></br></br>
|
||||
<a href="{{ url_for('index.all_song') }}">谱面信息查询 All songs</a></br></br>
|
||||
<a href="{{ url_for('index.all_character') }}">角色信息查询 All characters</a></br></br>
|
||||
<a href="{{ url_for('index.all_item') }}">购买信息查询 All items</a></br></br>
|
||||
<a href="{{ url_for('index.all_item') }}">物品信息查询 All items</a></br></br>
|
||||
<a href="{{ url_for('index.all_purchase') }}">购买信息查询 All purchases</a></br></br>
|
||||
<a href="{{ url_for('index.all_present') }}">奖励信息查询 All presents</a></br></br>
|
||||
<a href="{{ url_for('index.all_redeem') }}">兑换码信息查询 All redeem codes</a></br></br>
|
||||
<a href="{{ url_for('index.single_chart_top') }}">单个铺面排行榜查询 Single song chart tops</a>
|
||||
<hr>
|
||||
<h1>系统方面 System</h1>
|
||||
<a href="{{ url_for('index.update_database') }}">数据库更新 Update databases</a></br></br>
|
||||
<a href="{{ url_for('index.edit_userpwd') }}">用户密码修改 Change user password</a></br></br>
|
||||
<a href="{{ url_for('index.ban_user') }}">封禁用户 Ban the user</a></br></br>
|
||||
<a href="{{ url_for('index.change_score') }}">成绩删除 Delete the scores</a></br></br>
|
||||
<a href="{{ url_for('index.update_user_save') }}">用户存档更新 Update user saves</a></br></br>
|
||||
<a href="{{ url_for('index.change_song') }}">歌曲修改 Change the songs</a></br></br>
|
||||
<a href="{{ url_for('index.change_character') }}">角色修改 Change the characters</a></br></br>
|
||||
<a href="{{ url_for('index.change_item') }}">购买信息修改 Change the items</a></br></br>
|
||||
<a href="{{ url_for('index.change_item') }}">物品信息修改 Change the items</a></br></br>
|
||||
<a href="{{ url_for('index.change_purchase') }}">购买信息修改 Change the purchases</a></br></br>
|
||||
<a href="{{ url_for('index.change_purchase_item') }}">购买信息的物品修改 Change the purchase items</a></br></br>
|
||||
<a href="{{ url_for('index.change_user') }}">用户信息修改 Change user information</a></br></br>
|
||||
<a href="{{ url_for('index.change_user_purchase') }}">用户购买信息修改 Change user purchase information</a>
|
||||
<a href="{{ url_for('index.change_user_purchase') }}">用户购买信息修改 Change user purchase information</a></br></br>
|
||||
<a href="{{ url_for('index.change_present') }}">奖励修改 Change the presents</a></br></br>
|
||||
<a href="{{ url_for('index.deliver_present') }}">奖励分发 Deliver the presents</a></br></br>
|
||||
<a href="{{ url_for('index.change_redeem') }}">兑换码修改 Change the redeem codes</a>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
24
latest version/templates/web/redeem.html
Normal file
24
latest version/templates/web/redeem.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block header %}
|
||||
<h1>{% block title %}Redeem - {{code}}{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if posts %}
|
||||
{% for user in posts %}
|
||||
|
||||
<div id="user-info">
|
||||
<div class="name">{{user['name']}}
|
||||
<span class="rank">UID: {{user['user_id']}}</span>
|
||||
<span class="rank">User code: {{user['user_code']}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if not loop.last %}
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
<br />
|
||||
<a href="{{ url_for('index.all_redeem') }}">Back</a>
|
||||
{% endblock %}
|
||||
@@ -22,6 +22,9 @@
|
||||
<div class="name">{{user['name']}}
|
||||
<span class="rank">UID: {{user['user_id']}}</span>
|
||||
<span class="rank">User code: {{user['user_code']}}</span>
|
||||
{% if user['ban_flag'] %}
|
||||
<span class="rank">Banned</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="join-date">注册于 Registered in: {{user['join_date']}}</div>
|
||||
<div class="ptt">Memories: {{user['ticket']}}</div>
|
||||
@@ -161,7 +164,7 @@
|
||||
{% if recent %}
|
||||
<div class="title">Recent 30</div>
|
||||
{% for i in recent %}
|
||||
|
||||
|
||||
{% if i %}
|
||||
<div class="score-item">
|
||||
<span class="song-title">
|
||||
|
||||
@@ -10,12 +10,34 @@
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
<div class="content">
|
||||
这里可以将旧版本的数据库同步到新版本的数据库,并刷新用户拥有的角色列表。<br />
|
||||
这里可以将旧版本的数据库同步到新版本的数据库,并刷新用户拥有的全角色列表。<br />
|
||||
可上传文件: arcaea_database.db和arcsong.db<br />
|
||||
新数据库不存在的数据会被添加,存在的数据将不会被改变。<br /><br />
|
||||
Here you can synchronize the old version of the database to the new version of the database and refresh the list of
|
||||
新数据库不存在的数据会被添加,存在的重复数据也会被改变。<br /><br />
|
||||
Here you can synchronize the old version of the database to the new version of the database and refresh the list of full
|
||||
characters owned by players.<br />
|
||||
Uploadable files: arcaea_database.db & arcsong.db<br />
|
||||
Data that does not exist in the new database will be added and the existing data will not be changed.
|
||||
Data that does not exist in the new database will be added and the existing duplicate data will also be changed.
|
||||
</div>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/updatedatabase/refreshsonghash" method="post">
|
||||
<div class="title">Refresh songs' hash</div>
|
||||
<br />
|
||||
<input type="submit" value="Refresh">
|
||||
|
||||
<div class="content">这里可以刷新储存在数据库中的songs文件夹下所有文件的Hash值。目的是应对歌曲谱面文件的更新。</div>
|
||||
<div class="content">Here you can refresh the hash values of all the files stored in the songs folder in the
|
||||
database. The purpose is to deal with the updating of songs' files.</div>
|
||||
</form>
|
||||
<br />
|
||||
<hr />
|
||||
<form action="/web/updatedatabase/refreshsongrating" method="post">
|
||||
<div class="title">Refresh songs' rating</div>
|
||||
<br />
|
||||
<input type="submit" value="Refresh">
|
||||
|
||||
<div class="content">这里可以刷新数据库中所有成绩的评分。目的是应对歌曲谱面定数的更新。</div>
|
||||
<div class="content">Here you can refresh the ratings of all scores in the database. The purpose is to deal
|
||||
with the updating of songs' chart consts.</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
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
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,8 @@
|
||||
from flask import (Blueprint, flash, g, redirect,
|
||||
render_template, request, session, url_for)
|
||||
import functools
|
||||
import configparser
|
||||
from setting import Config
|
||||
import hashlib
|
||||
|
||||
bp = Blueprint('login', __name__, url_prefix='/web')
|
||||
|
||||
@@ -15,18 +16,16 @@ def login():
|
||||
password = request.form['password']
|
||||
error = None
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
path = r'setting.ini'
|
||||
config.read(path, encoding="utf-8")
|
||||
USERNAME = config.get('WEB', 'USERNAME')
|
||||
PASSWORD = config.get('WEB', 'PASSWORD')
|
||||
|
||||
if username != USERNAME and password != PASSWORD:
|
||||
if username != Config.USERNAME or password != Config.PASSWORD:
|
||||
error = '错误的用户名或密码 Incorrect username or password.'
|
||||
|
||||
if error is None:
|
||||
session.clear()
|
||||
session['user_id'] = USERNAME + PASSWORD
|
||||
hash_session = username + \
|
||||
hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
hash_session = hashlib.sha256(
|
||||
hash_session.encode("utf8")).hexdigest()
|
||||
session['user_id'] = hash_session
|
||||
return redirect(url_for('index.index'))
|
||||
|
||||
flash(error)
|
||||
@@ -48,16 +47,14 @@ def login_required(view):
|
||||
def wrapped_view(**kwargs):
|
||||
x = session.get('user_id')
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
path = r'setting.ini'
|
||||
config.read(path, encoding="utf-8")
|
||||
USERNAME = config.get('WEB', 'USERNAME')
|
||||
PASSWORD = config.get('WEB', 'PASSWORD')
|
||||
hash_session = Config.USERNAME + \
|
||||
hashlib.sha256(Config.PASSWORD.encode("utf8")).hexdigest()
|
||||
hash_session = hashlib.sha256(hash_session.encode("utf8")).hexdigest()
|
||||
|
||||
if x != USERNAME + PASSWORD:
|
||||
if x != hash_session:
|
||||
return redirect(url_for('login.login'))
|
||||
|
||||
g.user = {'user_id': x, 'username': USERNAME}
|
||||
g.user = {'user_id': x, 'username': Config.USERNAME}
|
||||
return view(**kwargs)
|
||||
|
||||
return wrapped_view
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import os
|
||||
import sqlite3
|
||||
from server.sql import Connect
|
||||
import time
|
||||
import json
|
||||
import server.arcscore
|
||||
import hashlib
|
||||
from random import Random
|
||||
from setting import Config
|
||||
|
||||
|
||||
def int2b(x):
|
||||
@@ -13,6 +16,17 @@ def int2b(x):
|
||||
return True
|
||||
|
||||
|
||||
def random_str(randomlength=10):
|
||||
# 随机生成字符串
|
||||
s = ''
|
||||
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
|
||||
length = len(chars) - 1
|
||||
random = Random()
|
||||
for i in range(randomlength):
|
||||
s += chars[random.randint(0, length)]
|
||||
return s
|
||||
|
||||
|
||||
def get_table_info(c, table_name):
|
||||
# 得到表结构,返回主键列表和字段名列表
|
||||
pk = []
|
||||
@@ -85,8 +99,25 @@ def get_sql_insert_table(table_name, field, value):
|
||||
return sql, sql_dict
|
||||
|
||||
|
||||
def get_sql_delete_table(table_name, where_field=[], where_value=[]):
|
||||
# sql语句拼接,delete from ... where ...
|
||||
sql = 'delete from ' + table_name
|
||||
sql_dict = {}
|
||||
|
||||
if where_field and where_value:
|
||||
sql += ' where '
|
||||
sql += where_field[0] + '=:' + where_field[0]
|
||||
sql_dict[where_field[0]] = where_value[0]
|
||||
if len(where_field) >= 1:
|
||||
for i in range(1, len(where_field)):
|
||||
sql_dict[where_field[i]] = where_value[i]
|
||||
sql += ' and ' + where_field[i] + '=:' + where_field[i]
|
||||
|
||||
return sql, sql_dict
|
||||
|
||||
|
||||
def update_one_table(c1, c2, table_name):
|
||||
# 从c1向c2更新数据表,c2中存在的信息不变
|
||||
# 从c1向c2更新数据表,c1中存在的信息不变
|
||||
c1.execute(
|
||||
'''select * from sqlite_master where type = 'table' and name = :a''', {'a': table_name})
|
||||
c2.execute(
|
||||
@@ -117,71 +148,81 @@ def update_one_table(c1, c2, table_name):
|
||||
sql = 'select exists(' + sql + ')'
|
||||
c2.execute(sql, sql_dict)
|
||||
|
||||
if c2.fetchone() == (0,):
|
||||
sql, sql_dict = get_sql_insert_table(
|
||||
table_name, field, list(y[i]))
|
||||
if c2.fetchone() == (1,): # 如果c2里存在,先删除
|
||||
sql, sql_dict = get_sql_delete_table(
|
||||
table_name, db1_pk, list(x[i]))
|
||||
c2.execute(sql, sql_dict)
|
||||
|
||||
sql, sql_dict = get_sql_insert_table(
|
||||
table_name, field, list(y[i]))
|
||||
c2.execute(sql, sql_dict)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def update_user_char(c):
|
||||
# 用character数据更新user_char
|
||||
c.execute('''select * from character''')
|
||||
c.execute('''select character_id, max_level, is_uncapped from character''')
|
||||
x = c.fetchall()
|
||||
c.execute('''select user_id from user''')
|
||||
y = c.fetchall()
|
||||
if x and y:
|
||||
for j in y:
|
||||
for i in x:
|
||||
c.execute('''delete from user_char where user_id=:a and character_id=:b''', {
|
||||
c.execute('''delete from user_char_full where user_id=:a and character_id=:b''', {
|
||||
'a': j[0], 'b': i[0]})
|
||||
c.execute('''insert into user_char values(:a,:b,:c,:d,:e,:f,:g,:h,:i,:j,:k,:l,:m,:n,:o)''', {
|
||||
'a': j[0], 'b': i[0], 'c': i[2], 'd': i[3], 'e': i[4], 'f': i[5], 'g': i[6], 'h': i[7], 'i': i[8], 'j': i[9], 'k': i[10], 'l': i[11], 'm': i[12], 'n': i[14], 'o': i[15]})
|
||||
exp = 25000 if i[1] == 30 else 10000
|
||||
c.execute('''insert into user_char_full values(?,?,?,?,?,?)''',
|
||||
(j[0], i[0], i[1], exp, i[2], 0))
|
||||
|
||||
|
||||
def update_database():
|
||||
# 将old数据库不存在数据加入到新数据库上,并删除old数据库
|
||||
# 对于arcaea_datebase.db,更新best_score,friend,recent30,user,user_world, user_item, user_save, login, present, user_present并用character数据更新user_char
|
||||
# 对于arcaea_datebase.db,更新一些表,并用character数据更新user_char_full
|
||||
# 对于arcsong.db,更新songs
|
||||
if os.path.isfile("database/old_arcaea_database.db") and os.path.isfile("database/arcaea_database.db"):
|
||||
conn1 = sqlite3.connect('./database/old_arcaea_database.db')
|
||||
c1 = conn1.cursor()
|
||||
conn2 = sqlite3.connect('./database/arcaea_database.db')
|
||||
c2 = conn2.cursor()
|
||||
with Connect('./database/old_arcaea_database.db') as c1:
|
||||
with Connect() as c2:
|
||||
|
||||
update_one_table(c1, c2, 'user')
|
||||
update_one_table(c1, c2, 'friend')
|
||||
update_one_table(c1, c2, 'best_score')
|
||||
update_one_table(c1, c2, 'recent30')
|
||||
update_one_table(c1, c2, 'user_world')
|
||||
update_one_table(c1, c2, 'user_item')
|
||||
update_one_table(c1, c2, 'user_save')
|
||||
update_one_table(c1, c2, 'login')
|
||||
update_one_table(c1, c2, 'present')
|
||||
update_one_table(c1, c2, 'user_present')
|
||||
update_one_table(c1, c2, 'user')
|
||||
update_one_table(c1, c2, 'friend')
|
||||
update_one_table(c1, c2, 'best_score')
|
||||
update_one_table(c1, c2, 'recent30')
|
||||
update_one_table(c1, c2, 'user_world')
|
||||
update_one_table(c1, c2, 'item')
|
||||
update_one_table(c1, c2, 'user_item')
|
||||
update_one_table(c1, c2, 'purchase')
|
||||
update_one_table(c1, c2, 'purchase_item')
|
||||
update_one_table(c1, c2, 'user_save')
|
||||
update_one_table(c1, c2, 'login')
|
||||
update_one_table(c1, c2, 'present')
|
||||
update_one_table(c1, c2, 'user_present')
|
||||
update_one_table(c1, c2, 'present_item')
|
||||
update_one_table(c1, c2, 'redeem')
|
||||
update_one_table(c1, c2, 'user_redeem')
|
||||
update_one_table(c1, c2, 'redeem_item')
|
||||
update_one_table(c1, c2, 'role')
|
||||
update_one_table(c1, c2, 'user_role')
|
||||
update_one_table(c1, c2, 'power')
|
||||
update_one_table(c1, c2, 'role_power')
|
||||
update_one_table(c1, c2, 'api_login')
|
||||
|
||||
update_user_char(c2)
|
||||
update_one_table(c1, c2, 'user_char')
|
||||
|
||||
if not Config.UPDATE_WITH_NEW_CHARACTER_DATA:
|
||||
update_one_table(c1, c2, 'character')
|
||||
|
||||
update_user_char(c2) # 更新user_char_full
|
||||
|
||||
conn1.commit()
|
||||
conn1.close()
|
||||
conn2.commit()
|
||||
conn2.close()
|
||||
os.remove('database/old_arcaea_database.db')
|
||||
|
||||
# songs
|
||||
if os.path.isfile("database/old_arcsong.db") and os.path.isfile("database/arcsong.db"):
|
||||
conn1 = sqlite3.connect('./database/old_arcsong.db')
|
||||
c1 = conn1.cursor()
|
||||
conn2 = sqlite3.connect('./database/arcsong.db')
|
||||
c2 = conn2.cursor()
|
||||
with Connect('./database/old_arcsong.db') as c1:
|
||||
with Connect('./database/arcsong.db') as c2:
|
||||
|
||||
update_one_table(c1, c2, 'songs')
|
||||
update_one_table(c1, c2, 'songs')
|
||||
|
||||
conn1.commit()
|
||||
conn1.close()
|
||||
conn2.commit()
|
||||
conn2.close()
|
||||
os.remove('database/old_arcsong.db')
|
||||
|
||||
|
||||
@@ -190,14 +231,16 @@ def unlock_all_user_item(c):
|
||||
|
||||
c.execute('''select user_id from user''')
|
||||
x = c.fetchall()
|
||||
c.execute('''select item_id, type from item''')
|
||||
c.execute('''select item_id, type from purchase_item''')
|
||||
y = c.fetchall()
|
||||
c.execute('''delete from user_item''')
|
||||
if x and y:
|
||||
for i in x:
|
||||
for j in y:
|
||||
c.execute('''insert into user_item values(:a,:b,:c)''', {
|
||||
c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', {
|
||||
'a': i[0], 'b': j[0], 'c': j[1]})
|
||||
if c.fetchone() == (0,) and j[1] != 'character':
|
||||
c.execute('''insert into user_item values(:a,:b,:c,1)''', {
|
||||
'a': i[0], 'b': j[0], 'c': j[1]})
|
||||
|
||||
return
|
||||
|
||||
@@ -205,49 +248,70 @@ def unlock_all_user_item(c):
|
||||
def unlock_user_item(c, user_id):
|
||||
# 解锁用户购买
|
||||
|
||||
c.execute('''select item_id, type from item''')
|
||||
c.execute('''select item_id, type from purchase_item''')
|
||||
y = c.fetchall()
|
||||
|
||||
for j in y:
|
||||
c.execute('''select exists(select * from user_item where user_id=:a and item_id=:b and type=:c)''', {
|
||||
'a': user_id, 'b': j[0], 'c': j[1]})
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into user_item values(:a,:b,:c)''', {
|
||||
if c.fetchone() == (0,) and j[1] != 'character':
|
||||
c.execute('''insert into user_item values(:a,:b,:c,1)''', {
|
||||
'a': user_id, 'b': j[0], 'c': j[1]})
|
||||
|
||||
return
|
||||
|
||||
|
||||
def get_all_item():
|
||||
# 所有物品数据查询
|
||||
with Connect() as c:
|
||||
c.execute('''select * from item''')
|
||||
x = c.fetchall()
|
||||
re = []
|
||||
if x:
|
||||
for i in x:
|
||||
re.append({'item_id': i[0],
|
||||
'type': i[1],
|
||||
'is_available': int2b(i[2])
|
||||
})
|
||||
|
||||
return re
|
||||
|
||||
|
||||
def get_all_purchase():
|
||||
# 所有购买数据查询
|
||||
conn = sqlite3.connect('./database/arcaea_database.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''select * from item''')
|
||||
x = c.fetchall()
|
||||
re = []
|
||||
if x:
|
||||
for i in x:
|
||||
discount_from = None
|
||||
discount_to = None
|
||||
with Connect() as c:
|
||||
c.execute('''select * from purchase''')
|
||||
x = c.fetchall()
|
||||
re = []
|
||||
if x:
|
||||
for i in x:
|
||||
|
||||
if i[5] and i[5] >= 0:
|
||||
discount_from = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[5])/1000))
|
||||
if i[6] and i[6] >= 0:
|
||||
discount_to = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[6])//1000))
|
||||
discount_from = None
|
||||
discount_to = None
|
||||
|
||||
re.append({'item_id': i[0],
|
||||
'type': i[1],
|
||||
'is_available': int2b(i[2]),
|
||||
'price': i[3],
|
||||
'orig_price': i[4],
|
||||
'discount_from': discount_from,
|
||||
'discount_to': discount_to
|
||||
})
|
||||
if i[3] and i[3] >= 0:
|
||||
discount_from = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[3])/1000))
|
||||
if i[4] and i[4] >= 0:
|
||||
discount_to = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(int(i[4])//1000))
|
||||
|
||||
c.execute(
|
||||
'''select * from purchase_item where purchase_name=?''', (i[0],))
|
||||
y = c.fetchall()
|
||||
items = []
|
||||
if y:
|
||||
for j in y:
|
||||
items.append({'item_id': j[1], 'type': j[2], 'amount':j[3]})
|
||||
|
||||
re.append({'purchase_name': i[0],
|
||||
'price': i[1],
|
||||
'orig_price': i[2],
|
||||
'discount_from': discount_from,
|
||||
'discount_to': discount_to,
|
||||
'items': items
|
||||
})
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return re
|
||||
|
||||
|
||||
@@ -270,6 +334,8 @@ def update_one_save(c, user_id):
|
||||
for i in scores:
|
||||
rating = server.arcscore.get_one_ptt(
|
||||
i['song_id'], i['difficulty'], i['score'])
|
||||
if rating < 0:
|
||||
rating = 0
|
||||
try:
|
||||
index = clear_song_id_difficulty.index(
|
||||
i['song_id'] + str(i['difficulty']))
|
||||
@@ -301,3 +367,178 @@ def update_all_save(c):
|
||||
update_one_save(c, i[0])
|
||||
|
||||
return
|
||||
|
||||
|
||||
def add_one_present(present_id, expire_ts, description, item_id, item_type, item_amount):
|
||||
# 添加一个奖励
|
||||
|
||||
message = None
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select exists(select * from present where present_id=:a)''', {'a': present_id})
|
||||
if c.fetchone() == (0,):
|
||||
c.execute(
|
||||
'''select exists(select * from item where item_id=? and type=?)''', (item_id, item_type))
|
||||
if c.fetchone() == (1,):
|
||||
c.execute('''insert into present values(:a,:b,:c)''', {
|
||||
'a': present_id, 'b': expire_ts, 'c': description})
|
||||
c.execute('''insert into present_item values(?,?,?,?)''',
|
||||
(present_id, item_id, item_type, item_amount))
|
||||
message = '添加成功 Successfully add it.'
|
||||
else:
|
||||
message = '物品不存在 The item does not exist.'
|
||||
else:
|
||||
message = '奖励已存在 The present exists.'
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def delete_one_present(present_id):
|
||||
# 删除一个奖励
|
||||
|
||||
message = None
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select exists(select * from present where present_id=:a)''', {'a': present_id})
|
||||
if c.fetchone() == (1,):
|
||||
c.execute('''delete from present where present_id = :a''',
|
||||
{'a': present_id})
|
||||
c.execute('''delete from user_present where present_id =:a''', {
|
||||
'a': present_id})
|
||||
c.execute('''delete from present_item where present_id =:a''', {
|
||||
'a': present_id})
|
||||
message = '删除成功 Successfully delete it.'
|
||||
else:
|
||||
message = '奖励不存在 The present does not exist.'
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def is_present_available(c, present_id):
|
||||
# 判断present_id是否有效
|
||||
c.execute(
|
||||
'''select exists(select * from present where present_id = :a)''', {'a': present_id})
|
||||
|
||||
if c.fetchone() == (1,):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def deliver_one_user_present(c, present_id, user_id):
|
||||
# 为指定玩家添加奖励,重复添加不会提示
|
||||
c.execute('''select exists(select * from user_present where user_id=:a and present_id=:b)''',
|
||||
{'a': user_id, 'b': present_id})
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into user_present values(:a,:b)''',
|
||||
{'a': user_id, 'b': present_id})
|
||||
return
|
||||
|
||||
|
||||
def deliver_all_user_present(c, present_id):
|
||||
# 为所有玩家添加奖励
|
||||
c.execute('''select user_id from user''')
|
||||
x = c.fetchall()
|
||||
if x:
|
||||
c.execute('''delete from user_present where present_id=:b''',
|
||||
{'b': present_id})
|
||||
for i in x:
|
||||
c.execute('''insert into user_present values(:a,:b)''',
|
||||
{'a': i[0], 'b': present_id})
|
||||
|
||||
return
|
||||
|
||||
|
||||
def add_one_redeem(code, redeem_type, item_id, item_type, item_amount):
|
||||
# 添加一个兑换码
|
||||
|
||||
message = None
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select exists(select * from redeem where code=:a)''', {'a': code})
|
||||
if c.fetchone() == (0,):
|
||||
c.execute(
|
||||
'''select exists(select * from item where item_id=? and type=?)''', (item_id, item_type))
|
||||
if c.fetchone() == (1,):
|
||||
c.execute('''insert into redeem values(:a,:c)''', {
|
||||
'a': code, 'c': redeem_type})
|
||||
c.execute('''insert into redeem_item values(?,?,?,?)''',
|
||||
(code, item_id, item_type, item_amount))
|
||||
message = '添加成功 Successfully add it.'
|
||||
else:
|
||||
message = '物品不存在 The item does not exist.'
|
||||
else:
|
||||
message = '兑换码已存在 The redeem code exists.'
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def add_some_random_redeem(amount, redeem_type, item_id, item_type, item_amount):
|
||||
# 随机生成一堆10位的兑换码
|
||||
|
||||
message = None
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select exists(select * from item where item_id=? and type=?)''', (item_id, item_type))
|
||||
if c.fetchone() == (0,):
|
||||
return '物品不存在 The item does not exist.'
|
||||
i = 1
|
||||
while i <= amount:
|
||||
code = random_str()
|
||||
c.execute(
|
||||
'''select exists(select * from redeem where code=:a)''', {'a': code})
|
||||
if c.fetchone() == (0,):
|
||||
c.execute('''insert into redeem values(:a,:c)''',
|
||||
{'a': code, 'c': redeem_type})
|
||||
c.execute('''insert into redeem_item values(?,?,?,?)''',
|
||||
(code, item_id, item_type, item_amount))
|
||||
i += 1
|
||||
|
||||
message = '添加成功 Successfully add it.'
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def delete_one_redeem(code):
|
||||
# 删除一个兑换码
|
||||
|
||||
message = None
|
||||
with Connect() as c:
|
||||
c.execute(
|
||||
'''select exists(select * from redeem where code=:a)''', {'a': code})
|
||||
if c.fetchone() == (1,):
|
||||
c.execute('''delete from redeem where code = :a''', {'a': code})
|
||||
c.execute(
|
||||
'''delete from user_redeem where code =:a''', {'a': code})
|
||||
c.execute(
|
||||
'''delete from redeem_item where code =:a''', {'a': code})
|
||||
message = '删除成功 Successfully delete it.'
|
||||
else:
|
||||
message = '兑换码不存在 The redeem code does not exist.'
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def change_userpwd(c, user_id, password):
|
||||
# 修改用户密码
|
||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||
c.execute('''update user set password =:a where user_id=:b''',
|
||||
{'a': hash_pwd, 'b': user_id})
|
||||
return
|
||||
|
||||
|
||||
def ban_one_user(c, user_id):
|
||||
# 封禁用户
|
||||
c.execute('''update user set password = '' where user_id=:a''',
|
||||
{'a': user_id})
|
||||
c.execute('''delete from login where user_id=:a''', {'a': user_id})
|
||||
return
|
||||
|
||||
|
||||
def clear_user_score(c, user_id):
|
||||
# 清除用户所有成绩,包括best_score和recent30,以及recent数据,但不包括云端存档
|
||||
c.execute('''update user set rating_ptt=0, song_id='', difficulty=0, score=0, shiny_perfect_count=0, perfect_count=0, near_count=0, miss_count=0, health=0, time_played=0, rating=0, world_rank_score=0 where user_id=:a''',
|
||||
{'a': user_id})
|
||||
c.execute('''delete from best_score where user_id=:a''', {'a': user_id})
|
||||
c.execute('''delete from recent30 where user_id=:a''', {'a': user_id})
|
||||
return
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import time
|
||||
|
||||
|
||||
def get_user_score(c, user_id, limit=-1):
|
||||
def get_user_score(c, user_id, limit=-1, offset=0):
|
||||
# 返回用户的所有歌曲数据,带排名,返回字典列表
|
||||
if limit >= 0:
|
||||
c.execute('''select * from best_score where user_id =:a order by rating DESC limit :b''',
|
||||
{'a': user_id, 'b': limit})
|
||||
c.execute('''select * from best_score where user_id =:a order by rating DESC limit :b offset :c''',
|
||||
{'a': user_id, 'b': limit, 'c': offset})
|
||||
else:
|
||||
c.execute(
|
||||
'''select * from best_score where user_id =:a order by rating DESC''', {'a': user_id})
|
||||
@@ -49,6 +49,10 @@ def get_user(c, user_id):
|
||||
if x[20]:
|
||||
time_played = time.strftime('%Y-%m-%d %H:%M:%S',
|
||||
time.localtime(int(x[20])//1000))
|
||||
if x[2] == '':
|
||||
ban_flag = True
|
||||
else:
|
||||
ban_flag = False
|
||||
|
||||
r = {'name': x[1],
|
||||
'user_id': user_id,
|
||||
@@ -65,14 +69,15 @@ def get_user(c, user_id):
|
||||
'time_played': time_played,
|
||||
'clear_type': x[21],
|
||||
'rating': x[22],
|
||||
'ticket': x[26]
|
||||
'ticket': x[26],
|
||||
'ban_flag': ban_flag
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def get_user_recent30(c, user_id):
|
||||
# 获取玩家recent30信息并计算这一部分的ptt,返回字典列表和一个值
|
||||
# 获取玩家recent30信息并计算recent10的ptt,返回字典列表和一个值
|
||||
c.execute('''select * from recent30 where user_id=:a''', {'a': user_id})
|
||||
sumr = 0
|
||||
x = c.fetchone()
|
||||
|
||||
Reference in New Issue
Block a user