mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-09 17:27:27 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56ae7047fc | ||
|
|
f5d2589971 | ||
|
|
8bb5c28663 |
26
README.md
26
README.md
@@ -8,7 +8,7 @@ This is a small local Arcaea server based on Python and Flask, which can simulat
|
|||||||
> It looks stupid, but it works!
|
> It looks stupid, but it works!
|
||||||
|
|
||||||
## 特性 Features
|
## 特性 Features
|
||||||
有 We have:
|
有以下 We have:
|
||||||
- 登录、注册 Login and registration
|
- 登录、注册 Login and registration
|
||||||
- 成绩上传 Score upload
|
- 成绩上传 Score upload
|
||||||
- PTT
|
- PTT
|
||||||
@@ -17,16 +17,18 @@ This is a small local Arcaea server based on Python and Flask, which can simulat
|
|||||||
- 数据同步 Data synchronization
|
- 数据同步 Data synchronization
|
||||||
- 爬梯 Climbing steps
|
- 爬梯 Climbing steps
|
||||||
- 自定义世界模式 Customizable World Mode
|
- 自定义世界模式 Customizable World Mode
|
||||||
|
- 自定义歌曲下载 Customizable songs download
|
||||||
- 全角色立绘 All character drawings
|
- 全角色立绘 All character drawings
|
||||||
|
- 角色技能 Character skills
|
||||||
- 全剧情解锁 Unlock all the storys
|
- 全剧情解锁 Unlock all the storys
|
||||||
- 后台查分 Background search scores
|
- 后台查分 Background search scores
|
||||||
- 后台自定义歌曲定数 Customize chart consts in the background
|
- 后台自定义歌曲定数 Customize chart consts in the background
|
||||||
|
- 成绩校验 Score check
|
||||||
|
|
||||||
没有以下 We don't have:
|
没有以下 We don't have:
|
||||||
- 角色特性 Character characteristics
|
- 角色数值 Character characteristic value
|
||||||
- 购买 Purchase
|
- 购买 Purchase
|
||||||
- 歌曲解锁、曲包解锁 Songs unlocking and music packs unlocking
|
- 歌曲解锁、曲包解锁 Songs unlocking and music packs unlocking
|
||||||
- 反作弊系统 Anti cheating system
|
|
||||||
- 服务器安全性保证 Server security assurance
|
- 服务器安全性保证 Server security assurance
|
||||||
|
|
||||||
可能有问题 There may be problems:
|
可能有问题 There may be problems:
|
||||||
@@ -36,23 +38,23 @@ This is a small local Arcaea server based on Python and Flask, which can simulat
|
|||||||
只是很有趣,用处探索中。
|
只是很有趣,用处探索中。
|
||||||
It is just so interesting. What it can do is under exploration.
|
It is just so interesting. What it can do is under exploration.
|
||||||
|
|
||||||
进行了一下项目改进,可能目录有所变化
|
|
||||||
Some project improvements have been made and the catalog may have changed.
|
|
||||||
|
|
||||||
## 下载 Download
|
## 下载 Download
|
||||||
[这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases)
|
[这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases)
|
||||||
|
|
||||||
|
[Arcaea](https://konmai.cn/#arcaea)
|
||||||
|
|
||||||
## 更新日志 Update log
|
## 更新日志 Update log
|
||||||
只保留最新版本 Only keep the latest version.
|
只保留最新版本 Only keep the latest version.
|
||||||
### Version 1.3
|
|
||||||
- 适用于Arcaea 3.2.1版本 For Arcaea 3.2.1
|
|
||||||
- 更新了歌曲数据库 Update the song database.
|
|
||||||
- 新增了可自定义的世界模式 Add customizable World Mode.
|
|
||||||
- 对于官方版本,解锁几乎所有歌曲 For the official version, unlock almost all songs.
|
|
||||||
|
|
||||||
> 更新说明:本次更新无法从旧版本同步数据,请使用游戏内数据同步功能上传best_score数据
|
> 提醒:更新时请注意保留原先的数据库,以防数据丢失
|
||||||
|
>
|
||||||
|
> Tips: When updating, please keep the original database in case of data loss.
|
||||||
|
|
||||||
> Update note: In this update you cannot synchronize data from an older version. Please use the data synchronization function in game to upload best_score data.
|
### Version 1.5
|
||||||
|
- 仍然适用于Arcaea 3.2.2版本 Still for Arcaea 3.2.2
|
||||||
|
- 新增了角色技能 Add character skills.
|
||||||
|
- 新增了分数校验系统 Add score check system.
|
||||||
|
|
||||||
## 运行环境与依赖 Running environment and requirements
|
## 运行环境与依赖 Running environment and requirements
|
||||||
- Windows操作系统 Windows operating system
|
- Windows操作系统 Windows operating system
|
||||||
|
|||||||
Binary file not shown.
@@ -178,13 +178,24 @@ char = ['Hikari','Tairitsu','Kou','Sapphire','Lethe','','Tairitsu(Axium)'
|
|||||||
,'Chuni Penguin','Haruna','Nono','MTA-XXX','MDA-21','Kanae','Hikari(Fantasia)','Tairitsu(Sonata)','Sia','DORO*C'
|
,'Chuni Penguin','Haruna','Nono','MTA-XXX','MDA-21','Kanae','Hikari(Fantasia)','Tairitsu(Sonata)','Sia','DORO*C'
|
||||||
,'Tairitsu(Tempest)','Brillante','Ilith(Summer)','Etude']
|
,'Tairitsu(Tempest)','Brillante','Ilith(Summer)','Etude']
|
||||||
|
|
||||||
|
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','']
|
||||||
|
|
||||||
|
skill_id_uncap = ['','','frags_kou','','visual_ink','','','','','','','','','shirabe_entry_fee','','','','','','','','frags_yume','','','','','','','','','','','','','','','','','']
|
||||||
|
|
||||||
for i in range(0, 39):
|
for i in range(0, 39):
|
||||||
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
|
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
|
||||||
sql = 'insert into character values('+str(i)+',"'+char[i]+'''",30,25000,25000,90,90,90,'',0,0,'',0,'',1,1)'''
|
sql = 'insert into character values('+str(i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',1,1)'''
|
||||||
c.execute(sql)
|
c.execute(sql)
|
||||||
else:
|
else:
|
||||||
if i != 5:
|
if i != 5:
|
||||||
sql = 'insert into character values('+str(i)+',"'+char[i]+'''",30,25000,25000,90,90,90,'',0,0,'',0,'',0,0)'''
|
sql = 'insert into character values('+str(i)+',"'+char[i]+'''",30,25000,25000,90,90,90,"'''+skill_id[i]+'''",0,0,"'''+skill_id_uncap[i]+'''",0,'',0,0)'''
|
||||||
c.execute(sql)
|
c.execute(sql)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ AudioOffset:408
|
|||||||
-
|
-
|
||||||
timing(0,210.00,4.00);
|
timing(0,210.00,4.00);
|
||||||
(1142,4);
|
(1142,4);
|
||||||
|
flick(1142,0.50,0.50,0.00,1.00);
|
||||||
(1571,2);
|
(1571,2);
|
||||||
(2857,1);
|
(2857,1);
|
||||||
(3142,2);
|
(3142,2);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ wsgi_app = app.wsgi_app
|
|||||||
|
|
||||||
|
|
||||||
def error_return(error_code): # 错误返回
|
def error_return(error_code): # 错误返回
|
||||||
|
# 2 Arcaea服务器正在维护
|
||||||
|
# 5 请更新Arcaea到最新版本
|
||||||
# 100 无法在此ip地址下登录游戏
|
# 100 无法在此ip地址下登录游戏
|
||||||
# 101 用户名占用
|
# 101 用户名占用
|
||||||
# 102 电子邮箱已注册
|
# 102 电子邮箱已注册
|
||||||
@@ -307,6 +309,8 @@ def song_score_post():
|
|||||||
headers = request.headers
|
headers = request.headers
|
||||||
token = headers['Authorization']
|
token = headers['Authorization']
|
||||||
token = token[7:]
|
token = token[7:]
|
||||||
|
song_token = request.form['song_token']
|
||||||
|
song_hash = request.form['song_hash']
|
||||||
song_id = request.form['song_id']
|
song_id = request.form['song_id']
|
||||||
difficulty = int(request.form['difficulty'])
|
difficulty = int(request.form['difficulty'])
|
||||||
score = int(request.form['score'])
|
score = int(request.form['score'])
|
||||||
@@ -318,10 +322,15 @@ def song_score_post():
|
|||||||
modifier = int(request.form['modifier'])
|
modifier = int(request.form['modifier'])
|
||||||
beyond_gauge = int(request.form['beyond_gauge'])
|
beyond_gauge = int(request.form['beyond_gauge'])
|
||||||
clear_type = int(request.form['clear_type'])
|
clear_type = int(request.form['clear_type'])
|
||||||
|
submission_hash = request.form['submission_hash']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = server.auth.token_get_id(token)
|
user_id = server.auth.token_get_id(token)
|
||||||
if user_id is not None:
|
if user_id is not None:
|
||||||
|
# 增加成绩校验
|
||||||
|
if not server.arcscore.arc_score_check(user_id, song_id, difficulty, score, shiny_perfect_count, perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type, song_token, song_hash, submission_hash):
|
||||||
|
return error_return(107)
|
||||||
|
|
||||||
r, re = server.arcscore.arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count,
|
r, re = server.arcscore.arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count,
|
||||||
perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type)
|
perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type)
|
||||||
if r:
|
if r:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import sqlite3
|
|||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
import server.arcworld
|
import server.arcworld
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
def b2int(x):
|
def b2int(x):
|
||||||
@@ -20,6 +21,16 @@ def int2b(x):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def md5(code):
|
||||||
|
# md5加密算法
|
||||||
|
code = code.encode()
|
||||||
|
md5s = hashlib.md5()
|
||||||
|
md5s.update(code)
|
||||||
|
codes = md5s.hexdigest()
|
||||||
|
|
||||||
|
return codes
|
||||||
|
|
||||||
|
|
||||||
def get_score(c, user_id, song_id, difficulty):
|
def get_score(c, user_id, song_id, difficulty):
|
||||||
# 根据user_id、song_id、难度得到该曲目最好成绩,返回字典
|
# 根据user_id、song_id、难度得到该曲目最好成绩,返回字典
|
||||||
c.execute('''select * from best_score where user_id = :a and song_id = :b and difficulty = :c''',
|
c.execute('''select * from best_score where user_id = :a and song_id = :b and difficulty = :c''',
|
||||||
@@ -480,6 +491,29 @@ def arc_score_post(user_id, song_id, difficulty, score, shiny_perfect_count, per
|
|||||||
return ptt, re
|
return ptt, re
|
||||||
|
|
||||||
|
|
||||||
|
def arc_score_check(user_id, song_id, difficulty, score, shiny_perfect_count, perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type, song_token, song_hash, submission_hash):
|
||||||
|
# 分数校验,返回布尔值
|
||||||
|
if shiny_perfect_count < 0 or perfect_count < 0 or near_count < 0 or miss_count < 0 or score < 0:
|
||||||
|
return False
|
||||||
|
if difficulty not in [0, 1, 2, 3]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
all_note = perfect_count + near_count + miss_count
|
||||||
|
ascore = 10000000 / all_note * \
|
||||||
|
(perfect_count + near_count/2) + shiny_perfect_count
|
||||||
|
if abs(ascore - score) >= 5:
|
||||||
|
return False
|
||||||
|
|
||||||
|
x = song_token + song_hash + song_id + str(difficulty) + str(score) + str(shiny_perfect_count) + str(
|
||||||
|
perfect_count) + str(near_count) + str(miss_count) + str(health) + str(modifier) + str(clear_type)
|
||||||
|
y = str(user_id) + song_hash
|
||||||
|
checksum = md5(x+md5(y))
|
||||||
|
if checksum != submission_hash:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def arc_all_post(user_id, scores_data, clearlamps_data):
|
def arc_all_post(user_id, scores_data, clearlamps_data):
|
||||||
# 向云端同步,无返回
|
# 向云端同步,无返回
|
||||||
# 注意,best_score表不比较,直接覆盖
|
# 注意,best_score表不比较,直接覆盖
|
||||||
|
|||||||
@@ -62,18 +62,6 @@ def arc_register(name: str, password: str): # 注册
|
|||||||
else:
|
else:
|
||||||
return 2000001
|
return 2000001
|
||||||
|
|
||||||
# def insert_user_char(c, user_id):
|
|
||||||
# # 为用户添加所有可用角色
|
|
||||||
# for i in range(0, 38):
|
|
||||||
# if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21]:
|
|
||||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
|
||||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,1,1)'''
|
|
||||||
# c.execute(sql)
|
|
||||||
# else:
|
|
||||||
# if i != 5:
|
|
||||||
# sql = 'insert into user_char values('+str(user_id)+','+str(
|
|
||||||
# i)+''',30,25000,25000,90,90,90,'',0,0,'',0,0,0)'''
|
|
||||||
# c.execute(sql)
|
|
||||||
def insert_user_char(c, user_id):
|
def insert_user_char(c, user_id):
|
||||||
# 为用户添加所有可用角色
|
# 为用户添加所有可用角色
|
||||||
c.execute('''select * from character''')
|
c.execute('''select * from character''')
|
||||||
|
|||||||
Reference in New Issue
Block a user