diff --git a/README.md b/README.md index e888609..4af9034 100644 --- a/README.md +++ b/README.md @@ -19,15 +19,16 @@ This is a small local Arcaea server based on Python and Flask, which can simulat - 自定义世界模式 Customizable World Mode - 自定义歌曲下载 Customizable songs download - 全角色立绘 All character drawings +- 角色技能 Character skills - 全剧情解锁 Unlock all the storys - 后台查分 Background search scores - 后台自定义歌曲定数 Customize chart consts in the background +- 成绩校验 Score check 没有以下 We don't have: -- 角色特性 Character characteristics +- 角色数值 Character characteristic value - 购买 Purchase - 歌曲解锁、曲包解锁 Songs unlocking and music packs unlocking -- 反作弊系统 Anti cheating system - 服务器安全性保证 Server security assurance 可能有问题 There may be problems: @@ -37,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. -不太会apk反编译,想破解songlists hash验证,有人教我吗( -I don't know APK decompilation very well. I want to crack the songlists hash verification. Help. ## 下载 Download [这里 Here](https://github.com/Lost-MSth/Arcaea-server/releases) +[Arcaea](https://konmai.cn/#arcaea) + ## 更新日志 Update log 只保留最新版本 Only keep the latest version. -### Version 1.4 -- 适用于Arcaea 3.2.2版本 For Arcaea 3.2.2 -- 更新了歌曲数据库 Update the song database. -- 新增自定义歌曲下载功能 Add the customizable song download function. -- 角色**咲弥**可以看到立绘了 The drawing of character **Etude** can be seen. -> 提醒:反编译只对c版本有效,对谷歌版本无效 +> 提醒:更新时请注意保留原先的数据库,以防数据丢失 +> +> Tips: When updating, please keep the original database in case of data loss. -> Tips: Decompilation is only valid for c version, not for Google version. +### Version 1.5 +- 仍然适用于Arcaea 3.2.2版本 Still for Arcaea 3.2.2 +- 新增了角色技能 Add character skills. +- 新增了分数校验系统 Add score check system. ## 运行环境与依赖 Running environment and requirements - Windows操作系统 Windows operating system diff --git a/latest version/database/arcaea_database.db b/latest version/database/arcaea_database.db index a1852a5..d39e931 100644 Binary files a/latest version/database/arcaea_database.db and b/latest version/database/arcaea_database.db differ diff --git a/latest version/database/database_initialize.py b/latest version/database/database_initialize.py index dce53ee..9eeebf5 100644 --- a/latest version/database/database_initialize.py +++ b/latest version/database/database_initialize.py @@ -178,13 +178,24 @@ ,'Chuni Penguin','Haruna','Nono','MTA-XXX','MDA-21','Kanae','Hikari(Fantasia)','Tairitsu(Sonata)','Sia','DORO*C' ,'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): 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) else: 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) diff --git a/latest version/database/songs/dement/3.aff b/latest version/database/songs/dement/3.aff index c5e53a6..3c3a669 100644 --- a/latest version/database/songs/dement/3.aff +++ b/latest version/database/songs/dement/3.aff @@ -2,6 +2,7 @@ AudioOffset:408 - timing(0,210.00,4.00); (1142,4); +flick(1142,0.50,0.50,0.00,1.00); (1571,2); (2857,1); (3142,2); diff --git a/latest version/main.py b/latest version/main.py index e3c2acf..1cff63c 100644 --- a/latest version/main.py +++ b/latest version/main.py @@ -16,6 +16,8 @@ def error_return(error_code): # 错误返回 + # 2 Arcaea服务器正在维护 + # 5 请更新Arcaea到最新版本 # 100 无法在此ip地址下登录游戏 # 101 用户名占用 # 102 电子邮箱已注册 @@ -307,6 +309,8 @@ def song_score_post(): headers = request.headers token = headers['Authorization'] token = token[7:] + song_token = request.form['song_token'] + song_hash = request.form['song_hash'] song_id = request.form['song_id'] difficulty = int(request.form['difficulty']) score = int(request.form['score']) @@ -318,10 +322,15 @@ def song_score_post(): modifier = int(request.form['modifier']) beyond_gauge = int(request.form['beyond_gauge']) clear_type = int(request.form['clear_type']) + submission_hash = request.form['submission_hash'] try: user_id = server.auth.token_get_id(token) 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, perfect_count, near_count, miss_count, health, modifier, beyond_gauge, clear_type) if r: diff --git a/latest version/server/arcscore.py b/latest version/server/arcscore.py index c053697..8cf87fa 100644 --- a/latest version/server/arcscore.py +++ b/latest version/server/arcscore.py @@ -2,6 +2,7 @@ import time import json import server.arcworld +import hashlib def b2int(x): @@ -20,6 +21,16 @@ def int2b(x): 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): # 根据user_id、song_id、难度得到该曲目最好成绩,返回字典 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 +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): # 向云端同步,无返回 # 注意,best_score表不比较,直接覆盖 diff --git a/latest version/server/auth.py b/latest version/server/auth.py index 5a8b6d3..12b88cd 100644 --- a/latest version/server/auth.py +++ b/latest version/server/auth.py @@ -62,18 +62,6 @@ def build_user_id(c): else: 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): # 为用户添加所有可用角色 c.execute('''select * from character''')