mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-09 17:27:27 +08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a3ee0f0c7 | ||
|
|
a04df8bba6 | ||
|
|
fb110fd01c | ||
|
|
7a614eaa57 | ||
|
|
8f1c28eb5c | ||
|
|
299c8c582c | ||
|
|
6298b37ea2 | ||
|
|
b1575af0db | ||
|
|
7bf43c3e07 | ||
|
|
12cd187563 | ||
|
|
574d2c9d9f | ||
|
|
732947a3f0 | ||
|
|
27ee147989 | ||
|
|
b38dbf1a55 | ||
|
|
d852be8029 | ||
|
|
4f52df35ce | ||
|
|
cbd2198e43 | ||
|
|
cdf76fb93a | ||
|
|
5fba93524c | ||
|
|
93f4ad4999 | ||
|
|
47f05cdf1e | ||
|
|
dbfed37dda | ||
|
|
6801af197f | ||
|
|
210ba99606 | ||
|
|
0705c35be8 | ||
|
|
c9bcbb8ec4 | ||
|
|
ac0071bfd6 | ||
|
|
ef8e551261 |
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Please use a clear and concise title 能不能来个正常点的标题 -->
|
||||||
|
<!-- English or Chinese is ok 中英皆可 -->
|
||||||
|
|
||||||
|
**Describe the bug 问题描述**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce 复现过程**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Error log 报错日志**
|
||||||
|
<!-- Including logs, texts in CMD, some messages in some softwares... -->
|
||||||
|
```
|
||||||
|
[Error] Balabalabalabala...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Screenshots 截图**
|
||||||
|
<!-- Well, if you have error logs, pictures are optional. It is too slow for me to open a picture. -->
|
||||||
|
<!-- 有错误日志就别放图了,网速有多慢你懂的 -->
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Environment 环境**
|
||||||
|
- Server OS: [e.g. Windows 11 Beta]
|
||||||
|
- Client OS: [e.g. Android 10]
|
||||||
|
- Server Version: [e.g. v2.8.1]
|
||||||
|
- Client Version [e.g. 4.0.256c]
|
||||||
|
- Others like python version, flask version, some proxy softwares if you used...
|
||||||
|
|
||||||
|
**Additional context 附加内容**
|
||||||
|
Add any other context about the problem here.
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
14
.github/ISSUE_TEMPLATE/q-a.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/q-a.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: Q&A
|
||||||
|
about: Ask a question or wonder something
|
||||||
|
title: ''
|
||||||
|
labels: Q&A
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**What you want to know 你想知道什么**
|
||||||
|
Clearly describe your question here.
|
||||||
|
|
||||||
|
**Additional context 附加内容**
|
||||||
|
Add anything other here. Your abilities or knowledge base, your contact information, some pictures...
|
||||||
57
README.md
57
README.md
@@ -67,38 +67,39 @@ It is just so interesting. What it can do is under exploration.
|
|||||||
> Tips: When updating, please keep the original database in case of data loss.
|
> Tips: When updating, please keep the original database in case of data loss.
|
||||||
|
|
||||||
|
|
||||||
### Version 2.9
|
### Version 2.10.0
|
||||||
- 适用于Arcaea 4.0.0版本 For Arcaea 4.0.0
|
- 适用于Arcaea 4.1.0版本 For Arcaea 4.1.0
|
||||||
- 新增段位/课题模式 Add Dan/Course Mode.
|
- 新搭档 **咲姬** 已解锁 Unlock the character **Saki**.
|
||||||
- 新搭档 **光(Fatalis)** 已解锁 Unlock the character **Hikari(Fatalis)**.
|
- 新搭档 **刹那** 已解锁 Unlock the character **Setsuna**.
|
||||||
- 新增对`光(Fatalis)`的技能的支持 Add support for the skill of `Hikari(Fatalis)`.
|
- 完善了日志系统 Improve the log system.
|
||||||
- 新增对额外文件下载的支持 Add support for downloading additional files.
|
- 现在可以利用`songlist`确保`3.aff`以外文件不被下载 Now you can use `songlist` to ensure that files other than `3.aff` should not be downloaded. [#60](https://github.com/Lost-MSth/Arcaea-server/issues/60)
|
||||||
- 新增对`结局挑战`的云端存档的支持 Add support for the cloud save of `finale challenge`.
|
- 适配v4.0.0以下版本的客户端云存档 Ensure that the clients under v4.0.0 can upload the cloud save.
|
||||||
- 修复`Link Play`模式下当房主退出时,新房主没有立即显示的问题 Fix a bug that the other player will not become the host of the room at once, when the player disconnect in `Link Play`.
|
- 优化数据库索引 Optimize database indices.
|
||||||
- 修改了`Link Play`模式的一些时间常数 Change some constants of time in `Link Play` mode.
|
- 尝试确保LinkPlay服务器的线程安全,现在此功能将作为独立服务端 Try to ensure thread safety in LinkPlay server. Now this function will be served as an independent server.
|
||||||
- 重构一些代码,可能引入了新Bug Refactoring some codes and there may be more bugs.
|
- 对API接口的分数列表添加歌曲名 Add song names for getting the score list in API.
|
||||||
|
- 为下载错误增添HTTP状态码 Add HTTP status code when meeting download error.
|
||||||
- 以下是累积更新 The following are cumulative updates:
|
|
||||||
- 修复一个`v2.8.5`版本引入的有关无法加好友的问题 Fix a bug of unable to add friend which arose in the `version 2.8.5`.
|
|
||||||
- 修复`Link Play`模式下重复计算歌曲完成状态的问题 Fix a bug of duplicate calculating finishing states when players finish playing the chart in `Link Play`.
|
|
||||||
|
|
||||||
> 注意:
|
|
||||||
> - 不再提供歌曲数据
|
|
||||||
> - 目前尽量不会对前端web页面增补功能
|
|
||||||
> - `对立`不会死
|
|
||||||
>
|
|
||||||
> Note:
|
|
||||||
> - No longer provide song data.
|
|
||||||
> - Since now, probably not add functions to the front-end web pages.
|
|
||||||
> - `Tairitsu` will not die.
|
|
||||||
|
|
||||||
|
|
||||||
|
- 修复iOS客户端因世界模式地图数据闪退的问题 Fix a bug when world maps' data don't have some unnecessary parts the client of iOS may break down.
|
||||||
|
- 修复API接口无数据`GET`请求导致报错的问题 Fix a bug that `GET` requests without data will report an error in API. [#50](https://github.com/Lost-MSth/Arcaea-server/issues/50)
|
||||||
|
- 修复`aggregate`请求无法捕获内部错误的问题 Fix a bug that `aggregate` requests will get an error when the inner function raises an error.
|
||||||
|
- 修复因错误设置主键导致课程模式谱面无法相同的问题 Fix a bug that the charts of a course cannot be the same because of the incorrect primary keys.
|
||||||
|
- 修复无谱面数据时世界排名分计算出错的问题 Fix a bug that global ranking scores cannot be calculated if there are no chart in the database. [#61](https://github.com/Lost-MSth/Arcaea-server/issues/61)
|
||||||
|
- 修复条件满足但隐藏段位依然无法解锁的问题 Fix a bug that the hidden courses cannot appear even if their requirements are satisfied.
|
||||||
|
- 修复Finale挑战中某些无法解锁的问题 Fix a bug that something of the finale challenge cannot be unlocked.
|
||||||
|
- 修复用户物品数量无法为0的问题,此问题导致了一些购买操作异常 Fix a bug that the users' items will not be zero, which will disturb some purchase operations.
|
||||||
|
- 修复角色等级能超过最大等级的问题 Fix a bug that the level of the character can exceed the max level.
|
||||||
|
- 修复使用`以太之滴`升级角色时应答不正确的问题 Fix a bug that the response is incorrect when upgrading the characters by `generic core`.
|
||||||
|
- 修复`源韵强化`数值显示不正确的问题 Fix a bug that the `prog boost` shows the incorrect value.
|
||||||
|
- 修复世界模式奖励可能被重复发放的问题 Fix a bug that the rewards can be get repeatedly in World Mode.
|
||||||
|
- 修复世界Boss的第二管血量无法削减的问题 Fix a bug that second tube of blood of the world boss won't change.
|
||||||
|
- 修复我的排名显示不正确的问题 Fix a bug that `my rank` doesn't work correctly.
|
||||||
|
- 修复在歌曲结束后无法及时轮换房主的问题 Fix a bug that the room host will be changed late when finishing a song.
|
||||||
|
|
||||||
|
|
||||||
## 运行环境与依赖 Running environment and requirements
|
## 运行环境与依赖 Running environment and requirements
|
||||||
- Windows/Linux/Mac OS/Android...
|
- Windows/Linux/Mac OS/Android...
|
||||||
- Python 3
|
- Python 3
|
||||||
- Flask module >= 2.0, Cryptography module
|
- Flask module >= 2.0, Cryptography module >= 35.0.0
|
||||||
- Charles, IDA, proxy app... (optional)
|
- Charles, IDA, proxy app... (optional)
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -116,6 +117,10 @@ It is just so interesting. What it can do is under exploration.
|
|||||||
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Attentions)
|
[English](https://github.com/Lost-MSth/Arcaea-server/wiki/Attentions)
|
||||||
|
|
||||||
|
|
||||||
|
## Q&A
|
||||||
|
[中文/English](https://github.com/Lost-MSth/Arcaea-server/wiki/Q&A)
|
||||||
|
|
||||||
|
|
||||||
## 鸣谢 Thanks
|
## 鸣谢 Thanks
|
||||||
~~歌曲数据库来自 Using song database from
|
~~歌曲数据库来自 Using song database from
|
||||||
[BotArcAPI releases](https://github.com/TheSnowfield/BotArcAPI/releases)~~
|
[BotArcAPI releases](https://github.com/TheSnowfield/BotArcAPI/releases)~~
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import functools
|
from functools import wraps
|
||||||
|
from traceback import format_exc
|
||||||
|
|
||||||
from core.api_user import APIUser
|
from core.api_user import APIUser
|
||||||
from core.error import ArcError, NoAccess, PostError
|
from core.error import ArcError, NoAccess, PostError
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
|
from flask import current_app
|
||||||
from setting import Config
|
from setting import Config
|
||||||
|
|
||||||
from .api_code import error_return
|
from .api_code import error_return
|
||||||
@@ -11,9 +13,10 @@ from .api_code import error_return
|
|||||||
def role_required(request, powers=[]):
|
def role_required(request, powers=[]):
|
||||||
'''api token验证,写成了修饰器'''
|
'''api token验证,写成了修饰器'''
|
||||||
def decorator(view):
|
def decorator(view):
|
||||||
@functools.wraps(view)
|
@wraps(view)
|
||||||
def wrapped_view(*args, **kwargs):
|
def wrapped_view(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
|
if request.data:
|
||||||
request.json # 检查请求json格式
|
request.json # 检查请求json格式
|
||||||
except:
|
except:
|
||||||
return error_return(PostError('Payload must be a valid json', api_error_code=-1), 400)
|
return error_return(PostError('Payload must be a valid json', api_error_code=-1), 400)
|
||||||
@@ -56,10 +59,13 @@ def request_json_handle(request, required_keys=[], optional_keys=[]):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
def decorator(view):
|
def decorator(view):
|
||||||
@functools.wraps(view)
|
@wraps(view)
|
||||||
def wrapped_view(*args, **kwargs):
|
def wrapped_view(*args, **kwargs):
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
|
if not request.data:
|
||||||
|
return view(data, *args, **kwargs)
|
||||||
|
|
||||||
for key in required_keys:
|
for key in required_keys:
|
||||||
if key not in request.json:
|
if key not in request.json:
|
||||||
return error_return(PostError('Missing parameter: ' + key, api_error_code=-100))
|
return error_return(PostError('Missing parameter: ' + key, api_error_code=-100))
|
||||||
@@ -73,3 +79,21 @@ def request_json_handle(request, required_keys=[], optional_keys=[]):
|
|||||||
|
|
||||||
return wrapped_view
|
return wrapped_view
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def api_try(view):
|
||||||
|
'''替代try/except,记录`ArcError`为warning'''
|
||||||
|
@wraps(view)
|
||||||
|
def wrapped_view(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
data = view(*args, **kwargs)
|
||||||
|
if data is None:
|
||||||
|
return error_return()
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
except ArcError as e:
|
||||||
|
if Config.ALLOW_WARNING_LOG:
|
||||||
|
current_app.logger.warning(format_exc())
|
||||||
|
return error_return(e, e.status)
|
||||||
|
|
||||||
|
return wrapped_view
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from core.error import ArcError, NoData
|
from core.error import NoData
|
||||||
from core.song import Song
|
from core.song import Song
|
||||||
from core.sql import Connect, Query, Sql
|
from core.sql import Connect, Query, Sql
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .api_auth import request_json_handle, role_required
|
from .api_auth import api_try, request_json_handle, role_required
|
||||||
from .api_code import error_return, success_return
|
from .api_code import success_return
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
|
|
||||||
bp = Blueprint('songs', __name__, url_prefix='/songs')
|
bp = Blueprint('songs', __name__, url_prefix='/songs')
|
||||||
@@ -12,27 +12,24 @@ bp = Blueprint('songs', __name__, url_prefix='/songs')
|
|||||||
|
|
||||||
@bp.route('/<string:song_id>', methods=['GET'])
|
@bp.route('/<string:song_id>', methods=['GET'])
|
||||||
@role_required(request, ['select', 'select_song_info'])
|
@role_required(request, ['select', 'select_song_info'])
|
||||||
|
@api_try
|
||||||
def songs_song_get(user, song_id):
|
def songs_song_get(user, song_id):
|
||||||
'''查询歌曲信息'''
|
'''查询歌曲信息'''
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
s = Song(c, song_id).select()
|
s = Song(c, song_id).select()
|
||||||
return success_return(s.to_dict())
|
return success_return(s.to_dict())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('', methods=['GET'])
|
@bp.route('', methods=['GET'])
|
||||||
@role_required(request, ['select', 'select_song_info'])
|
@role_required(request, ['select', 'select_song_info'])
|
||||||
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||||
|
@api_try
|
||||||
def songs_get(data, user):
|
def songs_get(data, user):
|
||||||
'''查询全歌曲信息'''
|
'''查询全歌曲信息'''
|
||||||
A = ['song_id', 'name']
|
A = ['song_id', 'name']
|
||||||
B = ['song_id', 'name', 'rating_pst',
|
B = ['song_id', 'name', 'rating_pst',
|
||||||
'rating_prs', 'rating_ftr', 'rating_byn']
|
'rating_prs', 'rating_ftr', 'rating_byn']
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
query = Query(A, A, B).from_data(data)
|
query = Query(A, A, B).from_data(data)
|
||||||
x = Sql(c).select('chart', query=query)
|
x = Sql(c).select('chart', query=query)
|
||||||
r = []
|
r = []
|
||||||
@@ -43,6 +40,3 @@ def songs_get(data, user):
|
|||||||
raise NoData(api_error_code=-2)
|
raise NoData(api_error_code=-2)
|
||||||
|
|
||||||
return success_return([x.to_dict() for x in r])
|
return success_return([x.to_dict() for x in r])
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
|
||||||
from core.api_user import APIUser
|
from core.api_user import APIUser
|
||||||
from core.error import ArcError, PostError
|
from core.error import PostError
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .api_auth import request_json_handle, role_required
|
from .api_auth import api_try, request_json_handle, role_required
|
||||||
from .api_code import error_return, success_return
|
from .api_code import success_return
|
||||||
|
|
||||||
bp = Blueprint('token', __name__, url_prefix='/token')
|
bp = Blueprint('token', __name__, url_prefix='/token')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('', methods=['POST'])
|
@bp.route('', methods=['POST'])
|
||||||
@request_json_handle(request, required_keys=['auth'])
|
@request_json_handle(request, required_keys=['auth'])
|
||||||
|
@api_try
|
||||||
def token_post(data):
|
def token_post(data):
|
||||||
'''
|
'''
|
||||||
登录,获取token\
|
登录,获取token\
|
||||||
@@ -21,23 +22,20 @@ def token_post(data):
|
|||||||
try:
|
try:
|
||||||
auth_decode = bytes.decode(b64decode(data['auth']))
|
auth_decode = bytes.decode(b64decode(data['auth']))
|
||||||
except:
|
except:
|
||||||
return error_return(PostError(api_error_code=-100))
|
raise PostError(api_error_code=-100)
|
||||||
if not ':' in auth_decode:
|
if not ':' in auth_decode:
|
||||||
return error_return(PostError(api_error_code=-100))
|
raise PostError(api_error_code=-100)
|
||||||
name, password = auth_decode.split(':', 1)
|
name, password = auth_decode.split(':', 1)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = APIUser(c)
|
user = APIUser(c)
|
||||||
user.login(name, password, request.remote_addr)
|
user.login(name, password, request.remote_addr)
|
||||||
return success_return({'token': user.token, 'user_id': user.user_id})
|
return success_return({'token': user.token, 'user_id': user.user_id})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('', methods=['GET'])
|
@bp.route('', methods=['GET'])
|
||||||
@role_required(request, ['select_me', 'select'])
|
@role_required(request, ['select_me', 'select'])
|
||||||
|
@api_try
|
||||||
def token_get(user):
|
def token_get(user):
|
||||||
'''判断登录有效性'''
|
'''判断登录有效性'''
|
||||||
return success_return()
|
return success_return()
|
||||||
@@ -45,13 +43,10 @@ def token_get(user):
|
|||||||
|
|
||||||
@bp.route('', methods=['DELETE'])
|
@bp.route('', methods=['DELETE'])
|
||||||
@role_required(request, ['change_me', 'select_me', 'select'])
|
@role_required(request, ['change_me', 'select_me', 'select'])
|
||||||
|
@api_try
|
||||||
def token_delete(user):
|
def token_delete(user):
|
||||||
'''登出'''
|
'''登出'''
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user.c = c
|
user.c = c
|
||||||
user.logout()
|
user.logout()
|
||||||
return success_return()
|
return success_return()
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from core.error import ArcError, InputError, NoAccess, NoData
|
from core.error import InputError, NoAccess, NoData
|
||||||
from core.score import Potential, UserScoreList
|
from core.score import Potential, UserScoreList
|
||||||
from core.sql import Connect, Query, Sql
|
from core.sql import Connect, Query, Sql
|
||||||
from core.user import UserInfo, UserRegister
|
from core.user import UserInfo, UserRegister
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .api_auth import request_json_handle, role_required
|
from .api_auth import api_try, request_json_handle, role_required
|
||||||
from .api_code import error_return, success_return
|
from .api_code import error_return, success_return
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
|
|
||||||
@@ -14,31 +14,28 @@ bp = Blueprint('users', __name__, url_prefix='/users')
|
|||||||
@bp.route('', methods=['POST'])
|
@bp.route('', methods=['POST'])
|
||||||
@role_required(request, ['change'])
|
@role_required(request, ['change'])
|
||||||
@request_json_handle(request, ['name', 'password', 'email'])
|
@request_json_handle(request, ['name', 'password', 'email'])
|
||||||
|
@api_try
|
||||||
def users_post(data, _):
|
def users_post(data, _):
|
||||||
'''注册一个用户'''
|
'''注册一个用户'''
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
new_user = UserRegister(c)
|
new_user = UserRegister(c)
|
||||||
try:
|
|
||||||
new_user.set_name(data['name'])
|
new_user.set_name(data['name'])
|
||||||
new_user.set_password(data['password'])
|
new_user.set_password(data['password'])
|
||||||
new_user.set_email(data['email'])
|
new_user.set_email(data['email'])
|
||||||
new_user.register()
|
new_user.register()
|
||||||
return success_return({'user_id': new_user.user_id, 'user_code': new_user.user_code})
|
return success_return({'user_id': new_user.user_id, 'user_code': new_user.user_code})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('', methods=['GET'])
|
@bp.route('', methods=['GET'])
|
||||||
@role_required(request, ['select'])
|
@role_required(request, ['select'])
|
||||||
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||||
|
@api_try
|
||||||
def users_get(data, user):
|
def users_get(data, user):
|
||||||
'''查询全用户信息'''
|
'''查询全用户信息'''
|
||||||
A = ['user_id', 'name', 'user_code']
|
A = ['user_id', 'name', 'user_code']
|
||||||
B = ['user_id', 'name', 'user_code', 'join_date',
|
B = ['user_id', 'name', 'user_code', 'join_date',
|
||||||
'rating_ptt', 'time_played', 'ticket', 'world_rank_score']
|
'rating_ptt', 'time_played', 'ticket', 'world_rank_score']
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
query = Query(A, A, B).from_data(data)
|
query = Query(A, A, B).from_data(data)
|
||||||
x = Sql(c).select('user', query=query)
|
x = Sql(c).select('user', query=query)
|
||||||
r = []
|
r = []
|
||||||
@@ -60,13 +57,11 @@ def users_get(data, user):
|
|||||||
'is_hide_rating': x.is_hide_rating,
|
'is_hide_rating': x.is_hide_rating,
|
||||||
'ticket': x.ticket
|
'ticket': x.ticket
|
||||||
} for x in r])
|
} for x in r])
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<int:user_id>', methods=['GET'])
|
@bp.route('/<int:user_id>', methods=['GET'])
|
||||||
@role_required(request, ['select', 'select_me'])
|
@role_required(request, ['select', 'select_me'])
|
||||||
|
@api_try
|
||||||
def users_user_get(user, user_id):
|
def users_user_get(user, user_id):
|
||||||
'''查询用户信息'''
|
'''查询用户信息'''
|
||||||
if user_id <= 0:
|
if user_id <= 0:
|
||||||
@@ -76,16 +71,13 @@ def users_user_get(user, user_id):
|
|||||||
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
u = UserInfo(c, user_id)
|
u = UserInfo(c, user_id)
|
||||||
return success_return(u.to_dict())
|
return success_return(u.to_dict())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<int:user_id>/b30', methods=['GET'])
|
@bp.route('/<int:user_id>/b30', methods=['GET'])
|
||||||
@role_required(request, ['select', 'select_me'])
|
@role_required(request, ['select', 'select_me'])
|
||||||
|
@api_try
|
||||||
def users_user_b30_get(user, user_id):
|
def users_user_b30_get(user, user_id):
|
||||||
'''查询用户b30'''
|
'''查询用户b30'''
|
||||||
if user_id <= 0:
|
if user_id <= 0:
|
||||||
@@ -95,21 +87,19 @@ def users_user_b30_get(user, user_id):
|
|||||||
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = UserScoreList(c, UserInfo(c, user_id))
|
x = UserScoreList(c, UserInfo(c, user_id))
|
||||||
x.query.limit = 30
|
x.query.limit = 30
|
||||||
x.select_from_user()
|
x.select_from_user()
|
||||||
|
x.select_song_name()
|
||||||
r = x.to_dict_list()
|
r = x.to_dict_list()
|
||||||
rating_sum = sum([i.rating for i in x.scores])
|
rating_sum = sum([i.rating for i in x.scores])
|
||||||
return success_return({'user_id': user_id, 'b30_ptt': rating_sum / 30, 'data': r})
|
return success_return({'user_id': user_id, 'b30_ptt': rating_sum / 30, 'data': r})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<int:user_id>/best', methods=['GET'])
|
@bp.route('/<int:user_id>/best', methods=['GET'])
|
||||||
@role_required(request, ['select', 'select_me'])
|
@role_required(request, ['select', 'select_me'])
|
||||||
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
|
||||||
|
@api_try
|
||||||
def users_user_best_get(data, user, user_id):
|
def users_user_best_get(data, user, user_id):
|
||||||
'''查询用户所有best成绩'''
|
'''查询用户所有best成绩'''
|
||||||
if user_id <= 0:
|
if user_id <= 0:
|
||||||
@@ -119,19 +109,16 @@ def users_user_best_get(data, user, user_id):
|
|||||||
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = UserScoreList(c, UserInfo(c, user_id))
|
x = UserScoreList(c, UserInfo(c, user_id))
|
||||||
x.query.from_data(data)
|
x.query.from_data(data)
|
||||||
x.select_from_user()
|
x.select_from_user()
|
||||||
r = x.to_dict_list()
|
r = x.to_dict_list()
|
||||||
return success_return({'user_id': user_id, 'data': r})
|
return success_return({'user_id': user_id, 'data': r})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/<int:user_id>/r30', methods=['GET'])
|
@bp.route('/<int:user_id>/r30', methods=['GET'])
|
||||||
@role_required(request, ['select', 'select_me'])
|
@role_required(request, ['select', 'select_me'])
|
||||||
|
@api_try
|
||||||
def users_user_r30_get(user, user_id):
|
def users_user_r30_get(user, user_id):
|
||||||
'''查询用户r30'''
|
'''查询用户r30'''
|
||||||
|
|
||||||
@@ -142,9 +129,5 @@ def users_user_r30_get(user, user_id):
|
|||||||
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
return error_return(NoAccess('No permission', api_error_code=-1), 403)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
p = Potential(c, UserInfo(c, user_id))
|
p = Potential(c, UserInfo(c, user_id))
|
||||||
return success_return({'user_id': user_id, 'r10_ptt': p.recent_10 / 10, 'data': p.recent_30_to_dict_list()})
|
return success_return({'user_id': user_id, 'r10_ptt': p.recent_10 / 10, 'data': p.recent_30_to_dict_list()})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class Level:
|
|||||||
if exp >= Constant.LEVEL_STEPS[self.max_level]:
|
if exp >= Constant.LEVEL_STEPS[self.max_level]:
|
||||||
self.exp = Constant.LEVEL_STEPS[self.max_level]
|
self.exp = Constant.LEVEL_STEPS[self.max_level]
|
||||||
self.level = self.max_level
|
self.level = self.max_level
|
||||||
|
return None
|
||||||
|
|
||||||
a = []
|
a = []
|
||||||
b = []
|
b = []
|
||||||
@@ -177,8 +178,10 @@ class UserCharacter(Character):
|
|||||||
|
|
||||||
x = self.c.fetchone()
|
x = self.c.fetchone()
|
||||||
if not x:
|
if not x:
|
||||||
raise NoData('The character of the user does not exist.')
|
self.is_uncapped = False
|
||||||
|
self.is_uncapped_override = False
|
||||||
|
# raise NoData('The character of the user does not exist.')
|
||||||
|
else:
|
||||||
self.is_uncapped = x[0] == 1
|
self.is_uncapped = x[0] == 1
|
||||||
self.is_uncapped_override = x[1] == 1
|
self.is_uncapped_override = x[1] == 1
|
||||||
|
|
||||||
@@ -227,8 +230,8 @@ class UserCharacter(Character):
|
|||||||
"skill_unlock_level": self.skill.skill_unlock_level,
|
"skill_unlock_level": self.skill.skill_unlock_level,
|
||||||
"skill_id": self.skill.skill_id,
|
"skill_id": self.skill.skill_id,
|
||||||
"overdrive": self.overdrive.get_value(self.level),
|
"overdrive": self.overdrive.get_value(self.level),
|
||||||
"prog": self.overdrive.get_value(self.level),
|
"prog": self.prog.get_value(self.level),
|
||||||
"frag": self.overdrive.get_value(self.level),
|
"frag": self.frag.get_value(self.level),
|
||||||
"level_exp": self.level.level_exp,
|
"level_exp": self.level.level_exp,
|
||||||
"exp": self.level.exp,
|
"exp": self.level.exp,
|
||||||
"level": self.level.level,
|
"level": self.level.level,
|
||||||
@@ -238,7 +241,7 @@ class UserCharacter(Character):
|
|||||||
if self.voice:
|
if self.voice:
|
||||||
r['voice'] = self.voice
|
r['voice'] = self.voice
|
||||||
if self.character_id == 55:
|
if self.character_id == 55:
|
||||||
r['fatalis_is_limited'] = True # emmmmmmm
|
r['fatalis_is_limited'] = False # emmmmmmm
|
||||||
if self.character_id in [1, 6, 7, 17, 18, 24, 32, 35, 52]:
|
if self.character_id in [1, 6, 7, 17, 18, 24, 32, 35, 52]:
|
||||||
r['base_character_id'] = 1
|
r['base_character_id'] = 1
|
||||||
return r
|
return r
|
||||||
|
|||||||
@@ -21,25 +21,34 @@ class Constant:
|
|||||||
|
|
||||||
MAX_FRIEND_COUNT = 50
|
MAX_FRIEND_COUNT = 50
|
||||||
|
|
||||||
|
MY_RANK_MAX_LOCAL_POSITION = 5
|
||||||
|
MY_RANK_MAX_GLOBAL_POSITION = 9999
|
||||||
|
|
||||||
# You can change this to make another PTT mechanism.
|
# You can change this to make another PTT mechanism.
|
||||||
BEST30_WEIGHT = 1 / 40
|
BEST30_WEIGHT = 1 / 40
|
||||||
RECENT10_WEIGHT = 1 / 40
|
RECENT10_WEIGHT = 1 / 40
|
||||||
|
|
||||||
WORLD_MAP_FOLDER_PATH = './database/map/'
|
WORLD_MAP_FOLDER_PATH = './database/map/'
|
||||||
SONG_FILE_FOLDER_PATH = './database/songs/'
|
SONG_FILE_FOLDER_PATH = './database/songs/'
|
||||||
|
SONGLIST_FILE_PATH = './database/songs/songlist'
|
||||||
SQLITE_DATABASE_PATH = './database/arcaea_database.db'
|
SQLITE_DATABASE_PATH = './database/arcaea_database.db'
|
||||||
|
|
||||||
DOWNLOAD_TIMES_LIMIT = Config.DOWNLOAD_TIMES_LIMIT
|
DOWNLOAD_TIMES_LIMIT = Config.DOWNLOAD_TIMES_LIMIT
|
||||||
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
|
DOWNLOAD_TIME_GAP_LIMIT = Config.DOWNLOAD_TIME_GAP_LIMIT
|
||||||
DOWNLOAD_LINK_PREFIX = Config.DOWNLOAD_LINK_PREFIX
|
DOWNLOAD_LINK_PREFIX = Config.DOWNLOAD_LINK_PREFIX
|
||||||
|
|
||||||
LINK_PLAY_UNLOCK_LENGTH = 512 # Units: bytes
|
LINKPLAY_UNLOCK_LENGTH = 512 # Units: bytes
|
||||||
LINK_PLAY_TIMEOUT = 10 # Units: seconds
|
LINKPLAY_TIMEOUT = 5 # Units: seconds
|
||||||
|
|
||||||
|
LINKPLAY_HOST = '127.0.0.1' if Config.SET_LINKPLAY_SERVER_AS_SUB_PROCESS else Config.LINKPLAY_HOST
|
||||||
|
LINKPLAY_TCP_PORT = Config.LINKPLAY_TCP_PORT
|
||||||
|
LINKPLAY_UDP_PORT = Config.LINKPLAY_UDP_PORT
|
||||||
|
LINKPLAY_AUTHENTICATION = Config.LINKPLAY_AUTHENTICATION
|
||||||
|
|
||||||
COURSE_STAMINA_COST = 4
|
COURSE_STAMINA_COST = 4
|
||||||
|
|
||||||
# Well, I can't say a word when I see this.
|
# Well, I can't say a word when I see this.
|
||||||
SHIT_DATA_OF_616 = [
|
FINALE_SWITCH = [
|
||||||
(0x0015F0, 0x00B032), (0x014C9A, 0x014408), (0x062585, 0x02783B),
|
(0x0015F0, 0x00B032), (0x014C9A, 0x014408), (0x062585, 0x02783B),
|
||||||
(0x02429E, 0x0449A4), (0x099C9C,
|
(0x02429E, 0x0449A4), (0x099C9C,
|
||||||
0x07CFB4), (0x0785BF, 0x019B2C),
|
0x07CFB4), (0x0785BF, 0x019B2C),
|
||||||
@@ -78,7 +87,9 @@ class Constant:
|
|||||||
(0x4B05EC, 0x97FDFE), (0x207258,
|
(0x4B05EC, 0x97FDFE), (0x207258,
|
||||||
0x02BB9B), (0x20A9EE, 0x1BA4BB),
|
0x02BB9B), (0x20A9EE, 0x1BA4BB),
|
||||||
(0x503D21, 0x6A41D0), (0x1C256C,
|
(0x503D21, 0x6A41D0), (0x1C256C,
|
||||||
0x6DD3BC), (0x6E4E0C, 0x89FDAA), (0x3C5F95, 0x3BA786)
|
0x6DD3BC), (0x6E4E0C, 0x89FDAA), (0x3C5F95, 0x3BA786),
|
||||||
|
(0XFEA5, 0x2e4ca), (0X7BF653, 0x4befd11), (0X46BEA7B,
|
||||||
|
0x11d3684), (0X8BFB04, 0xa83d6c1),
|
||||||
|
(0X5D6FC5, 0xab97ef), (0X237206D, 0xdfef2), (0XA3DEE,
|
||||||
|
0x6CB300), (0XA35687B, 0xE456CDEA)
|
||||||
]
|
]
|
||||||
UNKNOWN_SHIT = [0XFEA5, 0X7BF653, 0X46BEA7B,
|
|
||||||
0X8BFB04, 0X5D6FC5, 0X237206D, 0XA3DEE, 0XA35687B]
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
from .error import NoData
|
from .error import NoData
|
||||||
from .song import Chart
|
|
||||||
from .item import ItemFactory
|
from .item import ItemFactory
|
||||||
|
from .song import Chart
|
||||||
|
|
||||||
|
|
||||||
class CourseChart(Chart):
|
class CourseChart(Chart):
|
||||||
@@ -213,7 +211,18 @@ class UserCourseList:
|
|||||||
self.courses: list = []
|
self.courses: list = []
|
||||||
|
|
||||||
def to_dict_list(self) -> list:
|
def to_dict_list(self) -> list:
|
||||||
return [x.to_dict() for x in self.courses]
|
# 临时修复满足条件也无法解锁隐藏段位的问题
|
||||||
|
completed_course_id_list: list = []
|
||||||
|
r: list = []
|
||||||
|
for x in self.courses:
|
||||||
|
if x.is_completed:
|
||||||
|
completed_course_id_list.append(x.course_id)
|
||||||
|
r.append(x.to_dict())
|
||||||
|
for x in r:
|
||||||
|
for i in x['requirements']:
|
||||||
|
if i['value'] in completed_course_id_list:
|
||||||
|
i['is_fullfilled'] = True
|
||||||
|
return r
|
||||||
|
|
||||||
def select_all(self) -> None:
|
def select_all(self) -> None:
|
||||||
self.c.execute('''select * from course''')
|
self.c.execute('''select * from course''')
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
from json import loads
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
|
from flask import url_for
|
||||||
|
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
from .error import NoAccess
|
from .error import NoAccess
|
||||||
from .user import User
|
from .user import User
|
||||||
@@ -25,6 +28,22 @@ def initialize_songfile():
|
|||||||
del x
|
del x
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def get_only_3_song_ids():
|
||||||
|
'''初始化只能下载byd相关的歌曲id'''
|
||||||
|
if not os.path.isfile(Constant.SONGLIST_FILE_PATH):
|
||||||
|
return []
|
||||||
|
only_3_song_ids = []
|
||||||
|
data = []
|
||||||
|
with open(Constant.SONGLIST_FILE_PATH, 'r', encoding='utf-8') as f:
|
||||||
|
data = loads(f.read())['songs']
|
||||||
|
for x in data:
|
||||||
|
if 'remote_dl' not in x or 'remote_dl' in x and not x['remote_dl']:
|
||||||
|
if any(i['ratingClass'] == 3 for i in x['difficulties']):
|
||||||
|
only_3_song_ids.append(x['id'])
|
||||||
|
return only_3_song_ids
|
||||||
|
|
||||||
|
|
||||||
class UserDownload:
|
class UserDownload:
|
||||||
'''
|
'''
|
||||||
用户下载类\
|
用户下载类\
|
||||||
@@ -38,7 +57,6 @@ class UserDownload:
|
|||||||
self.song_id: str = None
|
self.song_id: str = None
|
||||||
self.file_name: str = None
|
self.file_name: str = None
|
||||||
|
|
||||||
self.file_path: str = None
|
|
||||||
self.token: str = None
|
self.token: str = None
|
||||||
self.token_time: int = None
|
self.token_time: int = None
|
||||||
|
|
||||||
@@ -49,6 +67,8 @@ class UserDownload:
|
|||||||
@property
|
@property
|
||||||
def is_limited(self) -> bool:
|
def is_limited(self) -> bool:
|
||||||
'''是否达到用户最大下载量'''
|
'''是否达到用户最大下载量'''
|
||||||
|
if self.user is None:
|
||||||
|
self.select_for_check()
|
||||||
self.c.execute(
|
self.c.execute(
|
||||||
'''select count(*) from user_download where user_id = :a''', {'a': self.user.user_id})
|
'''select count(*) from user_download where user_id = :a''', {'a': self.user.user_id})
|
||||||
y = self.c.fetchone()
|
y = self.c.fetchone()
|
||||||
@@ -57,27 +77,26 @@ class UserDownload:
|
|||||||
@property
|
@property
|
||||||
def is_valid(self) -> bool:
|
def is_valid(self) -> bool:
|
||||||
'''链接是否有效且未过期'''
|
'''链接是否有效且未过期'''
|
||||||
return int(time()) - self.token_time <= Constant.DOWNLOAD_TIME_GAP_LIMIT and self.song_id+'/'+self.file_name == self.file_path
|
if self.token_time is None:
|
||||||
|
self.select_for_check()
|
||||||
|
return int(time()) - self.token_time <= Constant.DOWNLOAD_TIME_GAP_LIMIT
|
||||||
|
|
||||||
def insert_user_download(self) -> None:
|
def insert_user_download(self) -> None:
|
||||||
'''记录下载信息'''
|
'''记录下载信息'''
|
||||||
self.c.execute('''insert into user_download values(:a,:b,:c)''', {
|
self.c.execute('''insert into user_download values(:a,:b,:c)''', {
|
||||||
'a': self.user.user_id, 'b': self.token, 'c': int(time())})
|
'a': self.user.user_id, 'c': self.token, 'b': int(time())})
|
||||||
|
|
||||||
def select_from_token(self, token: str = None) -> None:
|
def select_for_check(self) -> None:
|
||||||
if token is not None:
|
'''利用token、song_id、file_name查询其它信息'''
|
||||||
self.token = token
|
self.c.execute('''select user_id, time from download_token where song_id=? and file_name=? and token = ? limit 1;''',
|
||||||
self.c.execute('''select * from download_token where token = :t limit 1''',
|
(self.song_id, self.file_name, self.token))
|
||||||
{'t': self.token})
|
|
||||||
|
|
||||||
x = self.c.fetchone()
|
x = self.c.fetchone()
|
||||||
if not x:
|
if not x:
|
||||||
raise NoAccess('The token `%s` is not valid.' % self.token)
|
raise NoAccess('The token `%s` is not valid.' % self.token, status=403)
|
||||||
self.user = User()
|
self.user = User()
|
||||||
self.user.user_id = x[0]
|
self.user.user_id = x[0]
|
||||||
self.song_id = x[1]
|
self.token_time = x[1]
|
||||||
self.file_name = x[2]
|
|
||||||
self.token_time = x[4]
|
|
||||||
|
|
||||||
def generate_token(self) -> None:
|
def generate_token(self) -> None:
|
||||||
self.token_time = int(time())
|
self.token_time = int(time())
|
||||||
@@ -101,7 +120,6 @@ class UserDownload:
|
|||||||
prefix += '/'
|
prefix += '/'
|
||||||
return prefix + self.song_id + '/' + self.file_name + '?t=' + self.token
|
return prefix + self.song_id + '/' + self.file_name + '?t=' + self.token
|
||||||
else:
|
else:
|
||||||
from flask import url_for
|
|
||||||
return url_for('download', file_path=self.song_id + '/' + self.file_name, t=self.token, _external=True)
|
return url_for('download', file_path=self.song_id + '/' + self.file_name, t=self.token, _external=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -137,6 +155,8 @@ class DownloadList(UserDownload):
|
|||||||
re = {}
|
re = {}
|
||||||
for i in dir_list:
|
for i in dir_list:
|
||||||
if os.path.isfile(os.path.join(Constant.SONG_FILE_FOLDER_PATH, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg', 'video.mp4', 'video_audio.ogg']:
|
if os.path.isfile(os.path.join(Constant.SONG_FILE_FOLDER_PATH, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg', 'video.mp4', 'video_audio.ogg']:
|
||||||
|
if song_id in get_only_3_song_ids() and i not in ['3.aff', '3.ogg']:
|
||||||
|
continue
|
||||||
x = UserDownload(self.c, self.user)
|
x = UserDownload(self.c, self.user)
|
||||||
# self.downloads.append(x) # 这实际上没有用
|
# self.downloads.append(x) # 这实际上没有用
|
||||||
x.song_id = song_id
|
x.song_id = song_id
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
class ArcError(Exception):
|
class ArcError(Exception):
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
self.message = message
|
self.message = message
|
||||||
self.error_code = error_code
|
self.error_code = error_code
|
||||||
self.api_error_code = api_error_code
|
self.api_error_code = api_error_code
|
||||||
self.extra_data = extra_data
|
self.extra_data = extra_data
|
||||||
|
self.status = status
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return repr(self.message)
|
return repr(self.message)
|
||||||
@@ -11,8 +12,8 @@ class ArcError(Exception):
|
|||||||
|
|
||||||
class InputError(ArcError):
|
class InputError(ArcError):
|
||||||
# 输入类型错误
|
# 输入类型错误
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class DataExist(ArcError):
|
class DataExist(ArcError):
|
||||||
@@ -22,62 +23,62 @@ class DataExist(ArcError):
|
|||||||
|
|
||||||
class NoData(ArcError):
|
class NoData(ArcError):
|
||||||
# 数据不存在
|
# 数据不存在
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-2, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-2, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class PostError(ArcError):
|
class PostError(ArcError):
|
||||||
# 缺少输入
|
# 缺少输入
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-100, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class UserBan(ArcError):
|
class UserBan(ArcError):
|
||||||
# 用户封禁
|
# 用户封禁
|
||||||
def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=121, api_error_code=-202, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class ItemNotEnough(ArcError):
|
class ItemNotEnough(ArcError):
|
||||||
# 物品数量不足
|
# 物品数量不足
|
||||||
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class ItemUnavailable(ArcError):
|
class ItemUnavailable(ArcError):
|
||||||
# 物品不可用
|
# 物品不可用
|
||||||
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class RedeemUnavailable(ArcError):
|
class RedeemUnavailable(ArcError):
|
||||||
# 兑换码不可用
|
# 兑换码不可用
|
||||||
def __init__(self, message=None, error_code=505, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=505, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class MapLocked(ArcError):
|
class MapLocked(ArcError):
|
||||||
# 地图锁定
|
# 地图锁定
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class StaminaNotEnough(ArcError):
|
class StaminaNotEnough(ArcError):
|
||||||
# 体力不足
|
# 体力不足
|
||||||
def __init__(self, message=None, error_code=107, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=107, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class TicketNotEnough(ArcError):
|
class TicketNotEnough(ArcError):
|
||||||
# 记忆源点不足
|
# 记忆源点不足
|
||||||
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=-6, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class FriendError(ArcError):
|
class FriendError(ArcError):
|
||||||
# 好友系统出错
|
# 好友系统出错
|
||||||
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None) -> None:
|
def __init__(self, message=None, error_code=108, api_error_code=-999, extra_data=None, status=200) -> None:
|
||||||
super().__init__(message, error_code, api_error_code, extra_data)
|
super().__init__(message, error_code, api_error_code, extra_data, status)
|
||||||
|
|
||||||
|
|
||||||
class NoAccess(ArcError):
|
class NoAccess(ArcError):
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class UserItem(Item):
|
|||||||
(self.user.user_id, self.item_id, self.item_type))
|
(self.user.user_id, self.item_id, self.item_type))
|
||||||
x = self.c.fetchone()
|
x = self.c.fetchone()
|
||||||
if x:
|
if x:
|
||||||
self.amount = x[0] if x[0] else 1
|
self.amount = x[0] if x[0] is not None else 1
|
||||||
else:
|
else:
|
||||||
self.amount = 0
|
self.amount = 0
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ class ProgBoost(UserItem):
|
|||||||
世界模式prog_boost\
|
世界模式prog_boost\
|
||||||
parameters: `user` - `UserOnline`类或子类的实例
|
parameters: `user` - `UserOnline`类或子类的实例
|
||||||
'''
|
'''
|
||||||
user.update_user_one_column('prog_boost', 1)
|
user.update_user_one_column('prog_boost', 300)
|
||||||
|
|
||||||
|
|
||||||
class Stamina6(UserItem):
|
class Stamina6(UserItem):
|
||||||
@@ -373,8 +373,8 @@ class UserItemList:
|
|||||||
self.c.execute(
|
self.c.execute(
|
||||||
'''select item_id from item where type=?''', (item_type,))
|
'''select item_id from item where type=?''', (item_type,))
|
||||||
else:
|
else:
|
||||||
self.c.execute('''select item_id, amount from user_item where type = :a''', {
|
self.c.execute('''select item_id, amount from user_item where type = :a and user_id = :b''', {
|
||||||
'a': item_type})
|
'a': item_type, 'b': self.user.user_id})
|
||||||
x = self.c.fetchall()
|
x = self.c.fetchall()
|
||||||
if not x:
|
if not x:
|
||||||
return self
|
return self
|
||||||
@@ -382,7 +382,7 @@ class UserItemList:
|
|||||||
self.items: list = []
|
self.items: list = []
|
||||||
for i in x:
|
for i in x:
|
||||||
if len(i) > 1:
|
if len(i) > 1:
|
||||||
amount = i[1] if i[1] else 0
|
amount = i[1] if i[1] is not None else 1
|
||||||
else:
|
else:
|
||||||
amount = 1
|
amount = 1
|
||||||
self.items.append(ItemFactory.from_dict(
|
self.items.append(ItemFactory.from_dict(
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
from base64 import b64encode
|
import socket
|
||||||
|
from base64 import b64decode, b64encode
|
||||||
|
|
||||||
from core.error import ArcError, Timeout
|
from core.error import ArcError, Timeout
|
||||||
|
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
from .user import UserInfo
|
from .user import UserInfo
|
||||||
|
|
||||||
|
socket.setdefaulttimeout(Constant.LINKPLAY_TIMEOUT)
|
||||||
|
|
||||||
|
|
||||||
def get_song_unlock(client_song_map: dict) -> bytes:
|
def get_song_unlock(client_song_map: dict) -> bytes:
|
||||||
'''处理可用歌曲bit,返回bytes'''
|
'''处理可用歌曲bit,返回bytes'''
|
||||||
|
|
||||||
user_song_unlock = [0] * Constant.LINK_PLAY_UNLOCK_LENGTH
|
user_song_unlock = [0] * Constant.LINKPLAY_UNLOCK_LENGTH
|
||||||
for i in range(0, Constant.LINK_PLAY_UNLOCK_LENGTH*2, 2):
|
for i in range(0, Constant.LINKPLAY_UNLOCK_LENGTH*2, 2):
|
||||||
x = 0
|
x = 0
|
||||||
y = 0
|
y = 0
|
||||||
if str(i) in client_song_map:
|
if str(i) in client_song_map:
|
||||||
@@ -82,9 +85,8 @@ class Room:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LocalMultiPlayer:
|
class RemoteMultiPlayer:
|
||||||
def __init__(self, conn=None) -> None:
|
def __init__(self) -> None:
|
||||||
self.conn = conn
|
|
||||||
self.user: 'Player' = None
|
self.user: 'Player' = None
|
||||||
self.room: 'Room' = None
|
self.room: 'Room' = None
|
||||||
|
|
||||||
@@ -93,29 +95,44 @@ class LocalMultiPlayer:
|
|||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
return dict(self.room.to_dict(), **self.user.to_dict())
|
return dict(self.room.to_dict(), **self.user.to_dict())
|
||||||
|
|
||||||
def data_swap(self, data: tuple) -> tuple:
|
@staticmethod
|
||||||
self.conn.send(data)
|
def tcp(data: str) -> str:
|
||||||
if self.conn.poll(Constant.LINK_PLAY_TIMEOUT):
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
self.data_recv = self.conn.recv()
|
sock.connect((Constant.LINKPLAY_HOST,
|
||||||
if self.data_recv[0] != 0:
|
Constant.LINKPLAY_TCP_PORT))
|
||||||
raise ArcError('Link Play error.', self.data_recv[0])
|
sock.sendall(bytes(data + "\n", "utf-8"))
|
||||||
else:
|
try:
|
||||||
|
received = str(sock.recv(1024), "utf-8").strip()
|
||||||
|
except socket.timeout:
|
||||||
raise Timeout(
|
raise Timeout(
|
||||||
'Timeout when waiting for data from local udp server.')
|
'Timeout when waiting for data from link play server.', status=400)
|
||||||
|
# print(received)
|
||||||
|
return received
|
||||||
|
|
||||||
|
def data_swap(self, data: tuple) -> tuple:
|
||||||
|
|
||||||
|
received = self.tcp(Constant.LINKPLAY_AUTHENTICATION +
|
||||||
|
'|' + '|'.join([str(x) for x in data]))
|
||||||
|
|
||||||
|
self.data_recv = received.split('|')
|
||||||
|
if self.data_recv[0] != '0':
|
||||||
|
raise ArcError('Link Play error.',
|
||||||
|
int(self.data_recv[0]), status=400)
|
||||||
|
|
||||||
def create_room(self, user: 'Player' = None) -> None:
|
def create_room(self, user: 'Player' = None) -> None:
|
||||||
'''创建房间'''
|
'''创建房间'''
|
||||||
if user is not None:
|
if user is not None:
|
||||||
self.user = user
|
self.user = user
|
||||||
user.select_user_one_column('name')
|
user.select_user_one_column('name')
|
||||||
self.data_swap((1, self.user.name, self.user.song_unlock))
|
self.data_swap((1, self.user.name, b64encode(
|
||||||
|
self.user.song_unlock).decode('utf-8')))
|
||||||
self.room = Room()
|
self.room = Room()
|
||||||
self.room.room_code = self.data_recv[1]
|
self.room.room_code = self.data_recv[1]
|
||||||
self.room.room_id = self.data_recv[2]
|
self.room.room_id = int(self.data_recv[2])
|
||||||
self.room.song_unlock = self.user.song_unlock
|
self.room.song_unlock = self.user.song_unlock
|
||||||
self.user.token = self.data_recv[3]
|
self.user.token = int(self.data_recv[3])
|
||||||
self.user.key = self.data_recv[4]
|
self.user.key = b64decode(self.data_recv[4])
|
||||||
self.user.player_id = self.data_recv[5]
|
self.user.player_id = int(self.data_recv[5])
|
||||||
|
|
||||||
def join_room(self, room: 'Room' = None, user: 'Player' = None) -> None:
|
def join_room(self, room: 'Room' = None, user: 'Player' = None) -> None:
|
||||||
'''加入房间'''
|
'''加入房间'''
|
||||||
@@ -126,13 +143,13 @@ class LocalMultiPlayer:
|
|||||||
|
|
||||||
self.user.select_user_one_column('name')
|
self.user.select_user_one_column('name')
|
||||||
self.data_swap(
|
self.data_swap(
|
||||||
(2, self.user.name, self.user.song_unlock, room.room_code))
|
(2, self.user.name, b64encode(self.user.song_unlock).decode('utf-8'), room.room_code))
|
||||||
self.room.room_code = self.data_recv[1]
|
self.room.room_code = self.data_recv[1]
|
||||||
self.room.room_id = self.data_recv[2]
|
self.room.room_id = int(self.data_recv[2])
|
||||||
self.room.song_unlock = self.data_recv[6]
|
self.room.song_unlock = b64decode(self.data_recv[6])
|
||||||
self.user.token = self.data_recv[3]
|
self.user.token = int(self.data_recv[3])
|
||||||
self.user.key = self.data_recv[4]
|
self.user.key = b64decode(self.data_recv[4])
|
||||||
self.user.player_id = self.data_recv[5]
|
self.user.player_id = int(self.data_recv[5])
|
||||||
|
|
||||||
def update_room(self, user: 'Player' = None) -> None:
|
def update_room(self, user: 'Player' = None) -> None:
|
||||||
'''更新房间'''
|
'''更新房间'''
|
||||||
@@ -141,7 +158,7 @@ class LocalMultiPlayer:
|
|||||||
self.data_swap((3, self.user.token))
|
self.data_swap((3, self.user.token))
|
||||||
self.room = Room()
|
self.room = Room()
|
||||||
self.room.room_code = self.data_recv[1]
|
self.room.room_code = self.data_recv[1]
|
||||||
self.room.room_id = self.data_recv[2]
|
self.room.room_id = int(self.data_recv[2])
|
||||||
self.room.song_unlock = self.data_recv[5]
|
self.room.song_unlock = b64decode(self.data_recv[5])
|
||||||
self.user.key = self.data_recv[3]
|
self.user.key = b64decode(self.data_recv[3])
|
||||||
self.user.player_id = self.data_recv[4]
|
self.user.player_id = int(self.data_recv[4])
|
||||||
|
|||||||
@@ -70,10 +70,37 @@ class RankList:
|
|||||||
y.rank = rank
|
y.rank = rank
|
||||||
self.list.append(y)
|
self.list.append(y)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_my_rank_parameter(my_rank: int, amount: int, all_limit: int = 20, max_local_position: int = Constant.MY_RANK_MAX_LOCAL_POSITION, max_global_position: int = Constant.MY_RANK_MAX_GLOBAL_POSITION):
|
||||||
|
'''
|
||||||
|
计算我的排名中的查询参数\
|
||||||
|
returns:
|
||||||
|
`sql_limit`: int - 查询limit参数
|
||||||
|
`sql_offset`: int - 查询offset参数
|
||||||
|
`need_myself`: bool - 是否需要在排名结尾添加自己
|
||||||
|
'''
|
||||||
|
sql_limit = all_limit
|
||||||
|
sql_offset = 0
|
||||||
|
need_myself = False
|
||||||
|
|
||||||
|
if my_rank <= max_local_position: # 排名在前面,前方人数不足
|
||||||
|
pass
|
||||||
|
elif my_rank > max_global_position: # 排名太后了,不显示排名
|
||||||
|
sql_limit -= 1
|
||||||
|
sql_offset = max_global_position - all_limit + 1
|
||||||
|
need_myself = True
|
||||||
|
elif amount - my_rank < all_limit - max_local_position: # 后方人数不足,显示排名
|
||||||
|
sql_offset = amount - all_limit
|
||||||
|
elif my_rank >= max_local_position and my_rank <= max_global_position - all_limit + max_local_position - 1: # 前方人数足够,显示排名
|
||||||
|
sql_offset = my_rank - max_local_position
|
||||||
|
else: # 我已经忘了这是什么了
|
||||||
|
sql_offset = max_global_position - all_limit
|
||||||
|
|
||||||
|
return sql_limit, sql_offset, need_myself
|
||||||
|
|
||||||
def select_me(self, user=None) -> None:
|
def select_me(self, user=None) -> None:
|
||||||
'''
|
'''
|
||||||
得到我的排名分数表\
|
得到我的排名分数表
|
||||||
尚不清楚这个函数有没有问题
|
|
||||||
'''
|
'''
|
||||||
if user:
|
if user:
|
||||||
self.user = user
|
self.user = user
|
||||||
@@ -85,20 +112,17 @@ class RankList:
|
|||||||
|
|
||||||
self.c.execute('''select count(*) from best_score where song_id = :song_id and difficulty = :difficulty and ( score > :score or (score = :score and time_played > :time_played) )''', {
|
self.c.execute('''select count(*) from best_score where song_id = :song_id and difficulty = :difficulty and ( score > :score or (score = :score and time_played > :time_played) )''', {
|
||||||
'user_id': self.user.user_id, 'song_id': self.song.song_id, 'difficulty': self.song.difficulty, 'score': x[0], 'time_played': x[1]})
|
'user_id': self.user.user_id, 'song_id': self.song.song_id, 'difficulty': self.song.difficulty, 'score': x[0], 'time_played': x[1]})
|
||||||
x = self.c.fetchone()
|
my_rank = int(self.c.fetchone()[0]) + 1
|
||||||
myrank = int(x[0]) + 1
|
|
||||||
self.c.execute('''select count(*) from best_score where song_id=:a and difficulty=:b''',
|
self.c.execute('''select count(*) from best_score where song_id=:a and difficulty=:b''',
|
||||||
{'a': self.song.song_id, 'b': self.song.difficulty})
|
{'a': self.song.song_id, 'b': self.song.difficulty})
|
||||||
amount = int(self.c.fetchone()[0])
|
|
||||||
|
|
||||||
if myrank <= 4: # 排名在前4
|
sql_limit, sql_offset, need_myself = self.get_my_rank_parameter(
|
||||||
self.select_top()
|
my_rank, int(self.c.fetchone()[0]), self.limit)
|
||||||
elif myrank >= 5 and myrank <= 9999 - self.limit + 4 and amount >= 10000: # 万名内,前面有4个人
|
|
||||||
self.c.execute('''select user_id from best_score where song_id = :song_id and difficulty = :difficulty order by score DESC, time_played DESC limit :limit offset :offset''', {
|
self.c.execute('''select user_id from best_score where song_id = :song_id and difficulty = :difficulty order by score DESC, time_played DESC limit :limit offset :offset''', {
|
||||||
'song_id': self.song.song_id, 'difficulty': self.song.difficulty, 'limit': self.limit, 'offset': myrank - 5})
|
'song_id': self.song.song_id, 'difficulty': self.song.difficulty, 'limit': sql_limit, 'offset': sql_offset})
|
||||||
x = self.c.fetchall()
|
x = self.c.fetchall()
|
||||||
if x:
|
if x:
|
||||||
rank = myrank - 5
|
rank = sql_offset if sql_offset > 0 else 0
|
||||||
self.list = []
|
self.list = []
|
||||||
for i in x:
|
for i in x:
|
||||||
rank += 1
|
rank += 1
|
||||||
@@ -107,51 +131,9 @@ class RankList:
|
|||||||
y.select_score()
|
y.select_score()
|
||||||
y.rank = rank
|
y.rank = rank
|
||||||
self.list.append(y)
|
self.list.append(y)
|
||||||
|
if need_myself:
|
||||||
elif myrank >= 10000: # 万名外
|
|
||||||
self.c.execute('''select user_id from best_score where song_id = :song_id and difficulty = :difficulty order by score DESC, time_played DESC limit :limit offset :offset''', {
|
|
||||||
'song_id': self.song.song_id, 'difficulty': self.song.difficulty, 'limit': self.limit - 1, 'offset': 9999-self.limit})
|
|
||||||
x = self.c.fetchall()
|
|
||||||
if x:
|
|
||||||
rank = 9999 - self.limit
|
|
||||||
for i in x:
|
|
||||||
rank += 1
|
|
||||||
y = UserScore(self.c, UserInfo(self.c, i[0]))
|
|
||||||
y.song = self.song
|
|
||||||
y.select_score()
|
|
||||||
y.rank = rank
|
|
||||||
self.list.append(y)
|
|
||||||
y = UserScore(self.c, UserInfo(self.c, self.user.user_id))
|
y = UserScore(self.c, UserInfo(self.c, self.user.user_id))
|
||||||
y.song = self.song
|
y.song = self.song
|
||||||
|
y.select_score()
|
||||||
y.rank = -1
|
y.rank = -1
|
||||||
self.list.append(y)
|
self.list.append(y)
|
||||||
|
|
||||||
elif amount - myrank < self.limit - 5: # 后方人数不足
|
|
||||||
self.c.execute('''select user_id from best_score where song_id = :song_id and difficulty = :difficulty order by score DESC, time_played DESC limit :limit offset :offset''', {
|
|
||||||
'song_id': self.song.song_id, 'difficulty': self.song.difficulty, 'limit': self.limit, 'offset': amount - self.limit})
|
|
||||||
x = self.c.fetchall()
|
|
||||||
if x:
|
|
||||||
rank = amount - self.limit
|
|
||||||
if rank < 0:
|
|
||||||
rank = 0
|
|
||||||
for i in x:
|
|
||||||
rank += 1
|
|
||||||
y = UserScore(self.c, UserInfo(self.c, i[0]))
|
|
||||||
y.song = self.song
|
|
||||||
y.select_score()
|
|
||||||
y.rank = rank
|
|
||||||
self.list.append(y)
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.c.execute('''select user_id from best_score where song_id = :song_id and difficulty = :difficulty order by score DESC, time_played DESC limit :limit offset :offset''', {
|
|
||||||
'song_id': self.song.song_id, 'difficulty': self.song.difficulty, 'limit': self.limit, 'offset': 9998-self.limit})
|
|
||||||
x = self.c.fetchall()
|
|
||||||
if x:
|
|
||||||
rank = 9998 - self.limit
|
|
||||||
for i in x:
|
|
||||||
rank += 1
|
|
||||||
y = UserScore(self.c, UserInfo(self.c, i[0]))
|
|
||||||
y.song = self.song
|
|
||||||
y.select_score()
|
|
||||||
y.rank = rank
|
|
||||||
self.list.append(y)
|
|
||||||
|
|||||||
@@ -93,8 +93,8 @@ class SaveData:
|
|||||||
else:
|
else:
|
||||||
i['complete'] = 1
|
i['complete'] = 1
|
||||||
|
|
||||||
self.finalestate_data = '|'.join(['0', '100'] + [str(i[1]) for i in Constant.SHIT_DATA_OF_616] + [
|
self.finalestate_data = '|'.join(
|
||||||
str(i) for i in Constant.UNKNOWN_SHIT] + ['1337'])
|
['0', '100'] + [str(x[0]) if i in [64, 65, 66, 71] else str(x[1]) for i, x in enumerate(Constant.FINALE_SWITCH)] + ['1337'])
|
||||||
|
|
||||||
def update_all(self, user) -> None:
|
def update_all(self, user) -> None:
|
||||||
'''
|
'''
|
||||||
@@ -110,6 +110,8 @@ class SaveData:
|
|||||||
'''
|
'''
|
||||||
从Arcaea客户端给的奇怪字符串中获取存档数据,并进行数据校验
|
从Arcaea客户端给的奇怪字符串中获取存档数据,并进行数据校验
|
||||||
'''
|
'''
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
if key not in self.__dict__:
|
if key not in self.__dict__:
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
'Property `%s` is not found in the instance of `SaveData` class.' % key)
|
'Property `%s` is not found in the instance of `SaveData` class.' % key)
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class Score:
|
|||||||
return self.rating
|
return self.rating
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
return {
|
r = {
|
||||||
"rating": self.rating,
|
"rating": self.rating,
|
||||||
"modifier": self.modifier,
|
"modifier": self.modifier,
|
||||||
"time_played": self.time_played,
|
"time_played": self.time_played,
|
||||||
@@ -142,6 +142,9 @@ class Score:
|
|||||||
"difficulty": self.song.difficulty,
|
"difficulty": self.song.difficulty,
|
||||||
"song_id": self.song.song_id
|
"song_id": self.song.song_id
|
||||||
}
|
}
|
||||||
|
if self.song.song_name is not None:
|
||||||
|
r["song_name"] = self.song.song_name
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
class UserScore(Score):
|
class UserScore(Score):
|
||||||
@@ -222,8 +225,9 @@ class UserPlay(UserScore):
|
|||||||
else:
|
else:
|
||||||
r = {}
|
r = {}
|
||||||
r['user_rating'] = self.user.rating_ptt
|
r['user_rating'] = self.user.rating_ptt
|
||||||
|
r['finale_challenge_higher'] = self.rating > self.ptt.value
|
||||||
r['global_rank'] = self.user.global_rank
|
r['global_rank'] = self.user.global_rank
|
||||||
r['finale_play_value'] = 0 # emmmm
|
r['finale_play_value'] = self.rating * 5 # emmmm
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -285,7 +289,7 @@ class UserPlay(UserScore):
|
|||||||
self.c.execute('''select prog_boost from user where user_id=:a''', {
|
self.c.execute('''select prog_boost from user where user_id=:a''', {
|
||||||
'a': self.user.user_id})
|
'a': self.user.user_id})
|
||||||
x = self.c.fetchone()
|
x = self.c.fetchone()
|
||||||
if x and x[0] == 1:
|
if x and x[0] == 300:
|
||||||
self.prog_boost_multiply = 300
|
self.prog_boost_multiply = 300
|
||||||
|
|
||||||
self.clear_play_state()
|
self.clear_play_state()
|
||||||
@@ -565,7 +569,15 @@ class UserScoreList:
|
|||||||
|
|
||||||
self.query.query_append({'user_id': self.user.user_id})
|
self.query.query_append({'user_id': self.user.user_id})
|
||||||
self.query.sort += [{'column': 'rating', 'order': 'DESC'}]
|
self.query.sort += [{'column': 'rating', 'order': 'DESC'}]
|
||||||
print(self.query.sort)
|
|
||||||
x = Sql(self.c).select('best_score', query=self.query)
|
x = Sql(self.c).select('best_score', query=self.query)
|
||||||
|
|
||||||
self.scores = [UserScore(self.c, self.user).from_list(i) for i in x]
|
self.scores = [UserScore(self.c, self.user).from_list(i) for i in x]
|
||||||
|
|
||||||
|
def select_song_name(self) -> None:
|
||||||
|
'''为所有成绩中的song_id查询song_name'''
|
||||||
|
if self.scores is None:
|
||||||
|
return
|
||||||
|
for score in self.scores:
|
||||||
|
self.c.execute(
|
||||||
|
'''select name from chart where song_id = ?''', (score.song.song_id,))
|
||||||
|
score.song.song_name = self.c.fetchone()[0]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class Chart:
|
|||||||
self.c = c
|
self.c = c
|
||||||
self.set_chart(song_id, difficulty)
|
self.set_chart(song_id, difficulty)
|
||||||
self.defnum: int = None
|
self.defnum: int = None
|
||||||
|
self.song_name: str = None
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import traceback
|
import traceback
|
||||||
from certifi import where
|
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from .constant import Constant
|
from .constant import Constant
|
||||||
from .error import InputError
|
from .error import ArcError, InputError
|
||||||
|
|
||||||
|
|
||||||
class Connect:
|
class Connect:
|
||||||
@@ -20,12 +19,16 @@ class Connect:
|
|||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.conn = sqlite3.connect(self.file_path)
|
self.conn = sqlite3.connect(self.file_path, timeout=10)
|
||||||
self.c = self.conn.cursor()
|
self.c = self.conn.cursor()
|
||||||
return self.c
|
return self.c
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
|
def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
|
||||||
|
flag = True
|
||||||
if exc_type is not None:
|
if exc_type is not None:
|
||||||
|
if issubclass(exc_type, ArcError):
|
||||||
|
flag = False
|
||||||
|
else:
|
||||||
if self.conn:
|
if self.conn:
|
||||||
self.conn.rollback()
|
self.conn.rollback()
|
||||||
|
|
||||||
@@ -36,7 +39,7 @@ class Connect:
|
|||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
return True
|
return flag
|
||||||
|
|
||||||
|
|
||||||
class Query:
|
class Query:
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ class UserInfo(User):
|
|||||||
@property
|
@property
|
||||||
def cores(self) -> list:
|
def cores(self) -> list:
|
||||||
if self.__cores is None:
|
if self.__cores is None:
|
||||||
x = UserItemList(self.c, self.user_id).select_from_type('core')
|
x = UserItemList(self.c, self).select_from_type('core')
|
||||||
self.__cores = [{'core_type': i.item_id,
|
self.__cores = [{'core_type': i.item_id,
|
||||||
'amount': i.amount} for i in x.items]
|
'amount': i.amount} for i in x.items]
|
||||||
|
|
||||||
@@ -311,7 +311,7 @@ class UserInfo(User):
|
|||||||
@property
|
@property
|
||||||
def singles(self) -> list:
|
def singles(self) -> list:
|
||||||
if self.__singles is None:
|
if self.__singles is None:
|
||||||
x = UserItemList(self.c, self.user_id).select_from_type('single')
|
x = UserItemList(self.c, self).select_from_type('single')
|
||||||
self.__singles = [i.item_id for i in x.items]
|
self.__singles = [i.item_id for i in x.items]
|
||||||
|
|
||||||
return self.__singles
|
return self.__singles
|
||||||
@@ -319,7 +319,7 @@ class UserInfo(User):
|
|||||||
@property
|
@property
|
||||||
def packs(self) -> list:
|
def packs(self) -> list:
|
||||||
if self.__packs is None:
|
if self.__packs is None:
|
||||||
x = UserItemList(self.c, self.user_id).select_from_type('pack')
|
x = UserItemList(self.c, self).select_from_type('pack')
|
||||||
self.__packs = [i.item_id for i in x.items]
|
self.__packs = [i.item_id for i in x.items]
|
||||||
|
|
||||||
return self.__packs
|
return self.__packs
|
||||||
@@ -327,7 +327,7 @@ class UserInfo(User):
|
|||||||
@property
|
@property
|
||||||
def world_unlocks(self) -> list:
|
def world_unlocks(self) -> list:
|
||||||
if self.__world_unlocks is None:
|
if self.__world_unlocks is None:
|
||||||
x = UserItemList(self.c, self.user_id).select_from_type(
|
x = UserItemList(self.c, self).select_from_type(
|
||||||
'world_unlock')
|
'world_unlock')
|
||||||
self.__world_unlocks = [i.item_id for i in x.items]
|
self.__world_unlocks = [i.item_id for i in x.items]
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ class UserInfo(User):
|
|||||||
def world_songs(self) -> list:
|
def world_songs(self) -> list:
|
||||||
if self.__world_songs is None:
|
if self.__world_songs is None:
|
||||||
x = UserItemList(
|
x = UserItemList(
|
||||||
self.c, self.user_id).select_from_type('world_song')
|
self.c, self).select_from_type('world_song')
|
||||||
self.__world_songs = [i.item_id for i in x.items]
|
self.__world_songs = [i.item_id for i in x.items]
|
||||||
|
|
||||||
return self.__world_songs
|
return self.__world_songs
|
||||||
@@ -346,7 +346,7 @@ class UserInfo(User):
|
|||||||
def course_banners(self) -> list:
|
def course_banners(self) -> list:
|
||||||
if self.__course_banners is None:
|
if self.__course_banners is None:
|
||||||
x = UserItemList(
|
x = UserItemList(
|
||||||
self.c, self.user_id).select_from_type('course_banner')
|
self.c, self).select_from_type('course_banner')
|
||||||
self.__course_banners = [i.item_id for i in x.items]
|
self.__course_banners = [i.item_id for i in x.items]
|
||||||
|
|
||||||
return self.__course_banners
|
return self.__course_banners
|
||||||
@@ -450,6 +450,10 @@ class UserInfo(User):
|
|||||||
favorite_character_id = self.favorite_character.character_id
|
favorite_character_id = self.favorite_character.character_id
|
||||||
else:
|
else:
|
||||||
favorite_character_id = -1
|
favorite_character_id = -1
|
||||||
|
|
||||||
|
if self.character.character_id not in character_list:
|
||||||
|
self.character.character_id = 0
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"is_aprilfools": Config.IS_APRILFOOLS,
|
"is_aprilfools": Config.IS_APRILFOOLS,
|
||||||
"curr_available_maps": self.curr_available_maps_list,
|
"curr_available_maps": self.curr_available_maps_list,
|
||||||
@@ -477,7 +481,7 @@ class UserInfo(User):
|
|||||||
"world_songs": self.world_songs,
|
"world_songs": self.world_songs,
|
||||||
"singles": self.singles,
|
"singles": self.singles,
|
||||||
"packs": self.packs,
|
"packs": self.packs,
|
||||||
"characters": self.characters_list,
|
"characters": character_list,
|
||||||
"cores": self.cores,
|
"cores": self.cores,
|
||||||
"recent_score": self.recent_score_list,
|
"recent_score": self.recent_score_list,
|
||||||
"max_friend": Constant.MAX_FRIEND_COUNT,
|
"max_friend": Constant.MAX_FRIEND_COUNT,
|
||||||
@@ -615,7 +619,7 @@ class UserInfo(User):
|
|||||||
with Connect() as c2:
|
with Connect() as c2:
|
||||||
c2.execute('''select song_id, rating_ftr, rating_byn from chart''')
|
c2.execute('''select song_id, rating_ftr, rating_byn from chart''')
|
||||||
x = c2.fetchall()
|
x = c2.fetchall()
|
||||||
if x:
|
|
||||||
song_list_ftr = [self.user_id]
|
song_list_ftr = [self.user_id]
|
||||||
song_list_byn = [self.user_id]
|
song_list_byn = [self.user_id]
|
||||||
for i in x:
|
for i in x:
|
||||||
@@ -624,15 +628,14 @@ class UserInfo(User):
|
|||||||
if i[2] > 0:
|
if i[2] > 0:
|
||||||
song_list_byn.append(i[0])
|
song_list_byn.append(i[0])
|
||||||
|
|
||||||
|
score_sum = 0
|
||||||
if len(song_list_ftr) >= 2:
|
if len(song_list_ftr) >= 2:
|
||||||
self.c.execute('''select sum(score) from best_score where user_id=? and difficulty=2 and song_id in ({0})'''.format(
|
self.c.execute('''select sum(score) from best_score where user_id=? and difficulty=2 and song_id in ({0})'''.format(
|
||||||
','.join(['?']*(len(song_list_ftr)-1))), tuple(song_list_ftr))
|
','.join(['?']*(len(song_list_ftr)-1))), tuple(song_list_ftr))
|
||||||
|
|
||||||
x = self.c.fetchone()
|
x = self.c.fetchone()
|
||||||
if x[0] is not None:
|
if x[0] is not None:
|
||||||
score_sum = x[0]
|
score_sum += x[0]
|
||||||
else:
|
|
||||||
score_sum = 0
|
|
||||||
|
|
||||||
if len(song_list_byn) >= 2:
|
if len(song_list_byn) >= 2:
|
||||||
self.c.execute('''select sum(score) from best_score where user_id=? and difficulty=3 and song_id in ({0})'''.format(
|
self.c.execute('''select sum(score) from best_score where user_id=? and difficulty=3 and song_id in ({0})'''.format(
|
||||||
@@ -641,8 +644,6 @@ class UserInfo(User):
|
|||||||
x = self.c.fetchone()
|
x = self.c.fetchone()
|
||||||
if x[0] is not None:
|
if x[0] is not None:
|
||||||
score_sum += x[0]
|
score_sum += x[0]
|
||||||
else:
|
|
||||||
score_sum += 0
|
|
||||||
|
|
||||||
self.c.execute('''update user set world_rank_score = :b where user_id = :a''', {
|
self.c.execute('''update user set world_rank_score = :b where user_id = :a''', {
|
||||||
'a': self.user_id, 'b': score_sum})
|
'a': self.user_id, 'b': score_sum})
|
||||||
|
|||||||
@@ -158,17 +158,17 @@ class Map:
|
|||||||
self.is_legacy = raw_dict.get('is_legacy')
|
self.is_legacy = raw_dict.get('is_legacy')
|
||||||
self.is_beyond = raw_dict.get('is_beyond')
|
self.is_beyond = raw_dict.get('is_beyond')
|
||||||
self.beyond_health = raw_dict.get('beyond_health')
|
self.beyond_health = raw_dict.get('beyond_health')
|
||||||
self.character_affinity = raw_dict.get('character_affinity')
|
self.character_affinity = raw_dict.get('character_affinity', [])
|
||||||
self.affinity_multiplier = raw_dict.get('affinity_multiplier')
|
self.affinity_multiplier = raw_dict.get('affinity_multiplier', [])
|
||||||
self.chapter = raw_dict.get('chapter')
|
self.chapter = raw_dict.get('chapter')
|
||||||
self.available_from = raw_dict.get('available_from')
|
self.available_from = raw_dict.get('available_from', -1)
|
||||||
self.available_to = raw_dict.get('available_to')
|
self.available_to = raw_dict.get('available_to', 9999999999999)
|
||||||
self.is_repeatable = raw_dict.get('is_repeatable')
|
self.is_repeatable = raw_dict.get('is_repeatable')
|
||||||
self.require_id = raw_dict.get('require_id')
|
self.require_id = raw_dict.get('require_id', '')
|
||||||
self.require_type = raw_dict.get('require_type')
|
self.require_type = raw_dict.get('require_type', '')
|
||||||
self.require_value = raw_dict.get('require_value')
|
self.require_value = raw_dict.get('require_value', 1)
|
||||||
self.coordinate = raw_dict.get('coordinate')
|
self.coordinate = raw_dict.get('coordinate')
|
||||||
self.custom_bg = raw_dict.get('custom_bg')
|
self.custom_bg = raw_dict.get('custom_bg', '')
|
||||||
self.stamina_cost = raw_dict.get('stamina_cost')
|
self.stamina_cost = raw_dict.get('stamina_cost')
|
||||||
self.steps = [Step().from_dict(s) for s in raw_dict.get('steps')]
|
self.steps = [Step().from_dict(s) for s in raw_dict.get('steps')]
|
||||||
return self
|
return self
|
||||||
@@ -199,7 +199,7 @@ class UserMap(Map):
|
|||||||
@property
|
@property
|
||||||
def rewards_for_climbing(self) -> list:
|
def rewards_for_climbing(self) -> list:
|
||||||
rewards = []
|
rewards = []
|
||||||
for i in range(self.prev_position, self.curr_position+1):
|
for i in range(self.prev_position+1, self.curr_position+1):
|
||||||
step = self.steps[i]
|
step = self.steps[i]
|
||||||
if step.items:
|
if step.items:
|
||||||
rewards.append(
|
rewards.append(
|
||||||
@@ -209,7 +209,7 @@ class UserMap(Map):
|
|||||||
|
|
||||||
def rewards_for_climbing_to_dict(self) -> list:
|
def rewards_for_climbing_to_dict(self) -> list:
|
||||||
rewards = []
|
rewards = []
|
||||||
for i in range(self.prev_position, self.curr_position+1):
|
for i in range(self.prev_position+1, self.curr_position+1):
|
||||||
step = self.steps[i]
|
step = self.steps[i]
|
||||||
if step.items:
|
if step.items:
|
||||||
rewards.append(
|
rewards.append(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import json
|
|||||||
|
|
||||||
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
||||||
|
|
||||||
ARCAEA_SERVER_VERSION = 'v2.9.0'
|
ARCAEA_SERVER_VERSION = 'v2.10.0'
|
||||||
|
|
||||||
|
|
||||||
def main(path='./'):
|
def main(path='./'):
|
||||||
@@ -208,9 +208,9 @@ def main(path='./'):
|
|||||||
primary key(user_id, song_id, file_name)
|
primary key(user_id, song_id, file_name)
|
||||||
);''')
|
);''')
|
||||||
c.execute('''create table if not exists user_download(user_id int,
|
c.execute('''create table if not exists user_download(user_id int,
|
||||||
token text,
|
|
||||||
time int,
|
time int,
|
||||||
primary key(user_id, token, time)
|
token text,
|
||||||
|
primary key(user_id, time, token)
|
||||||
);''')
|
);''')
|
||||||
c.execute('''create table if not exists item(item_id text,
|
c.execute('''create table if not exists item(item_id text,
|
||||||
type text,
|
type text,
|
||||||
@@ -324,7 +324,7 @@ def main(path='./'):
|
|||||||
difficulty int,
|
difficulty int,
|
||||||
flag_as_hidden int,
|
flag_as_hidden int,
|
||||||
song_index int,
|
song_index int,
|
||||||
primary key(course_id, song_id, difficulty)
|
primary key(course_id, song_index)
|
||||||
);''')
|
);''')
|
||||||
c.execute('''create table if not exists course_requirement(course_id text,
|
c.execute('''create table if not exists course_requirement(course_id text,
|
||||||
required_id text,
|
required_id text,
|
||||||
@@ -337,48 +337,53 @@ def main(path='./'):
|
|||||||
primary key(course_id, item_id, type)
|
primary key(course_id, item_id, type)
|
||||||
);''')
|
);''')
|
||||||
|
|
||||||
# 搭档初始化
|
c.execute(
|
||||||
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',
|
'''create index best_score_1 on best_score (song_id, difficulty);''') # 排名查询优化
|
||||||
'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', 'hikari(fatalis)']
|
c.execute(
|
||||||
|
'''create index download_token_1 on download_token (song_id, file_name);''') # 下载token判断优化
|
||||||
|
|
||||||
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_fatalis']
|
char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', 'hikari&tairitsu(reunion)', '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', 'hikari(fatalis)', 'saki', 'setsuna']
|
||||||
|
|
||||||
|
skill_id = ['gauge_easy', '', '', '', 'note_mirror', 'skill_reunion', '', '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_fatalis', 'frags_ongeki_slash', 'frags_ongeki_hard']
|
||||||
|
|
||||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
|
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
|
||||||
'', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
|
'', '', '', '', '', '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,
|
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, 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, 0, 8, 0]
|
||||||
|
|
||||||
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
frag1 = [55, 55, 60, 50, 47, 79, 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, 38, 25, 58, 50, 61, 45, 45, 38, 34, 27]
|
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 38, 25, 58, 50, 61, 45, 45, 38, 34, 27, 18, 56]
|
||||||
|
|
||||||
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
prog1 = [35, 55, 47, 50, 60, 70, 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, 60]
|
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, 130, 18, 57]
|
||||||
|
|
||||||
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
overdrive1 = [35, 55, 25, 50, 47, 70, 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, 38, 30, 49, 15, 34, 45, 45, 38, 67, 65]
|
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67, 120, 44, 33]
|
||||||
|
|
||||||
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
frag20 = [78, 80, 90, 75, 70, 79, 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, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50]
|
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 34, 85]
|
||||||
|
|
||||||
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
|
prog20 = [61, 80, 70, 75, 90, 70, 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, 79, 55, 65, 59, 90, 50, 90, 90, 75, 100]
|
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 34, 86]
|
||||||
|
|
||||||
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
|
overdrive20 = [61, 80, 47, 75, 70, 70, 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, 61, 40, 69, 62, 51, 90, 67, 60, 100, 105]
|
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 84, 50]
|
||||||
|
|
||||||
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
frag30 = [88, 90, 100, 75, 80, 89, 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, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50]
|
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 34, 85]
|
||||||
|
|
||||||
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
|
prog30 = [71, 90, 80, 75, 100, 80, 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, 79, 55, 65, 59, 90, 50, 90, 90, 75, 100]
|
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 34, 86]
|
||||||
|
|
||||||
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
|
overdrive30 = [71, 90, 57, 75, 80, 80, 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, 61, 40, 69, 62, 51, 90, 67, 60, 100, 105]
|
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 84, 50]
|
||||||
|
|
||||||
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
||||||
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3]
|
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 2]
|
||||||
|
|
||||||
char_core = {
|
char_core = {
|
||||||
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
||||||
@@ -399,16 +404,14 @@ def main(path='./'):
|
|||||||
19: [{'core_id': 'core_colorful', 'amount': 30}]
|
19: [{'core_id': 'core_colorful', 'amount': 30}]
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in range(0, 56):
|
for i in range(0, 58):
|
||||||
skill_requires_uncap = 1 if i == 2 else 0
|
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]:
|
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43, 11, 12, 19, 5]:
|
||||||
sql = '''insert into character values(?,?,30,?,?,?,?,?,?,?,?,?,?,?,?,?,?,1)'''
|
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],
|
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
||||||
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
||||||
else:
|
else:
|
||||||
if i == 5:
|
|
||||||
continue
|
|
||||||
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
|
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
|
||||||
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
|
||||||
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
|
||||||
@@ -427,7 +430,7 @@ def main(path='./'):
|
|||||||
c.execute('''insert into item values(?,"core",1,'')''', (i,))
|
c.execute('''insert into item values(?,"core",1,'')''', (i,))
|
||||||
|
|
||||||
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
|
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
|
||||||
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia"]
|
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv']
|
||||||
for i in world_songs:
|
for i in world_songs:
|
||||||
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
|
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
|
||||||
|
|
||||||
@@ -495,6 +498,9 @@ def main(path='./'):
|
|||||||
packs = json.load(f)
|
packs = json.load(f)
|
||||||
f.close()
|
f.close()
|
||||||
insert_items(c, packs)
|
insert_items(c, packs)
|
||||||
|
c.execute('''select exists(select * from item where item_id='epilogue')''')
|
||||||
|
if c.fetchone() == (0,):
|
||||||
|
c.execute('''insert into item values('epilogue','pack',1,'')''')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -604,5 +604,23 @@
|
|||||||
],
|
],
|
||||||
"orig_price": 500,
|
"orig_price": 500,
|
||||||
"price": 500
|
"price": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ongeki_append_1",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "pack",
|
||||||
|
"id": "ongeki_append_1",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 5,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 500,
|
||||||
|
"price": 500
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1198,5 +1198,23 @@
|
|||||||
],
|
],
|
||||||
"orig_price": 100,
|
"orig_price": 100,
|
||||||
"price": 100
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pupa",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "pupa",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
12
latest version/database/songs/songlist
Normal file
12
latest version/database/songs/songlist
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"songs": [
|
||||||
|
{
|
||||||
|
"id": "dement",
|
||||||
|
"difficulties": [
|
||||||
|
{
|
||||||
|
"ratingClass": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1
latest version/linkplay_server/__init__.py
Normal file
1
latest version/linkplay_server/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .main import link_play
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import os
|
from os import urandom
|
||||||
from cryptography.hazmat.primitives.ciphers import (
|
from cryptography.hazmat.primitives.ciphers import (
|
||||||
Cipher, algorithms, modes
|
Cipher, algorithms, modes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def encrypt(key, plaintext, associated_data):
|
def encrypt(key, plaintext, associated_data):
|
||||||
iv = os.urandom(12)
|
iv = urandom(12)
|
||||||
encryptor = Cipher(
|
encryptor = Cipher(
|
||||||
algorithms.AES(key),
|
algorithms.AES(key),
|
||||||
modes.GCM(iv, min_tag_length=12),
|
modes.GCM(iv, min_tag_length=12),
|
||||||
27
latest version/linkplay_server/config.py
Normal file
27
latest version/linkplay_server/config.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
class Config:
|
||||||
|
'''
|
||||||
|
Link Play server configuration
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
服务器地址、端口号、校验码
|
||||||
|
Server address, port and verification code
|
||||||
|
'''
|
||||||
|
HOST = '0.0.0.0'
|
||||||
|
UDP_PORT = 10900
|
||||||
|
TCP_PORT = 10901
|
||||||
|
AUTHENTICATION = 'my_link_play_server'
|
||||||
|
'''
|
||||||
|
--------------------------------------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
TIME_LIMIT = 3600000
|
||||||
|
|
||||||
|
COMMAND_INTERVAL = 1000000
|
||||||
|
|
||||||
|
COUNTDOWM_TIME = 3999
|
||||||
|
|
||||||
|
PLAYER_PRE_TIMEOUT = 3000000
|
||||||
|
PLAYER_TIMEOUT = 20000000
|
||||||
|
|
||||||
|
LINK_PLAY_UNLOCK_LENGTH = 512
|
||||||
264
latest version/linkplay_server/main.py
Normal file
264
latest version/linkplay_server/main.py
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import socketserver
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from os import urandom
|
||||||
|
|
||||||
|
# import binascii
|
||||||
|
from .aes import decrypt, encrypt
|
||||||
|
from .config import Config
|
||||||
|
from .udp_class import Player, Room, bi
|
||||||
|
from .udp_parser import CommandParser
|
||||||
|
|
||||||
|
# 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
|
||||||
|
lock = threading.RLock()
|
||||||
|
|
||||||
|
logging.basicConfig(format='[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
|
||||||
|
level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
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 unique_random(dataset, length=8, random_func=None):
|
||||||
|
'''无重复随机,且默认非0'''
|
||||||
|
if random_func is None:
|
||||||
|
x = bi(urandom(length))
|
||||||
|
while x in dataset or x == 0:
|
||||||
|
x = bi(urandom(length))
|
||||||
|
else:
|
||||||
|
x = random_func()
|
||||||
|
while x in dataset:
|
||||||
|
x = random_func()
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
# 内存清理
|
||||||
|
with lock:
|
||||||
|
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 UDP_handler(socketserver.BaseRequestHandler):
|
||||||
|
def handle(self):
|
||||||
|
client_msg, server = self.request
|
||||||
|
try:
|
||||||
|
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 = decrypt(user['key'], b'', iv, ciphertext, tag)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
return None
|
||||||
|
# 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 = encrypt(user['key'], i, b'')
|
||||||
|
# print(binascii.b2a_hex(i))
|
||||||
|
|
||||||
|
server.sendto(token + iv + tag[:12] +
|
||||||
|
ciphertext, self.client_address)
|
||||||
|
|
||||||
|
|
||||||
|
class TCP_handler(socketserver.StreamRequestHandler):
|
||||||
|
def handle(self):
|
||||||
|
self.data = self.rfile.readline().strip()
|
||||||
|
|
||||||
|
message = self.data.decode('utf-8')
|
||||||
|
# print(message)
|
||||||
|
data = message.split('|')
|
||||||
|
if data[0] != Config.AUTHENTICATION:
|
||||||
|
self.wfile.write(b'No authentication')
|
||||||
|
logging.warning('TCP-%s-No authentication' %
|
||||||
|
self.client_address[0])
|
||||||
|
return None
|
||||||
|
|
||||||
|
global clean_timer
|
||||||
|
now = round(time.time() * 1000)
|
||||||
|
if now - clean_timer >= Config.TIME_LIMIT:
|
||||||
|
logging.info('Start cleaning memory...')
|
||||||
|
clean_timer = now
|
||||||
|
memory_clean(now)
|
||||||
|
|
||||||
|
self.wfile.write(data_swap(data[1:]).encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
|
def data_swap(data: list) -> str:
|
||||||
|
# data: list[str] = [command, ...]
|
||||||
|
if data[0] == '1':
|
||||||
|
# 开房
|
||||||
|
# data = ['1', name, song_unlock, ]
|
||||||
|
# song_unlock: base64 str
|
||||||
|
name = data[1]
|
||||||
|
song_unlock = base64.b64decode(data[2])
|
||||||
|
|
||||||
|
key = urandom(16)
|
||||||
|
with lock:
|
||||||
|
room_id = unique_random(room_id_dict)
|
||||||
|
|
||||||
|
room = Room()
|
||||||
|
room.room_id = room_id
|
||||||
|
room_id_dict[room_id] = room
|
||||||
|
|
||||||
|
player_id = unique_random(player_dict, 3)
|
||||||
|
player = Player()
|
||||||
|
player.player_id = player_id
|
||||||
|
player.set_player_name(name)
|
||||||
|
player_dict[player_id] = player
|
||||||
|
|
||||||
|
player.song_unlock = song_unlock
|
||||||
|
room.song_unlock = song_unlock
|
||||||
|
room.host_id = player_id
|
||||||
|
room.players[0] = player
|
||||||
|
room.player_num = 1
|
||||||
|
|
||||||
|
room_code = unique_random(
|
||||||
|
room_code_dict, random_func=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}
|
||||||
|
|
||||||
|
logging.info('TCP-Create room `%s` by player `%s`' % (room_code, name))
|
||||||
|
return '|'.join([str(x) for x in (0, room_code, room_id, token, base64.b64encode(key).decode('utf-8'), player_id)])
|
||||||
|
|
||||||
|
elif data[0] == '2':
|
||||||
|
# 入房
|
||||||
|
# data = ['2', name, song_unlock, room_code]
|
||||||
|
# song_unlock: base64 str
|
||||||
|
room_code = data[3].upper()
|
||||||
|
with lock:
|
||||||
|
if room_code not in room_code_dict:
|
||||||
|
# 房间号错误
|
||||||
|
return '1202'
|
||||||
|
room = room_code_dict[room_code]
|
||||||
|
|
||||||
|
if room.player_num == 4:
|
||||||
|
# 满人
|
||||||
|
return '1201'
|
||||||
|
elif room.state != 2:
|
||||||
|
# 无法加入
|
||||||
|
return '1205'
|
||||||
|
|
||||||
|
name = data[1]
|
||||||
|
song_unlock = base64.b64decode(data[2])
|
||||||
|
|
||||||
|
key = urandom(16)
|
||||||
|
with lock:
|
||||||
|
token = unique_random(link_play_data)
|
||||||
|
player_id = unique_random(player_dict, 3)
|
||||||
|
|
||||||
|
player = Player()
|
||||||
|
player.player_id = player_id
|
||||||
|
player.set_player_name(name)
|
||||||
|
player.token = token
|
||||||
|
player_dict[player_id] = player
|
||||||
|
player.song_unlock = song_unlock
|
||||||
|
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}
|
||||||
|
|
||||||
|
logging.info('TCP-Player `%s` joins room `%s`' % (name, room_code))
|
||||||
|
return '|'.join([str(x) for x in (0, room_code, room.room_id, token, base64.b64encode(key).decode('utf-8'), player_id, base64.b64encode(room.song_unlock).decode('utf-8'))])
|
||||||
|
|
||||||
|
elif data[0] == '3':
|
||||||
|
# 房间信息更新
|
||||||
|
# data = ['3', token]
|
||||||
|
token = int(data[1])
|
||||||
|
with lock:
|
||||||
|
if token in link_play_data:
|
||||||
|
r = link_play_data[token]
|
||||||
|
logging.info('TCP-Room `%s` info update' % room_code)
|
||||||
|
return '|'.join([str(x) for x in (0, r['room'].room_code, r['room'].room_id, base64.b64encode(r['key']).decode('utf-8'), r['room'].players[r['player_index']].player_id, base64.b64encode(r['room'].song_unlock).decode('utf-8'))])
|
||||||
|
else:
|
||||||
|
return '108'
|
||||||
|
|
||||||
|
|
||||||
|
def link_play(ip: str = Config.HOST, udp_port: int = Config.UDP_PORT, tcp_port: int = Config.TCP_PORT):
|
||||||
|
udp_server = socketserver.ThreadingUDPServer((ip, udp_port), UDP_handler)
|
||||||
|
tcp_server = socketserver.ThreadingTCPServer((ip, tcp_port), TCP_handler)
|
||||||
|
|
||||||
|
threads = [threading.Thread(target=udp_server.serve_forever), threading.Thread(
|
||||||
|
target=tcp_server.serve_forever)]
|
||||||
|
[t.start() for t in threads]
|
||||||
|
[t.join() for t in threads]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from .udp_config import Config
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
def b(value, length=1):
|
def b(value, length=1):
|
||||||
@@ -38,7 +38,7 @@ class Player:
|
|||||||
self.last_timestamp = 0
|
self.last_timestamp = 0
|
||||||
self.extra_command_queue = []
|
self.extra_command_queue = []
|
||||||
|
|
||||||
self.song_unlock = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
|
self.song_unlock: bytes = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
|
||||||
|
|
||||||
self.start_command_num = 0
|
self.start_command_num = 0
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from .udp_class import Room, bi
|
from .udp_class import Room, bi
|
||||||
from .udp_config import Config
|
from .config import Config
|
||||||
from .udp_sender import CommandSender
|
from .udp_sender import CommandSender
|
||||||
|
|
||||||
|
|
||||||
@@ -230,6 +230,9 @@ class CommandParser:
|
|||||||
self.room.countdown = Config.COUNTDOWM_TIME
|
self.room.countdown = Config.COUNTDOWM_TIME
|
||||||
self.room.timestamp = round(time.time() * 1000)
|
self.room.timestamp = round(time.time() * 1000)
|
||||||
self.room.state = 4
|
self.room.state = 4
|
||||||
|
if self.room.round_switch == 1:
|
||||||
|
# 将换房主时间提前到此刻
|
||||||
|
self.room.make_round()
|
||||||
|
|
||||||
if self.room.state == 4 or self.room.state == 5 or self.room.state == 6:
|
if self.room.state == 4 or self.room.state == 5 or self.room.state == 6:
|
||||||
timestamp = round(time.time() * 1000)
|
timestamp = round(time.time() * 1000)
|
||||||
@@ -279,8 +282,6 @@ class CommandParser:
|
|||||||
flag_13 = True
|
flag_13 = True
|
||||||
self.room.state = 1
|
self.room.state = 1
|
||||||
self.room.song_idx = 0xffff
|
self.room.song_idx = 0xffff
|
||||||
if self.room.round_switch == 1:
|
|
||||||
self.room.make_round()
|
|
||||||
|
|
||||||
for i in self.room.players:
|
for i in self.room.players:
|
||||||
i.timer = 0
|
i.timer = 0
|
||||||
@@ -311,7 +312,7 @@ class CommandParser:
|
|||||||
self.room.command_queue_length += 1
|
self.room.command_queue_length += 1
|
||||||
self.room.command_queue.append(x.command_12(self.player_index))
|
self.room.command_queue.append(x.command_12(self.player_index))
|
||||||
|
|
||||||
if self.room.state == 3:
|
if self.room.state == 3 or self.room.state == 2:
|
||||||
self.room.state = 1
|
self.room.state = 1
|
||||||
self.room.song_idx = 0xffff
|
self.room.song_idx = 0xffff
|
||||||
# self.room.command_queue_length += 1
|
# self.room.command_queue_length += 1
|
||||||
@@ -4,8 +4,9 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from logging.config import dictConfig
|
from logging.config import dictConfig
|
||||||
from multiprocessing import Process, set_start_method
|
from multiprocessing import Process, set_start_method
|
||||||
|
from traceback import format_exc
|
||||||
|
|
||||||
from flask import Flask, request, send_from_directory
|
from flask import Flask, make_response, request, send_from_directory
|
||||||
|
|
||||||
import api
|
import api
|
||||||
import server
|
import server
|
||||||
@@ -13,14 +14,20 @@ import server.init
|
|||||||
import web.index
|
import web.index
|
||||||
import web.login
|
import web.login
|
||||||
from core.constant import Constant
|
from core.constant import Constant
|
||||||
from core.download import UserDownload, initialize_songfile
|
from core.download import (UserDownload, get_only_3_song_ids,
|
||||||
|
initialize_songfile)
|
||||||
from core.error import ArcError
|
from core.error import ArcError
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from server.func import error_return
|
from server.func import error_return
|
||||||
from setting import Config
|
from setting import Config
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
wsgi_app = app.wsgi_app
|
|
||||||
|
# from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
# app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
|
||||||
|
# from flask_cors import CORS
|
||||||
|
# CORS(app, supports_credentials=True)
|
||||||
|
|
||||||
|
|
||||||
os.chdir(sys.path[0]) # 更改工作路径,以便于愉快使用相对路径
|
os.chdir(sys.path[0]) # 更改工作路径,以便于愉快使用相对路径
|
||||||
|
|
||||||
@@ -51,19 +58,32 @@ def download(file_path):
|
|||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
try:
|
||||||
x = UserDownload(c)
|
x = UserDownload(c)
|
||||||
x.file_path = file_path
|
x.token = request.args.get('t')
|
||||||
x.select_from_token(request.args.get('t'))
|
x.song_id, x.file_name = file_path.split('/', 1)
|
||||||
|
x.select_for_check()
|
||||||
if x.is_limited:
|
if x.is_limited:
|
||||||
raise ArcError('You have reached the download limit.', 903)
|
raise ArcError(
|
||||||
if x.is_valid:
|
'You have reached the download limit.', 903, status=403)
|
||||||
|
if not x.is_valid:
|
||||||
|
raise ArcError('Expired token.', status=403)
|
||||||
x.insert_user_download()
|
x.insert_user_download()
|
||||||
return send_from_directory(Constant.SONG_FILE_FOLDER_PATH, file_path, as_attachment=True)
|
# response = make_response()
|
||||||
|
# response.headers['Content-Type'] = 'application/octet-stream'
|
||||||
|
# response.headers['X-Accel-Redirect'] = '/nginx_download/' + file_path
|
||||||
|
# return response
|
||||||
|
return send_from_directory(Constant.SONG_FILE_FOLDER_PATH, file_path, as_attachment=True, conditional=True)
|
||||||
except ArcError as e:
|
except ArcError as e:
|
||||||
|
if Config.ALLOW_WARNING_LOG:
|
||||||
|
app.logger.warning(format_exc())
|
||||||
return error_return(e)
|
return error_return(e)
|
||||||
return error_return()
|
return error_return()
|
||||||
|
|
||||||
|
|
||||||
def tcp_server_run():
|
def tcp_server_run():
|
||||||
|
if False:
|
||||||
|
from gevent.pywsgi import WSGIServer
|
||||||
|
WSGIServer(("127.0.0.1", 5000), app).serve_forever()
|
||||||
|
else:
|
||||||
if Config.SSL_CERT and Config.SSL_KEY:
|
if Config.SSL_CERT and Config.SSL_KEY:
|
||||||
app.run(Config.HOST, Config.PORT, ssl_context=(
|
app.run(Config.HOST, Config.PORT, ssl_context=(
|
||||||
Config.SSL_CERT, Config.SSL_KEY))
|
Config.SSL_CERT, Config.SSL_KEY))
|
||||||
@@ -100,8 +120,8 @@ def main():
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if Config.ALLOW_LOG_INFO:
|
if Config.ALLOW_INFO_LOG:
|
||||||
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file']
|
log_dict['root']['handlers'].append('info_file')
|
||||||
log_dict['handlers']['info_file'] = {
|
log_dict['handlers']['info_file'] = {
|
||||||
"class": "logging.handlers.RotatingFileHandler",
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
"maxBytes": 1024 * 1024,
|
"maxBytes": 1024 * 1024,
|
||||||
@@ -111,6 +131,17 @@ def main():
|
|||||||
"formatter": "default",
|
"formatter": "default",
|
||||||
"filename": "./log/info.log"
|
"filename": "./log/info.log"
|
||||||
}
|
}
|
||||||
|
if Config.ALLOW_WARNING_LOG:
|
||||||
|
log_dict['root']['handlers'].append('warning_file')
|
||||||
|
log_dict['handlers']['warning_file'] = {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"maxBytes": 1024 * 1024,
|
||||||
|
"backupCount": 1,
|
||||||
|
"encoding": "utf-8",
|
||||||
|
"level": "WARNING",
|
||||||
|
"formatter": "default",
|
||||||
|
"filename": "./log/warning.log"
|
||||||
|
}
|
||||||
|
|
||||||
dictConfig(log_dict)
|
dictConfig(log_dict)
|
||||||
|
|
||||||
@@ -122,17 +153,20 @@ def main():
|
|||||||
app.logger.info("Start to initialize song data...")
|
app.logger.info("Start to initialize song data...")
|
||||||
try:
|
try:
|
||||||
initialize_songfile()
|
initialize_songfile()
|
||||||
|
get_only_3_song_ids()
|
||||||
app.logger.info('Complete!')
|
app.logger.info('Complete!')
|
||||||
except:
|
except:
|
||||||
app.logger.warning('Initialization error!')
|
app.logger.warning('Initialization error!')
|
||||||
|
|
||||||
if Config.UDP_PORT and Config.UDP_PORT != '':
|
if Config.LINKPLAY_HOST and Config.SET_LINKPLAY_SERVER_AS_SUB_PROCESS:
|
||||||
from server.multiplayer import conn2
|
from linkplay_server import link_play
|
||||||
from udpserver.udp_main import link_play
|
|
||||||
process = [Process(target=link_play, args=(
|
process = [Process(target=link_play, args=(
|
||||||
conn2, Config.HOST, int(Config.UDP_PORT)))]
|
Config.LINKPLAY_HOST, int(Config.LINKPLAY_UDP_PORT), int(Config.LINKPLAY_TCP_PORT)))]
|
||||||
[p.start() for p in process]
|
[p.start() for p in process]
|
||||||
app.logger.info("UDP server is running...")
|
app.logger.info("Link Play UDP server is running on " +
|
||||||
|
Config.LINKPLAY_HOST + ':' + str(Config.LINKPLAY_UDP_PORT) + " ...")
|
||||||
|
app.logger.info("Link Play TCP server is running on " +
|
||||||
|
Config.LINKPLAY_HOST + ':' + str(Config.LINKPLAY_TCP_PORT) + " ...")
|
||||||
tcp_server_run()
|
tcp_server_run()
|
||||||
[p.join() for p in process]
|
[p.join() for p in process]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
flask>=2.0
|
flask>=2.0.2
|
||||||
cryptography
|
cryptography>=35.0.0
|
||||||
|
|||||||
9
latest version/run_linkplay_server.py
Normal file
9
latest version/run_linkplay_server.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import linkplay_server
|
||||||
|
|
||||||
|
os.chdir(sys.path[0])
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
linkplay_server.link_play()
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from setting import Config
|
from setting import Config
|
||||||
from . import user
|
|
||||||
from . import auth
|
from . import (auth, course, friend, multiplayer, others, present, purchase,
|
||||||
from . import friend
|
score, user, world)
|
||||||
from . import score
|
|
||||||
from . import world
|
|
||||||
from . import purchase
|
|
||||||
from . import present
|
|
||||||
from . import others
|
|
||||||
from . import multiplayer
|
|
||||||
from . import course
|
|
||||||
|
|
||||||
bp = Blueprint('server', __name__, url_prefix=Config.GAME_API_PREFIX)
|
bp = Blueprint('server', __name__, url_prefix=Config.GAME_API_PREFIX)
|
||||||
bp.register_blueprint(user.bp)
|
bp.register_blueprint(user.bp)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import base64
|
import base64
|
||||||
import functools
|
from functools import wraps
|
||||||
|
|
||||||
from core.error import ArcError, NoAccess
|
from core.error import ArcError, NoAccess
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
@@ -7,22 +7,22 @@ from core.user import UserAuth, UserLogin
|
|||||||
from flask import Blueprint, jsonify, request
|
from flask import Blueprint, jsonify, request
|
||||||
from setting import Config
|
from setting import Config
|
||||||
|
|
||||||
from .func import error_return
|
from .func import arc_try, error_return
|
||||||
|
|
||||||
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/login', methods=['POST']) # 登录接口
|
@bp.route('/login', methods=['POST']) # 登录接口
|
||||||
|
@arc_try
|
||||||
def login():
|
def login():
|
||||||
if 'AppVersion' in request.headers: # 版本检查
|
|
||||||
if Config.ALLOW_APPVERSION:
|
|
||||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
|
||||||
return error_return(NoAccess('Wrong app version.', 1203))
|
|
||||||
|
|
||||||
headers = request.headers
|
headers = request.headers
|
||||||
|
if 'AppVersion' in headers: # 版本检查
|
||||||
|
if Config.ALLOW_APPVERSION:
|
||||||
|
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||||
|
raise NoAccess('Wrong app version.', 1203)
|
||||||
|
|
||||||
request.form['grant_type']
|
request.form['grant_type']
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
id_pwd = headers['Authorization']
|
id_pwd = headers['Authorization']
|
||||||
id_pwd = base64.b64decode(id_pwd[6:]).decode()
|
id_pwd = base64.b64decode(id_pwd[6:]).decode()
|
||||||
name, password = id_pwd.split(':', 1)
|
name, password = id_pwd.split(':', 1)
|
||||||
@@ -35,16 +35,12 @@ def login():
|
|||||||
user.login(name, password, device_id, request.remote_addr)
|
user.login(name, password, device_id, request.remote_addr)
|
||||||
|
|
||||||
return jsonify({"success": True, "token_type": "Bearer", 'user_id': user.user_id, 'access_token': user.token})
|
return jsonify({"success": True, "token_type": "Bearer", 'user_id': user.user_id, 'access_token': user.token})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
def auth_required(request):
|
def auth_required(request):
|
||||||
# arcaea登录验证,写成了修饰器
|
# arcaea登录验证,写成了修饰器
|
||||||
def decorator(view):
|
def decorator(view):
|
||||||
@functools.wraps(view)
|
@wraps(view)
|
||||||
def wrapped_view(*args, **kwargs):
|
def wrapped_view(*args, **kwargs):
|
||||||
|
|
||||||
headers = request.headers
|
headers = request.headers
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ from core.user import UserOnline
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import error_return, success_return
|
from .func import arc_try, success_return
|
||||||
|
|
||||||
bp = Blueprint('course', __name__, url_prefix='/course')
|
bp = Blueprint('course', __name__, url_prefix='/course')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me', methods=['GET'])
|
@bp.route('/me', methods=['GET'])
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def course_me(user_id):
|
def course_me(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
core = ItemCore(c)
|
core = ItemCore(c)
|
||||||
core.item_id = 'core_course_skip_purchase'
|
core.item_id = 'core_course_skip_purchase'
|
||||||
@@ -28,6 +28,3 @@ def course_me(user_id):
|
|||||||
"stamina_cost": Constant.COURSE_STAMINA_COST,
|
"stamina_cost": Constant.COURSE_STAMINA_COST,
|
||||||
"course_skip_purchase_ticket": core.amount
|
"course_skip_purchase_ticket": core.amount
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
from flask import Blueprint, request
|
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from core.error import ArcError
|
|
||||||
from core.user import UserOnline, code_get_id
|
from core.user import UserOnline, code_get_id
|
||||||
from .func import error_return, success_return
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
|
from .func import arc_try, success_return
|
||||||
|
|
||||||
bp = Blueprint('friend', __name__, url_prefix='/friend')
|
bp = Blueprint('friend', __name__, url_prefix='/friend')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/add', methods=['POST']) # 加好友
|
@bp.route('/me/add', methods=['POST']) # 加好友
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def add_friend(user_id):
|
def add_friend(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
friend_code = request.form['friend_code']
|
friend_code = request.form['friend_code']
|
||||||
friend_id = code_get_id(c, friend_code)
|
friend_id = code_get_id(c, friend_code)
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
@@ -24,16 +24,13 @@ def add_friend(user_id):
|
|||||||
"createdAt": "2020-09-06T10:05:18.471Z",
|
"createdAt": "2020-09-06T10:05:18.471Z",
|
||||||
"friends": user.friends
|
"friends": user.friends
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/delete', methods=['POST']) # 删好友
|
@bp.route('/me/delete', methods=['POST']) # 删好友
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def delete_friend(user_id):
|
def delete_friend(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
friend_id = int(request.form['friend_id'])
|
friend_id = int(request.form['friend_id'])
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
user.delete_friend(friend_id)
|
user.delete_friend(friend_id)
|
||||||
@@ -44,6 +41,3 @@ def delete_friend(user_id):
|
|||||||
"createdAt": "2020-09-06T10:05:18.471Z",
|
"createdAt": "2020-09-06T10:05:18.471Z",
|
||||||
"friends": user.friends
|
"friends": user.friends
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
from flask import jsonify
|
from functools import wraps
|
||||||
from core.error import ArcError
|
from traceback import format_exc
|
||||||
|
|
||||||
default_error = ArcError('Unknown Error')
|
from core.error import ArcError
|
||||||
|
from flask import current_app, jsonify
|
||||||
|
from setting import Config
|
||||||
|
|
||||||
|
default_error = ArcError('Unknown Error', status=500)
|
||||||
|
|
||||||
|
|
||||||
def error_return(e: ArcError = default_error): # 错误返回
|
def error_return(e: ArcError = default_error): # 错误返回
|
||||||
@@ -56,7 +60,7 @@ def error_return(e: ArcError = default_error): # 错误返回
|
|||||||
if e.extra_data:
|
if e.extra_data:
|
||||||
r['extra'] = e.extra_data
|
r['extra'] = e.extra_data
|
||||||
|
|
||||||
return jsonify(r)
|
return jsonify(r), e.status
|
||||||
|
|
||||||
|
|
||||||
def success_return(value=None):
|
def success_return(value=None):
|
||||||
@@ -64,3 +68,21 @@ def success_return(value=None):
|
|||||||
if value is not None:
|
if value is not None:
|
||||||
r['value'] = value
|
r['value'] = value
|
||||||
return jsonify(r)
|
return jsonify(r)
|
||||||
|
|
||||||
|
|
||||||
|
def arc_try(view):
|
||||||
|
'''替代try/except,记录`ArcError`为warning'''
|
||||||
|
@wraps(view)
|
||||||
|
def wrapped_view(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
data = view(*args, **kwargs)
|
||||||
|
if data is None:
|
||||||
|
return error_return()
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
except ArcError as e:
|
||||||
|
if Config.ALLOW_WARNING_LOG:
|
||||||
|
current_app.logger.warning(format_exc())
|
||||||
|
return error_return(e)
|
||||||
|
|
||||||
|
return wrapped_view
|
||||||
|
|||||||
@@ -1,49 +1,43 @@
|
|||||||
from multiprocessing import Pipe
|
|
||||||
|
|
||||||
from core.error import ArcError
|
from core.error import ArcError
|
||||||
from core.linkplay import LocalMultiPlayer, Player, Room
|
from core.linkplay import Player, RemoteMultiPlayer, Room
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
from setting import Config
|
from setting import Config
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import error_return, success_return
|
from .func import arc_try, success_return
|
||||||
|
|
||||||
bp = Blueprint('multiplayer', __name__, url_prefix='/multiplayer')
|
bp = Blueprint('multiplayer', __name__, url_prefix='/multiplayer')
|
||||||
|
|
||||||
conn1, conn2 = Pipe()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/room/create', methods=['POST']) # 创建房间
|
@bp.route('/me/room/create', methods=['POST']) # 创建房间
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def room_create(user_id):
|
def room_create(user_id):
|
||||||
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
if not Config.LINKPLAY_HOST:
|
||||||
return error_return(ArcError('The local udp server is down.', 151)), 404
|
raise ArcError('The link play server is unavailable.', 151, status=404)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
x = RemoteMultiPlayer()
|
||||||
x = LocalMultiPlayer(conn1)
|
|
||||||
user = Player(c, user_id)
|
user = Player(c, user_id)
|
||||||
user.get_song_unlock(request.json['clientSongMap'])
|
user.get_song_unlock(request.json['clientSongMap'])
|
||||||
x.create_room(user)
|
x.create_room(user)
|
||||||
r = x.to_dict()
|
r = x.to_dict()
|
||||||
r['endPoint'] = request.host.split(
|
r['endPoint'] = request.host.split(
|
||||||
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
|
':')[0] if Config.LINKPLAY_DISPLAY_HOST == '' else Config.LINKPLAY_DISPLAY_HOST
|
||||||
r['port'] = int(Config.UDP_PORT)
|
r['port'] = int(Config.LINKPLAY_UDP_PORT)
|
||||||
return success_return(r)
|
return success_return(r)
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e), 400
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/room/join/<room_code>', methods=['POST']) # 加入房间
|
@bp.route('/me/room/join/<room_code>', methods=['POST']) # 加入房间
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def room_join(user_id, room_code):
|
def room_join(user_id, room_code):
|
||||||
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
if not Config.LINKPLAY_HOST:
|
||||||
return error_return(ArcError('The local udp server is down.', 151)), 404
|
raise ArcError('The link play server is unavailable.', 151, status=404)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
x = RemoteMultiPlayer()
|
||||||
x = LocalMultiPlayer(conn1)
|
|
||||||
user = Player(c, user_id)
|
user = Player(c, user_id)
|
||||||
user.get_song_unlock(request.json['clientSongMap'])
|
user.get_song_unlock(request.json['clientSongMap'])
|
||||||
room = Room()
|
room = Room()
|
||||||
@@ -51,31 +45,25 @@ def room_join(user_id, room_code):
|
|||||||
x.join_room(room, user)
|
x.join_room(room, user)
|
||||||
r = x.to_dict()
|
r = x.to_dict()
|
||||||
r['endPoint'] = request.host.split(
|
r['endPoint'] = request.host.split(
|
||||||
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
|
':')[0] if Config.LINKPLAY_DISPLAY_HOST == '' else Config.LINKPLAY_DISPLAY_HOST
|
||||||
r['port'] = int(Config.UDP_PORT)
|
r['port'] = int(Config.LINKPLAY_UDP_PORT)
|
||||||
return success_return(r)
|
return success_return(r)
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e), 400
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/update', methods=['POST']) # 更新房间
|
@bp.route('/me/update', methods=['POST']) # 更新房间
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def multiplayer_update(user_id):
|
def multiplayer_update(user_id):
|
||||||
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
if not Config.LINKPLAY_HOST:
|
||||||
return error_return(ArcError('The local udp server is down.', 151)), 404
|
raise ArcError('The link play server is unavailable.', 151, status=404)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
x = RemoteMultiPlayer()
|
||||||
x = LocalMultiPlayer(conn1)
|
|
||||||
user = Player(c, user_id)
|
user = Player(c, user_id)
|
||||||
user.token = int(request.json['token'])
|
user.token = int(request.json['token'])
|
||||||
x.update_room(user)
|
x.update_room(user)
|
||||||
r = x.to_dict()
|
r = x.to_dict()
|
||||||
r['endPoint'] = request.host.split(
|
r['endPoint'] = request.host.split(
|
||||||
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
|
':')[0] if Config.LINKPLAY_DISPLAY_HOST == '' else Config.LINKPLAY_DISPLAY_HOST
|
||||||
r['port'] = int(Config.UDP_PORT)
|
r['port'] = int(Config.LINKPLAY_UDP_PORT)
|
||||||
return success_return(r)
|
return success_return(r)
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e), 400
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from flask import Blueprint, jsonify, request
|
|||||||
from werkzeug.datastructures import ImmutableMultiDict
|
from werkzeug.datastructures import ImmutableMultiDict
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import error_return, success_return
|
from .func import arc_try, error_return, success_return
|
||||||
from .present import present_info
|
from .present import present_info
|
||||||
from .purchase import bundle_pack, bundle_bundle
|
from .purchase import bundle_pack, bundle_bundle
|
||||||
from .score import song_score_friend
|
from .score import song_score_friend
|
||||||
@@ -27,9 +27,9 @@ def game_info():
|
|||||||
|
|
||||||
@bp.route('/serve/download/me/song', methods=['GET']) # 歌曲下载
|
@bp.route('/serve/download/me/song', methods=['GET']) # 歌曲下载
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def download_song(user_id):
|
def download_song(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = DownloadList(c, UserOnline(c, user_id))
|
x = DownloadList(c, UserOnline(c, user_id))
|
||||||
x.song_ids = request.args.getlist('sid')
|
x.song_ids = request.args.getlist('sid')
|
||||||
x.url_flag = json.loads(request.args.get('url', 'true'))
|
x.url_flag = json.loads(request.args.get('url', 'true'))
|
||||||
@@ -39,9 +39,6 @@ def download_song(user_id):
|
|||||||
|
|
||||||
x.add_songs()
|
x.add_songs()
|
||||||
return success_return(x.urls)
|
return success_return(x.urls)
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/finale/progress', methods=['GET'])
|
@bp.route('/finale/progress', methods=['GET'])
|
||||||
@@ -85,16 +82,19 @@ def aggregate():
|
|||||||
{key: value[0] for key, value in parse_qs(urlparse(endpoint).query).items()})
|
{key: value[0] for key, value in parse_qs(urlparse(endpoint).query).items()})
|
||||||
|
|
||||||
resp_t = map_dict[urlparse(endpoint).path]()
|
resp_t = map_dict[urlparse(endpoint).path]()
|
||||||
|
if isinstance(resp_t, tuple):
|
||||||
|
# The response may be a tuple, if it is an error response
|
||||||
|
resp_t = resp_t[0]
|
||||||
|
|
||||||
if hasattr(resp_t, "response"):
|
if hasattr(resp_t, "response"):
|
||||||
resp_t = resp_t.response[0].decode().rstrip('\n')
|
resp_t = resp_t.response[0].decode().rstrip('\n')
|
||||||
resp = json.loads(resp_t)
|
resp = json.loads(resp_t)
|
||||||
|
|
||||||
if hasattr(resp, 'get') and resp.get('success') is False:
|
if hasattr(resp, 'get') and resp.get('success') is False:
|
||||||
finally_response = {'success': False, 'error_code': 7, 'extra': {
|
finally_response = {'success': False, 'error_code': resp.get(
|
||||||
"id": i['id'], 'error_code': resp.get('error_code')}}
|
'error_code'), 'id': i['id']}
|
||||||
if "extra" in resp:
|
if "extra" in resp:
|
||||||
finally_response['extra']['extra'] = resp['extra']
|
finally_response['extra'] = resp['extra']
|
||||||
#request = request_
|
#request = request_
|
||||||
return jsonify(finally_response)
|
return jsonify(finally_response)
|
||||||
|
|
||||||
|
|||||||
@@ -5,34 +5,28 @@ from core.user import UserOnline
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import error_return, success_return
|
from .func import arc_try, error_return, success_return
|
||||||
|
|
||||||
bp = Blueprint('present', __name__, url_prefix='/present')
|
bp = Blueprint('present', __name__, url_prefix='/present')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me', methods=['GET']) # 用户奖励信息
|
@bp.route('/me', methods=['GET']) # 用户奖励信息
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def present_info(user_id):
|
def present_info(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = UserPresentList(c, UserOnline(c, user_id))
|
x = UserPresentList(c, UserOnline(c, user_id))
|
||||||
x.select_user_presents()
|
x.select_user_presents()
|
||||||
|
|
||||||
return success_return(x.to_dict_list())
|
return success_return(x.to_dict_list())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/claim/<present_id>', methods=['POST']) # 礼物确认
|
@bp.route('/me/claim/<present_id>', methods=['POST']) # 礼物确认
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def claim_present(user_id, present_id):
|
def claim_present(user_id, present_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = UserPresent(c, UserOnline(c, user_id))
|
x = UserPresent(c, UserOnline(c, user_id))
|
||||||
x.claim_user_present(present_id)
|
x.claim_user_present(present_id)
|
||||||
|
|
||||||
return success_return()
|
return success_return()
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from core.error import ArcError, ItemUnavailable
|
from core.error import InputError, ItemUnavailable, PostError
|
||||||
from core.item import ItemFactory, Stamina6
|
from core.item import ItemFactory, Stamina6
|
||||||
from core.purchase import Purchase, PurchaseList
|
from core.purchase import Purchase, PurchaseList
|
||||||
from core.redeem import UserRedeem
|
from core.redeem import UserRedeem
|
||||||
@@ -9,35 +9,29 @@ from core.user import UserOnline
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import error_return, success_return
|
from .func import arc_try, success_return
|
||||||
|
|
||||||
bp = Blueprint('purchase', __name__, url_prefix='/purchase')
|
bp = Blueprint('purchase', __name__, url_prefix='/purchase')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/bundle/pack', methods=['GET']) # 曲包信息
|
@bp.route('/bundle/pack', methods=['GET']) # 曲包信息
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def bundle_pack(user_id):
|
def bundle_pack(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = PurchaseList(c, UserOnline(c, user_id)
|
x = PurchaseList(c, UserOnline(c, user_id)
|
||||||
).select_from_type('pack')
|
).select_from_type('pack')
|
||||||
return success_return(x.to_dict_list())
|
return success_return(x.to_dict_list())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/bundle/single', methods=['GET']) # 单曲购买信息获取
|
@bp.route('/bundle/single', methods=['GET']) # 单曲购买信息获取
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def get_single(user_id):
|
def get_single(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = PurchaseList(c, UserOnline(c, user_id)
|
x = PurchaseList(c, UserOnline(c, user_id)
|
||||||
).select_from_type('single')
|
).select_from_type('single')
|
||||||
return success_return(x.to_dict_list())
|
return success_return(x.to_dict_list())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/bundle/bundle', methods=['GET']) # 捆绑包
|
@bp.route('/bundle/bundle', methods=['GET']) # 捆绑包
|
||||||
@@ -72,9 +66,9 @@ def bundle_bundle():
|
|||||||
|
|
||||||
@bp.route('/me/pack', methods=['POST']) # 曲包和单曲购买
|
@bp.route('/me/pack', methods=['POST']) # 曲包和单曲购买
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def buy_pack_or_single(user_id):
|
def buy_pack_or_single(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
if 'pack_id' in request.form:
|
if 'pack_id' in request.form:
|
||||||
purchase_name = request.form['pack_id']
|
purchase_name = request.form['pack_id']
|
||||||
elif 'single_id' in request.form:
|
elif 'single_id' in request.form:
|
||||||
@@ -92,18 +86,15 @@ def buy_pack_or_single(user_id):
|
|||||||
'singles': x.user.singles,
|
'singles': x.user.singles,
|
||||||
'characters': x.user.characters_list
|
'characters': x.user.characters_list
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/item', methods=['POST']) # 特殊购买,world模式boost和stamina
|
@bp.route('/me/item', methods=['POST']) # 特殊购买,world模式boost和stamina
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def buy_special(user_id):
|
def buy_special(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
if 'item_id' not in request.form:
|
if 'item_id' not in request.form:
|
||||||
return error_return()
|
raise PostError('`item_id` is required')
|
||||||
item_id = request.form['item_id']
|
item_id = request.form['item_id']
|
||||||
|
|
||||||
x = Purchase(c, UserOnline(c, user_id))
|
x = Purchase(c, UserOnline(c, user_id))
|
||||||
@@ -121,18 +112,15 @@ def buy_special(user_id):
|
|||||||
r['max_stamina_ts'] = x.user.stamina.max_stamina_ts
|
r['max_stamina_ts'] = x.user.stamina.max_stamina_ts
|
||||||
r['world_mode_locked_end_ts'] = -1
|
r['world_mode_locked_end_ts'] = -1
|
||||||
return success_return(r)
|
return success_return(r)
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/stamina/<buy_stamina_type>', methods=['POST']) # 购买体力
|
@bp.route('/me/stamina/<buy_stamina_type>', methods=['POST']) # 购买体力
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def purchase_stamina(user_id, buy_stamina_type):
|
def purchase_stamina(user_id, buy_stamina_type):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
if buy_stamina_type != 'fragment':
|
if buy_stamina_type != 'fragment':
|
||||||
return error_return()
|
raise InputError('Invalid type of buying stamina')
|
||||||
|
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
user.select_user_one_column('next_fragstam_ts', -1)
|
user.select_user_one_column('next_fragstam_ts', -1)
|
||||||
@@ -151,20 +139,14 @@ def purchase_stamina(user_id, buy_stamina_type):
|
|||||||
"next_fragstam_ts": user.next_fragstam_ts,
|
"next_fragstam_ts": user.next_fragstam_ts,
|
||||||
'world_mode_locked_end_ts': -1
|
'world_mode_locked_end_ts': -1
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/redeem', methods=['POST']) # 兑换码
|
@bp.route('/me/redeem', methods=['POST']) # 兑换码
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def redeem(user_id):
|
def redeem(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = UserRedeem(c, UserOnline(c, user_id))
|
x = UserRedeem(c, UserOnline(c, user_id))
|
||||||
x.claim_user_redeem(request.form['code'])
|
x.claim_user_redeem(request.form['code'])
|
||||||
|
|
||||||
return success_return({"coupon": "fragment" + str(x.fragment) if x.fragment > 0 else ""})
|
return success_return({"coupon": "fragment" + str(x.fragment) if x.fragment > 0 else ""})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from time import time
|
from time import time
|
||||||
from core.course import CoursePlay
|
from core.course import CoursePlay
|
||||||
|
|
||||||
from core.error import ArcError, InputError
|
from core.error import InputError
|
||||||
from core.rank import RankList
|
from core.rank import RankList
|
||||||
from core.score import UserPlay
|
from core.score import UserPlay
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
@@ -9,7 +9,7 @@ from core.user import UserOnline
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import error_return, success_return
|
from .func import arc_try, success_return
|
||||||
|
|
||||||
bp = Blueprint('score', __name__, url_prefix='/score')
|
bp = Blueprint('score', __name__, url_prefix='/score')
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ def score_token():
|
|||||||
|
|
||||||
@bp.route('/token/world', methods=['GET']) # 世界模式成绩上传所需的token
|
@bp.route('/token/world', methods=['GET']) # 世界模式成绩上传所需的token
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def score_token_world(user_id):
|
def score_token_world(user_id):
|
||||||
|
|
||||||
stamina_multiply = int(
|
stamina_multiply = int(
|
||||||
@@ -30,7 +31,6 @@ def score_token_world(user_id):
|
|||||||
prog_boost_multiply = int(
|
prog_boost_multiply = int(
|
||||||
request.args['prog_boost_multiply']) if 'prog_boost_multiply' in request.args else 0
|
request.args['prog_boost_multiply']) if 'prog_boost_multiply' in request.args else 0
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = UserPlay(c, UserOnline(c, user_id))
|
x = UserPlay(c, UserOnline(c, user_id))
|
||||||
x.song.set_chart(request.args['song_id'], int(
|
x.song.set_chart(request.args['song_id'], int(
|
||||||
request.args['difficulty']))
|
request.args['difficulty']))
|
||||||
@@ -42,16 +42,13 @@ def score_token_world(user_id):
|
|||||||
"token": x.song_token
|
"token": x.song_token
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/token/course', methods=['GET']) # 课题模式成绩上传所需的token
|
@bp.route('/token/course', methods=['GET']) # 课题模式成绩上传所需的token
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def score_token_course(user_id):
|
def score_token_course(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
use_course_skip_purchase = request.args.get(
|
use_course_skip_purchase = request.args.get(
|
||||||
'use_course_skip_purchase', 'false') == 'true'
|
'use_course_skip_purchase', 'false') == 'true'
|
||||||
|
|
||||||
@@ -83,16 +80,13 @@ def score_token_course(user_id):
|
|||||||
"token": user_play.song_token,
|
"token": user_play.song_token,
|
||||||
'status': status
|
'status': status
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/song', methods=['POST']) # 成绩上传
|
@bp.route('/song', methods=['POST']) # 成绩上传
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def song_score_post(user_id):
|
def song_score_post(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
x = UserPlay(c, UserOnline(c, user_id))
|
x = UserPlay(c, UserOnline(c, user_id))
|
||||||
x.song_token = request.form['song_token']
|
x.song_token = request.form['song_token']
|
||||||
x.song_hash = request.form['song_hash']
|
x.song_hash = request.form['song_hash']
|
||||||
@@ -106,51 +100,39 @@ def song_score_post(user_id):
|
|||||||
raise InputError('Invalid score.', 107)
|
raise InputError('Invalid score.', 107)
|
||||||
x.upload_score()
|
x.upload_score()
|
||||||
return success_return(x.to_dict())
|
return success_return(x.to_dict())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/song', methods=['GET']) # TOP20
|
@bp.route('/song', methods=['GET']) # TOP20
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def song_score_top(user_id):
|
def song_score_top(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
rank_list = RankList(c)
|
rank_list = RankList(c)
|
||||||
rank_list.song.set_chart(request.args.get(
|
rank_list.song.set_chart(request.args.get(
|
||||||
'song_id'), request.args.get('difficulty'))
|
'song_id'), request.args.get('difficulty'))
|
||||||
rank_list.select_top()
|
rank_list.select_top()
|
||||||
return success_return(rank_list.to_dict_list())
|
return success_return(rank_list.to_dict_list())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/song/me', methods=['GET']) # 我的排名,默认最多20
|
@bp.route('/song/me', methods=['GET']) # 我的排名,默认最多20
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def song_score_me(user_id):
|
def song_score_me(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
rank_list = RankList(c)
|
rank_list = RankList(c)
|
||||||
rank_list.song.set_chart(request.args.get(
|
rank_list.song.set_chart(request.args.get(
|
||||||
'song_id'), request.args.get('difficulty'))
|
'song_id'), request.args.get('difficulty'))
|
||||||
rank_list.select_me(UserOnline(c, user_id))
|
rank_list.select_me(UserOnline(c, user_id))
|
||||||
return success_return(rank_list.to_dict_list())
|
return success_return(rank_list.to_dict_list())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/song/friend', methods=['GET']) # 好友排名,默认最多50
|
@bp.route('/song/friend', methods=['GET']) # 好友排名,默认最多50
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def song_score_friend(user_id):
|
def song_score_friend(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
rank_list = RankList(c)
|
rank_list = RankList(c)
|
||||||
rank_list.song.set_chart(request.args.get(
|
rank_list.song.set_chart(request.args.get(
|
||||||
'song_id'), request.args.get('difficulty'))
|
'song_id'), request.args.get('difficulty'))
|
||||||
rank_list.select_friend(UserOnline(c, user_id))
|
rank_list.select_friend(UserOnline(c, user_id))
|
||||||
return success_return(rank_list.to_dict_list())
|
return success_return(rank_list.to_dict_list())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ from flask import Blueprint, request
|
|||||||
from setting import Config
|
from setting import Config
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
from .func import error_return, success_return
|
from .func import arc_try, success_return
|
||||||
|
|
||||||
bp = Blueprint('user', __name__, url_prefix='/user')
|
bp = Blueprint('user', __name__, url_prefix='/user')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('', methods=['POST']) # 注册接口
|
@bp.route('', methods=['POST']) # 注册接口
|
||||||
|
@arc_try
|
||||||
def register():
|
def register():
|
||||||
if 'AppVersion' in request.headers: # 版本检查
|
if 'AppVersion' in request.headers: # 版本检查
|
||||||
if Config.ALLOW_APPVERSION:
|
if Config.ALLOW_APPVERSION:
|
||||||
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
if request.headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||||
return error_return(NoAccess('Wrong app version.', 1203))
|
raise NoAccess('Wrong app version.', 1203)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
new_user = UserRegister(c)
|
new_user = UserRegister(c)
|
||||||
new_user.set_name(request.form['name'])
|
new_user.set_name(request.form['name'])
|
||||||
new_user.set_password(request.form['password'])
|
new_user.set_password(request.form['password'])
|
||||||
@@ -38,76 +38,61 @@ def register():
|
|||||||
user.login(new_user.name, new_user.password,
|
user.login(new_user.name, new_user.password,
|
||||||
device_id, request.remote_addr)
|
device_id, request.remote_addr)
|
||||||
return success_return({'user_id': user.user_id, 'access_token': user.token})
|
return success_return({'user_id': user.user_id, 'access_token': user.token})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me', methods=['GET']) # 用户信息
|
@bp.route('/me', methods=['GET']) # 用户信息
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def user_me(user_id):
|
def user_me(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
return success_return(UserOnline(c, user_id).to_dict())
|
return success_return(UserOnline(c, user_id).to_dict())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/character', methods=['POST']) # 角色切换
|
@bp.route('/me/character', methods=['POST']) # 角色切换
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def character_change(user_id):
|
def character_change(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
user.change_character(
|
user.change_character(
|
||||||
int(request.form['character']), request.form['skill_sealed'] == 'true')
|
int(request.form['character']), request.form['skill_sealed'] == 'true')
|
||||||
|
|
||||||
return success_return({'user_id': user.user_id, 'character': user.character.character_id})
|
return success_return({'user_id': user.user_id, 'character': user.character.character_id})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
# 角色觉醒切换
|
# 角色觉醒切换
|
||||||
@bp.route('/me/character/<int:character_id>/toggle_uncap', methods=['POST'])
|
@bp.route('/me/character/<int:character_id>/toggle_uncap', methods=['POST'])
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def toggle_uncap(user_id, character_id):
|
def toggle_uncap(user_id, character_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = User()
|
user = User()
|
||||||
user.user_id = user_id
|
user.user_id = user_id
|
||||||
character = UserCharacter(c, character_id)
|
character = UserCharacter(c, character_id)
|
||||||
character.change_uncap_override(user)
|
character.change_uncap_override(user)
|
||||||
character.select_character_info(user)
|
character.select_character_info(user)
|
||||||
return success_return({'user_id': user.user_id, 'character': [character.to_dict()]})
|
return success_return({'user_id': user.user_id, 'character': [character.to_dict()]})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
# 角色觉醒
|
# 角色觉醒
|
||||||
@bp.route('/me/character/<int:character_id>/uncap', methods=['POST'])
|
@bp.route('/me/character/<int:character_id>/uncap', methods=['POST'])
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def character_first_uncap(user_id, character_id):
|
def character_first_uncap(user_id, character_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
character = UserCharacter(c, character_id)
|
character = UserCharacter(c, character_id)
|
||||||
character.select_character_info(user)
|
character.select_character_info(user)
|
||||||
character.character_uncap(user)
|
character.character_uncap(user)
|
||||||
return success_return({'user_id': user.user_id, 'character': [character.to_dict()], 'cores': user.cores})
|
return success_return({'user_id': user.user_id, 'character': [character.to_dict()], 'cores': user.cores})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
# 角色使用以太之滴
|
# 角色使用以太之滴
|
||||||
@bp.route('/me/character/<int:character_id>/exp', methods=['POST'])
|
@bp.route('/me/character/<int:character_id>/exp', methods=['POST'])
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def character_exp(user_id, character_id):
|
def character_exp(user_id, character_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
character = UserCharacter(c, character_id)
|
character = UserCharacter(c, character_id)
|
||||||
character.select_character_info(user)
|
character.select_character_info(user)
|
||||||
@@ -115,32 +100,26 @@ def character_exp(user_id, character_id):
|
|||||||
core.amount = - int(request.form['amount'])
|
core.amount = - int(request.form['amount'])
|
||||||
core.item_id = 'core_generic'
|
core.item_id = 'core_generic'
|
||||||
character.upgrade_by_core(user, core)
|
character.upgrade_by_core(user, core)
|
||||||
return success_return({'user_id': user.user_id, 'character': [character.to_dict], 'cores': user.cores})
|
return success_return({'user_id': user.user_id, 'character': [character.to_dict()], 'cores': user.cores})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/save', methods=['GET']) # 从云端同步
|
@bp.route('/me/save', methods=['GET']) # 从云端同步
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def cloud_get(user_id):
|
def cloud_get(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = User()
|
user = User()
|
||||||
user.user_id = user_id
|
user.user_id = user_id
|
||||||
save = SaveData(c)
|
save = SaveData(c)
|
||||||
save.select_all(user)
|
save.select_all(user)
|
||||||
return success_return(save.to_dict())
|
return success_return(save.to_dict())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/save', methods=['POST']) # 向云端同步
|
@bp.route('/me/save', methods=['POST']) # 向云端同步
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def cloud_post(user_id):
|
def cloud_post(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = User()
|
user = User()
|
||||||
user.user_id = user_id
|
user.user_id = user_id
|
||||||
save = SaveData(c)
|
save = SaveData(c)
|
||||||
@@ -159,20 +138,17 @@ def cloud_post(user_id):
|
|||||||
save.set_value(
|
save.set_value(
|
||||||
'story_data', request.form['story_data'], request.form['story_checksum'])
|
'story_data', request.form['story_data'], request.form['story_checksum'])
|
||||||
save.set_value(
|
save.set_value(
|
||||||
'finalestate_data', request.form['finalestate_data'], request.form['finalestate_checksum'])
|
'finalestate_data', request.form.get('finalestate_data'), request.form.get('finalestate_checksum'))
|
||||||
|
|
||||||
save.update_all(user)
|
save.update_all(user)
|
||||||
return success_return({'user_id': user.user_id})
|
return success_return({'user_id': user.user_id})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/setting/<set_arg>', methods=['POST']) # 三个设置
|
@bp.route('/me/setting/<set_arg>', methods=['POST']) # 三个设置
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def sys_set(user_id, set_arg):
|
def sys_set(user_id, set_arg):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
value = request.form['value']
|
value = request.form['value']
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
if 'favorite_character' == set_arg:
|
if 'favorite_character' == set_arg:
|
||||||
@@ -182,12 +158,10 @@ def sys_set(user_id, set_arg):
|
|||||||
if 'is_hide_rating' == set_arg or 'max_stamina_notification_enabled' == set_arg:
|
if 'is_hide_rating' == set_arg or 'max_stamina_notification_enabled' == set_arg:
|
||||||
user.update_user_one_column(set_arg, value)
|
user.update_user_one_column(set_arg, value)
|
||||||
return success_return(user.to_dict())
|
return success_return(user.to_dict())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/me/request_delete', methods=['POST']) # 删除账号
|
@bp.route('/me/request_delete', methods=['POST']) # 删除账号
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def user_delete(user_id):
|
def user_delete(user_id):
|
||||||
return error_return(ArcError('Cannot delete the account.', 151)), 404
|
raise ArcError('Cannot delete the account.', 151, status=404)
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
from flask import Blueprint, request
|
|
||||||
from core.error import ArcError
|
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from core.user import UserOnline
|
from core.user import UserOnline
|
||||||
from core.world import UserMap, get_world_all
|
from core.world import UserMap, get_world_all
|
||||||
from .func import error_return, success_return
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from .auth import auth_required
|
from .auth import auth_required
|
||||||
|
from .func import arc_try, success_return
|
||||||
|
|
||||||
bp = Blueprint('world', __name__, url_prefix='/world')
|
bp = Blueprint('world', __name__, url_prefix='/world')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/map/me', methods=['GET']) # 获得世界模式信息,所有地图
|
@bp.route('/map/me', methods=['GET']) # 获得世界模式信息,所有地图
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def world_all(user_id):
|
def world_all(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
user = UserOnline(c, user_id)
|
user = UserOnline(c, user_id)
|
||||||
user.select_user_about_current_map()
|
user.select_user_about_current_map()
|
||||||
return success_return({
|
return success_return({
|
||||||
@@ -21,29 +21,23 @@ def world_all(user_id):
|
|||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
"maps": [x.to_dict(has_map_info=True, has_rewards=True) for x in get_world_all(c, user)]
|
"maps": [x.to_dict(has_map_info=True, has_rewards=True) for x in get_world_all(c, user)]
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/map/me', methods=['POST']) # 进入地图
|
@bp.route('/map/me', methods=['POST']) # 进入地图
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def world_in(user_id):
|
def world_in(user_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
arcmap = UserMap(c, request.form['map_id'], UserOnline(c, user_id))
|
arcmap = UserMap(c, request.form['map_id'], UserOnline(c, user_id))
|
||||||
if arcmap.unlock():
|
if arcmap.unlock():
|
||||||
return success_return(arcmap.to_dict())
|
return success_return(arcmap.to_dict())
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/map/me/<map_id>', methods=['GET']) # 获得单个地图完整信息
|
@bp.route('/map/me/<map_id>', methods=['GET']) # 获得单个地图完整信息
|
||||||
@auth_required(request)
|
@auth_required(request)
|
||||||
|
@arc_try
|
||||||
def world_one(user_id, map_id):
|
def world_one(user_id, map_id):
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
try:
|
|
||||||
arcmap = UserMap(c, map_id, UserOnline(c, user_id))
|
arcmap = UserMap(c, map_id, UserOnline(c, user_id))
|
||||||
arcmap.change_user_current_map()
|
arcmap.change_user_current_map()
|
||||||
return success_return({
|
return success_return({
|
||||||
@@ -51,6 +45,3 @@ def world_one(user_id, map_id):
|
|||||||
"current_map": map_id,
|
"current_map": map_id,
|
||||||
"maps": [arcmap.to_dict(has_map_info=True, has_steps=True)]
|
"maps": [arcmap.to_dict(has_map_info=True, has_steps=True)]
|
||||||
})
|
})
|
||||||
except ArcError as e:
|
|
||||||
return error_return(e)
|
|
||||||
return error_return()
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Config():
|
|||||||
游戏API地址前缀
|
游戏API地址前缀
|
||||||
Game API's URL prefix
|
Game API's URL prefix
|
||||||
'''
|
'''
|
||||||
GAME_API_PREFIX = '/divide/20'
|
GAME_API_PREFIX = '/join/21'
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
@@ -30,29 +30,30 @@ class Config():
|
|||||||
Allowed game versions
|
Allowed game versions
|
||||||
If it is blank, all are allowed.
|
If it is blank, all are allowed.
|
||||||
'''
|
'''
|
||||||
ALLOW_APPVERSION = ['3.12.6', '3.12.6c', '4.0.0', '4.0.0c']
|
ALLOW_APPVERSION = ['3.12.6', '3.12.6c',
|
||||||
|
'4.0.256', '4.0.256c', '4.1.0', '4.1.0c']
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
|
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
联机功能的端口号,若为空,则默认不开启联机功能
|
联机功能相关设置,请确保与Link Play服务器端的设置一致
|
||||||
Port of your link play server
|
Setting of your link play server
|
||||||
If it is blank, link play will be unavailable.
|
Please ensure that the settings on the side of Link Play server are consistent.
|
||||||
'''
|
'''
|
||||||
UDP_PORT = '10900'
|
# SET_LINKPLAY_SERVER_AS_SUB_PROCESS: 是否同时在本地启动Link Play服务器
|
||||||
'''
|
# SET_LINKPLAY_SERVER_AS_SUB_PROCESS: If it is `True`, the link play server will run with the main server locally at the same time.
|
||||||
--------------------
|
SET_LINKPLAY_SERVER_AS_SUB_PROCESS = True
|
||||||
'''
|
# LINKPLAY_HOST: 对主服务器来说的Link Play服务器的地址
|
||||||
|
# LINKPLAY_HOST: The address of the linkplay server based on the main server. If it is blank, the link play feature will be disabled.
|
||||||
'''
|
LINKPLAY_HOST = '0.0.0.0'
|
||||||
--------------------
|
LINKPLAY_UDP_PORT = 10900
|
||||||
联机功能地址,留空则自动获取
|
LINKPLAY_TCP_PORT = 10901
|
||||||
Link Play address
|
LINKPLAY_AUTHENTICATION = 'my_link_play_server'
|
||||||
If left blank, it will be obtained automatically.
|
# LINKPLAY_DISPLAY_HOST: 对客户端来说的Link Play服务器地址,如果为空,则自动获取
|
||||||
'''
|
# LINKPLAY_DISPLAY_HOST: The address of the linkplay server based on the client. If it is blank, the host of link play server for the client will be obtained automatically.
|
||||||
LINK_PLAY_HOST = '' # ***.com
|
LINKPLAY_DISPLAY_HOST = ''
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
@@ -75,7 +76,7 @@ class Config():
|
|||||||
愚人节模式开关
|
愚人节模式开关
|
||||||
Switch of April Fool's Day
|
Switch of April Fool's Day
|
||||||
'''
|
'''
|
||||||
IS_APRILFOOLS = False
|
IS_APRILFOOLS = True
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
@@ -186,7 +187,8 @@ class Config():
|
|||||||
是否记录详细的服务器日志
|
是否记录详细的服务器日志
|
||||||
If recording detailed server logs is enabled
|
If recording detailed server logs is enabled
|
||||||
'''
|
'''
|
||||||
ALLOW_LOG_INFO = False
|
ALLOW_INFO_LOG = False
|
||||||
|
ALLOW_WARNING_LOG = False
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
class Config:
|
|
||||||
TIME_LIMIT = 3600000
|
|
||||||
|
|
||||||
COMMAND_INTERVAL = 1000000
|
|
||||||
|
|
||||||
COUNTDOWM_TIME = 3999
|
|
||||||
|
|
||||||
PLAYER_PRE_TIMEOUT = 3000000
|
|
||||||
PLAYER_TIMEOUT = 20000000
|
|
||||||
|
|
||||||
LINK_PLAY_UNLOCK_LENGTH = 512
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -2,7 +2,7 @@ import os
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import server.arcscore
|
import server.arcscore
|
||||||
from core.download import initialize_songfile
|
from core.download import initialize_songfile, get_only_3_song_ids
|
||||||
from core.rank import RankList
|
from core.rank import RankList
|
||||||
from core.sql import Connect
|
from core.sql import Connect
|
||||||
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
||||||
@@ -288,6 +288,8 @@ def update_database():
|
|||||||
def update_song_hash():
|
def update_song_hash():
|
||||||
# 更新数据库内谱面文件hash值
|
# 更新数据库内谱面文件hash值
|
||||||
try:
|
try:
|
||||||
|
get_only_3_song_ids.cache_clear()
|
||||||
|
get_only_3_song_ids()
|
||||||
initialize_songfile()
|
initialize_songfile()
|
||||||
flash('数据刷新成功 Success refresh data.')
|
flash('数据刷新成功 Success refresh data.')
|
||||||
except:
|
except:
|
||||||
@@ -423,7 +425,7 @@ def all_character():
|
|||||||
def change_character():
|
def change_character():
|
||||||
# 修改角色数据
|
# 修改角色数据
|
||||||
skill_ids = ['No_skill', 'gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
skill_ids = ['No_skill', 'gauge_easy', 'note_mirror', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
||||||
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap', 'skill_vita', 'skill_fatalis']
|
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', 'frags_kou', 'visual_ink', 'shirabe_entry_fee', 'frags_yume', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', 'eto_uncap', 'luna_uncap', 'frags_preferred_song', 'visual_ghost_skynotes', 'ayu_uncap', 'skill_vita', 'skill_fatalis', 'skill_reunion', 'frags_ongeki_slash', 'frags_ongeki_hard']
|
||||||
return render_template('web/changechar.html', skill_ids=skill_ids)
|
return render_template('web/changechar.html', skill_ids=skill_ids)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,17 @@ def update_user_char(c):
|
|||||||
(j[0], i[0], i[1], exp, i[2], 0))
|
(j[0], i[0], i[1], exp, i[2], 0))
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_epilogue(c):
|
||||||
|
c.execute('''select user_id from user''')
|
||||||
|
x = c.fetchall()
|
||||||
|
for i in x:
|
||||||
|
c.execute(
|
||||||
|
'''select exists(select * from user_item where user_id=? and item_id=? and type='pack')''', (i[0], 'epilogue'))
|
||||||
|
if c.fetchone() == (0,):
|
||||||
|
c.execute('''insert into user_item values(?,?,'pack',1)''',
|
||||||
|
(i[0], 'epilogue'))
|
||||||
|
|
||||||
|
|
||||||
def update_database():
|
def update_database():
|
||||||
# 将old数据库不存在数据加入到新数据库上,并删除old数据库
|
# 将old数据库不存在数据加入到新数据库上,并删除old数据库
|
||||||
# 对于arcaea_datebase.db,更新一些表,并用character数据更新user_char_full
|
# 对于arcaea_datebase.db,更新一些表,并用character数据更新user_char_full
|
||||||
@@ -216,6 +227,7 @@ def update_database():
|
|||||||
update_one_table(c1, c2, 'character')
|
update_one_table(c1, c2, 'character')
|
||||||
|
|
||||||
update_user_char(c2) # 更新user_char_full
|
update_user_char(c2) # 更新user_char_full
|
||||||
|
update_user_epilogue(c2) # 更新user的epilogue
|
||||||
|
|
||||||
os.remove('database/old_arcaea_database.db')
|
os.remove('database/old_arcaea_database.db')
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
SONG_DATABASE_PATH = './arcsong.db'
|
||||||
|
SERVER_DATABASE_PATH = './arcaea_database.db'
|
||||||
|
|
||||||
|
|
||||||
class Connect():
|
class Connect():
|
||||||
# 数据库连接类,上下文管理
|
# 数据库连接类,上下文管理
|
||||||
|
|
||||||
def __init__(self, file_path='./arcaea_database.db'):
|
def __init__(self, file_path=SERVER_DATABASE_PATH):
|
||||||
"""
|
"""
|
||||||
数据库连接,默认连接arcaea_database.db\
|
数据库连接\
|
||||||
接受:文件路径\
|
接受:文件路径\
|
||||||
返回:sqlite3连接操作对象
|
返回:sqlite3连接操作对象
|
||||||
"""
|
"""
|
||||||
@@ -28,33 +34,81 @@ class Connect():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def insert(cursor, song_id, name, a, b, c, d):
|
def insert(cursor, song_id, name, a, b, c, d, update_type=0):
|
||||||
'''Insert a new song into database.'''
|
'''Insert a new song into database.'''
|
||||||
|
if update_type == 0 or update_type == 1:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
'''select exists(select * from chart where song_id=?)''', (song_id, ))
|
'''select exists(select * from chart where song_id=?)''', (song_id, ))
|
||||||
if cursor.fetchone()[0]:
|
if cursor.fetchone()[0]:
|
||||||
return None
|
if update_type == 0:
|
||||||
|
# 重复则不更新,以服务端数据库数据为准
|
||||||
|
return
|
||||||
|
elif update_type == 1:
|
||||||
|
# 重复则更新,以`arcsong.db`数据为准
|
||||||
|
cursor.execute('''update chart set name=?, rating_pst=?, rating_prs=?, rating_ftr=?, rating_byn=? where song_id=?''',
|
||||||
|
(name, a, b, c, d, song_id))
|
||||||
|
return
|
||||||
|
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
'''insert into chart values (?,?,?,?,?,?)''', (song_id, name, a, b, c, d))
|
'''insert into chart values (?,?,?,?,?,?)''', (song_id, name, a, b, c, d))
|
||||||
|
|
||||||
|
|
||||||
def old_to_new():
|
def from_song_datebase():
|
||||||
'''Update old database to new database.'''
|
'''Get song data from song database and insert them into server's database.'''
|
||||||
with Connect('./arcsong.db') as c:
|
with Connect(SONG_DATABASE_PATH) as c:
|
||||||
|
c.execute('''select name from sqlite_master where type="table"''')
|
||||||
|
tables = [x[0] for x in c.fetchall()]
|
||||||
|
if 'songs' in tables:
|
||||||
c.execute(
|
c.execute(
|
||||||
'''select sid, name_en, rating_pst, rating_prs, rating_ftr, rating_byn from songs''')
|
'''select sid, name_en, rating_pst, rating_prs, rating_ftr, rating_byn from songs''')
|
||||||
data = c.fetchall()
|
data = c.fetchall()
|
||||||
|
elif 'charts' in tables:
|
||||||
|
c.execute(
|
||||||
|
'''select song_id, rating_class, name_en, rating from charts''')
|
||||||
|
songs = {}
|
||||||
|
for song_id, rating_class, name_en, rating in c.fetchall():
|
||||||
|
if song_id not in songs:
|
||||||
|
songs[song_id] = [-1, -1, -1, -1, name_en]
|
||||||
|
songs[song_id][rating_class] = rating
|
||||||
|
|
||||||
|
data = [(x, y[-1], y[0], y[1], y[2], y[3])
|
||||||
|
for x, y in songs.items()]
|
||||||
|
else:
|
||||||
|
print('Error: Cannot find table `songs` or `charts` in the database.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# 用户确认更新方式
|
||||||
|
update_type = 0
|
||||||
|
x = input('Type a number to decide the update type:\n0: Do not update if the song already exists in the server database.\n1: Update even if the song already exists in the server database.\n2: Clear chart data in the server database and then update.\nYour choice: ')
|
||||||
|
x = x.strip()
|
||||||
|
if x not in ('0', '1', '2'):
|
||||||
|
print('Error: Invalid input.')
|
||||||
|
return
|
||||||
|
update_type = int(x)
|
||||||
|
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
|
if update_type == 2:
|
||||||
|
# 清空数据表后更新
|
||||||
|
c.execute('''delete from chart''')
|
||||||
for x in data:
|
for x in data:
|
||||||
insert(c, x[0], x[1], x[2], x[3], x[4], x[5])
|
insert(c, x[0], x[1], x[2], x[3], x[4], x[5], update_type)
|
||||||
|
|
||||||
|
print('Seems to be done.')
|
||||||
|
|
||||||
|
|
||||||
|
def check_file():
|
||||||
|
if not os.path.isfile(SONG_DATABASE_PATH) or not os.path.isfile(SERVER_DATABASE_PATH):
|
||||||
|
print('Error: Files cannot be found.')
|
||||||
|
print('Note: Please make sure that both `arcsong.db` and `arcaea_server.db` are in this directory.')
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
old_to_new()
|
if check_file():
|
||||||
|
from_song_datebase()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
print('Done.')
|
input('Press `Enter` key to exit.')
|
||||||
input()
|
|
||||||
exit()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user