mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-09 09:17:26 +08:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5de274fdf5 | ||
|
|
b60457c38b | ||
|
|
35b954e549 | ||
|
|
ceebba4664 | ||
|
|
5bc9b9a3dc | ||
|
|
d9a543bc5a | ||
|
|
4666c9c153 | ||
|
|
32bcbb0ccd | ||
|
|
34497d0638 | ||
|
|
23bf3c020f | ||
|
|
e1fc1bbcd1 | ||
|
|
355ec3557f | ||
|
|
cc4ac192e7 | ||
|
|
5068c7215c | ||
|
|
99f05cabb7 | ||
|
|
1a097bc4d0 | ||
|
|
849f4f7260 | ||
|
|
add81ee639 | ||
|
|
b927ad23f8 | ||
|
|
8bc74b36c7 | ||
|
|
7dc8bfea9a |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1 +1,5 @@
|
|||||||
*.log
|
*.log
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
33
README.md
33
README.md
@@ -21,6 +21,7 @@ This procedure is mainly used for study and research, and shall not be used for
|
|||||||
- PTT
|
- PTT
|
||||||
- 世界排名 Global rank
|
- 世界排名 Global rank
|
||||||
- 排名 Rank
|
- 排名 Rank
|
||||||
|
- Link Play
|
||||||
- 好友系统 Friends
|
- 好友系统 Friends
|
||||||
- 数据同步 Data synchronization
|
- 数据同步 Data synchronization
|
||||||
- 爬梯 Climbing steps
|
- 爬梯 Climbing steps
|
||||||
@@ -64,30 +65,30 @@ 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.7.0
|
|
||||||
- 适用于Arcaea 3.9.0版本 For Arcaea 3.9.0
|
### Version 2.8.1
|
||||||
|
- 适用于Arcaea 3.12.0版本 For Arcaea 3.12.0
|
||||||
- 更新了歌曲数据库 Update the song database.
|
- 更新了歌曲数据库 Update the song database.
|
||||||
- 新搭档 **爱丽丝 & 坦尼尔(小步舞曲)** 已解锁 Unlock the new character **Alice & Tenniel (Minuet)**.
|
- 新搭档**维塔**已解锁 Unlock the character **Vita**.
|
||||||
- 新搭档 **对立(挽歌)** 已解锁 Unlock the new character **Tairitsu (Elegy)**.
|
- 新增对**维塔**的技能支持 Add support for the skill of **Vita**.
|
||||||
- 搭档 **爱托** 已觉醒 Uncap the character **Eto**.
|
- 修正世界模式进度计算方式 Revise the algorithm of world mode progress.
|
||||||
- 搭档 **露娜** 已觉醒 Uncap the character **Luna**.
|
- 修复世界模式下 **对立(风暴)** 数值计算错误的问题 Fix the wrong value of **Tairitsu(Tempest)** in the World Mode.
|
||||||
- 新增爱托和露娜的觉醒技能(可能有问题) Add the uncapped skills of Eto and Luna (There may be bugs.).
|
|
||||||
- 新增是否同步角色表的设置选项 Add the setting option of whether updating the character table.
|
|
||||||
|
|
||||||
- 以下是累积更新 The following are cumulative updates:
|
- 以下是累积更新 The following are cumulative updates:
|
||||||
- 添加购买物品含有以太之滴的支持 Add support for purchasing items containing generic cores.
|
- #35 集成式接口优化(By Young-Lord) Optimize `aggregate` interface. (By Young-Lord)
|
||||||
- 修复关于购买物品打折的问题 Fix a bug about purchase discount.
|
- 新增填写Link Play服务器地址的选项,解决地址无法正确自动获取的问题 Add the option of filling in Link Play server address, which can solve the problem that the address cannot be obtained automatically.
|
||||||
- 修复关于数据库同步的问题,这可能导致api_login表为空 Fix a bug about database synchronization, which may make 'api_login' table empty.
|
- 修复高版本iOS客户端无法登陆的问题 Fix a bug that the high version of iOS client cannot log in.
|
||||||
- 新增登出API接口 Update a logout API.
|
- 修复有关下载的安全性问题 Fix a safety problem about downloading.
|
||||||
|
- 修复有不同音频的Beyond谱面无法下载的问题 Fix a bug about unable to download a beyond chart which has another audio file.
|
||||||
|
|
||||||
|
|
||||||
> 提示:某些数据可能尚不完整,某个单曲的解锁和某两个角色的数据可能会有问题
|
|
||||||
> Tips: Some data may not be complete, so there may be problems with the unlocking of a single song and the data of two characters.
|
|
||||||
|
|
||||||
|
|
||||||
## 运行环境与依赖 Running environment and requirements
|
## 运行环境与依赖 Running environment and requirements
|
||||||
- Windows/Linux/Mac OS/Android
|
- Windows/Linux/Mac OS/Android...
|
||||||
- Python 3
|
- Python 3
|
||||||
- Flask module
|
- Flask module, Cryptography module
|
||||||
- Charles, IDA, proxy app... (optional)
|
- Charles, IDA, proxy app... (optional)
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|||||||
0
latest version/api/__init__.py
Normal file
0
latest version/api/__init__.py
Normal file
0
latest version/database/__init__.py
Normal file
0
latest version/database/__init__.py
Normal file
Binary file not shown.
@@ -4,7 +4,7 @@ import json
|
|||||||
|
|
||||||
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
# 数据库初始化文件,删掉arcaea_database.db文件后运行即可,谨慎使用
|
||||||
|
|
||||||
ARCAEA_SERVER_VERSION = 'v2.7.0'
|
ARCAEA_SERVER_VERSION = 'v2.8.1'
|
||||||
|
|
||||||
|
|
||||||
def main(path='./'):
|
def main(path='./'):
|
||||||
@@ -299,46 +299,46 @@ def main(path='./'):
|
|||||||
|
|
||||||
# 搭档初始化
|
# 搭档初始化
|
||||||
char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'stella', 'Hikari & Fisica', 'ilith', 'eto', 'luna', 'shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
|
char = ['hikari', 'tairitsu', 'kou', 'sapphire', 'lethe', '', 'Tairitsu(Axium)', 'Tairitsu(Grievous Lady)', 'stella', 'Hikari & Fisica', 'ilith', 'eto', 'luna', 'shirabe', 'Hikari(Zero)', 'Hikari(Fracture)', 'Hikari(Summer)', 'Tairitsu(Summer)', 'Tairitsu & Trin',
|
||||||
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)']
|
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija', 'vita']
|
||||||
|
|
||||||
skill_id = ['gauge_easy', '', '', '', 'note_mirror', '', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
|
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', '', '']
|
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes', 'skill_vita']
|
||||||
|
|
||||||
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
|
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
|
||||||
'', '', '', '', '', '', '', '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]
|
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0]
|
||||||
|
|
||||||
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
|
||||||
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 50, 25, 58, 50, 61, 55, 55]
|
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 50, 25, 58, 50, 61, 45, 45, 38, 34]
|
||||||
|
|
||||||
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
|
||||||
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 55, 55]
|
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50]
|
||||||
|
|
||||||
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
|
||||||
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 50, 30, 49, 15, 34, 55, 55]
|
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 50, 30, 49, 15, 34, 45, 45, 38, 67]
|
||||||
|
|
||||||
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
|
||||||
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 100, 50, 68, 60, 90, 80, 80]
|
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60, 51]
|
||||||
|
|
||||||
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
|
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
|
||||||
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 100, 55, 65, 59, 90, 80, 80]
|
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90, 75]
|
||||||
|
|
||||||
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
|
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
|
||||||
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 100, 40, 69, 62, 51, 80, 80]
|
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60, 100]
|
||||||
|
|
||||||
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
|
||||||
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 100, 50, 68, 60, 90, 80, 80]
|
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60, 51]
|
||||||
|
|
||||||
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 83, 40, 83,
|
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
|
||||||
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 100, 55, 65, 59, 90, 80, 80]
|
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90, 75]
|
||||||
|
|
||||||
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 49, 64,
|
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
|
||||||
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 100, 40, 69, 62, 51, 80, 80]
|
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60, 100]
|
||||||
|
|
||||||
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
|
||||||
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0]
|
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]
|
||||||
|
|
||||||
char_core = {
|
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}],
|
||||||
@@ -355,13 +355,14 @@ def main(path='./'):
|
|||||||
42: [{'core_id': 'core_chunithm', 'amount': 15}],
|
42: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||||
43: [{'core_id': 'core_chunithm', 'amount': 15}],
|
43: [{'core_id': 'core_chunithm', 'amount': 15}],
|
||||||
11: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_hollow', 'amount': 5}],
|
11: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_hollow', 'amount': 5}],
|
||||||
12: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}]
|
12: [{'core_id': 'core_binary', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
|
||||||
|
19: [{'core_id': 'core_colorful', 'amount': 30}]
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in range(0, 53):
|
for i in range(0, 55):
|
||||||
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]:
|
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43, 11, 12, 19]:
|
||||||
sql = '''insert into character values(?,?,30,?,?,?,?,?,?,?,?,?,?,?,?,?,?,1)'''
|
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]))
|
||||||
@@ -371,23 +372,26 @@ def main(path='./'):
|
|||||||
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]))
|
||||||
|
|
||||||
|
c.execute('''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)''', (99,
|
||||||
|
'shirahime', 38, 33, 28, 66, 58, 50, 66, 58, 50, 'frags_preferred_song', 0, 0, '', 0))
|
||||||
|
|
||||||
for i in char_core:
|
for i in char_core:
|
||||||
for j in char_core[i]:
|
for j in char_core[i]:
|
||||||
c.execute('''insert into char_item values(?,?,'core',?)''',
|
c.execute('''insert into char_item values(?,?,'core',?)''',
|
||||||
(i, j['core_id'], j['amount']))
|
(i, j['core_id'], j['amount']))
|
||||||
cores = ['core_hollow', 'core_desolate', 'core_chunithm', 'core_crimson',
|
cores = ['core_hollow', 'core_desolate', 'core_chunithm', 'core_crimson',
|
||||||
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary']
|
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful']
|
||||||
|
|
||||||
for i in cores:
|
for i in cores:
|
||||||
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"]
|
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril"]
|
||||||
for i in world_songs:
|
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,))
|
||||||
|
|
||||||
world_unlocks = ["scenery_chap1", "scenery_chap2",
|
world_unlocks = ["scenery_chap1", "scenery_chap2",
|
||||||
"scenery_chap3", "scenery_chap4", "scenery_chap5"]
|
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6"]
|
||||||
for i in world_unlocks:
|
for i in world_unlocks:
|
||||||
c.execute('''insert into item values(?,"world_unlock",1,'')''', (i,))
|
c.execute('''insert into item values(?,"world_unlock",1,'')''', (i,))
|
||||||
|
|
||||||
|
|||||||
@@ -511,5 +511,46 @@
|
|||||||
],
|
],
|
||||||
"orig_price": 300,
|
"orig_price": 300,
|
||||||
"price": 300
|
"price": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dividedheart",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "pack",
|
||||||
|
"id": "dividedheart",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 5,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 500,
|
||||||
|
"price": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "musedash",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "pack",
|
||||||
|
"id": "musedash",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "character",
|
||||||
|
"id": "marija",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 5,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price": 400,
|
||||||
|
"orig_price": 400
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1018,5 +1018,113 @@
|
|||||||
],
|
],
|
||||||
"orig_price": 100,
|
"orig_price": 100,
|
||||||
"price": 100
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "summerfireworks",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "summerfireworks",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "init",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "init",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "internetoverdose",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "internetoverdose",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macromod",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "macromod",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "neowings",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "neowings",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kissinglucifer",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"id": "kissinglucifer",
|
||||||
|
"is_available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "core",
|
||||||
|
"amount": 1,
|
||||||
|
"id": "core_generic",
|
||||||
|
"is_available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orig_price": 100,
|
||||||
|
"price": 100
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
from flask import Flask, request, jsonify, make_response, send_from_directory
|
from flask import Flask, json, request, jsonify, send_from_directory
|
||||||
from logging.config import dictConfig
|
from logging.config import dictConfig
|
||||||
from setting import Config
|
from setting import Config
|
||||||
import base64
|
import base64
|
||||||
@@ -16,8 +16,15 @@ import server.arcdownload
|
|||||||
import server.arcpurchase
|
import server.arcpurchase
|
||||||
import server.init
|
import server.init
|
||||||
import server.character
|
import server.character
|
||||||
|
import server.arclinkplay
|
||||||
|
from udpserver.udp_main import link_play
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from multiprocessing import Process, Pipe
|
||||||
|
|
||||||
|
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
from werkzeug.datastructures import ImmutableMultiDict
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@@ -31,62 +38,7 @@ app.register_blueprint(web.login.bp)
|
|||||||
app.register_blueprint(web.index.bp)
|
app.register_blueprint(web.index.bp)
|
||||||
app.register_blueprint(api.api_main.bp)
|
app.register_blueprint(api.api_main.bp)
|
||||||
|
|
||||||
log_dict = {
|
conn1, conn2 = Pipe()
|
||||||
'version': 1,
|
|
||||||
'root': {
|
|
||||||
'level': 'INFO',
|
|
||||||
'handlers': ['wsgi', 'error_file']
|
|
||||||
},
|
|
||||||
'handlers': {
|
|
||||||
'wsgi': {
|
|
||||||
'class': 'logging.StreamHandler',
|
|
||||||
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
|
||||||
'formatter': 'default'
|
|
||||||
},
|
|
||||||
"error_file": {
|
|
||||||
"class": "logging.handlers.RotatingFileHandler",
|
|
||||||
"maxBytes": 1024 * 1024,
|
|
||||||
"backupCount": 1,
|
|
||||||
"encoding": "utf-8",
|
|
||||||
"level": "ERROR",
|
|
||||||
"formatter": "default",
|
|
||||||
"filename": "./log/error.log"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'formatters': {
|
|
||||||
'default': {
|
|
||||||
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if Config.ALLOW_LOG_INFO:
|
|
||||||
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file']
|
|
||||||
log_dict['handlers']['info_file'] = {
|
|
||||||
"class": "logging.handlers.RotatingFileHandler",
|
|
||||||
"maxBytes": 1024 * 1024,
|
|
||||||
"backupCount": 1,
|
|
||||||
"encoding": "utf-8",
|
|
||||||
"level": "INFO",
|
|
||||||
"formatter": "default",
|
|
||||||
"filename": "./log/info.log"
|
|
||||||
}
|
|
||||||
|
|
||||||
dictConfig(log_dict)
|
|
||||||
|
|
||||||
if not server.init.check_before_run(app):
|
|
||||||
app.logger.error('Something wrong. The server will not run.')
|
|
||||||
input('Press ENTER key to exit.')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
app.logger.info("Start to initialize data in 'songfile' table...")
|
|
||||||
try:
|
|
||||||
error = server.arcdownload.initialize_songfile()
|
|
||||||
except:
|
|
||||||
error = 'Something wrong.'
|
|
||||||
if error:
|
|
||||||
app.logger.warning(error)
|
|
||||||
else:
|
|
||||||
app.logger.info('Complete!')
|
|
||||||
|
|
||||||
|
|
||||||
def add_url_prefix(url, strange_flag=False):
|
def add_url_prefix(url, strange_flag=False):
|
||||||
@@ -119,7 +71,6 @@ def error_return(error_code, extra={}): # 错误返回
|
|||||||
# -4 您的账号已在别处登录
|
# -4 您的账号已在别处登录
|
||||||
# -3 无法连接至服务器
|
# -3 无法连接至服务器
|
||||||
# 2 Arcaea服务器正在维护
|
# 2 Arcaea服务器正在维护
|
||||||
# 5 请更新Arcaea到最新版本
|
|
||||||
# 9 新版本请等待几分钟
|
# 9 新版本请等待几分钟
|
||||||
# 100 无法在此ip地址下登录游戏
|
# 100 无法在此ip地址下登录游戏
|
||||||
# 101 用户名占用
|
# 101 用户名占用
|
||||||
@@ -127,12 +78,12 @@ def error_return(error_code, extra={}): # 错误返回
|
|||||||
# 103 已有一个账号由此设备创建
|
# 103 已有一个账号由此设备创建
|
||||||
# 104 用户名密码错误
|
# 104 用户名密码错误
|
||||||
# 105 24小时内登入两台设备
|
# 105 24小时内登入两台设备
|
||||||
# 106 账户冻结
|
# 106 121 账户冻结
|
||||||
# 107 你没有足够的体力
|
# 107 你没有足够的体力
|
||||||
# 113 活动已结束
|
# 113 活动已结束
|
||||||
# 114 该活动已结束,您的成绩不会提交
|
# 114 该活动已结束,您的成绩不会提交
|
||||||
|
# 115 请输入有效的电子邮箱地址
|
||||||
# 120 封号警告
|
# 120 封号警告
|
||||||
# 121 账户冻结
|
|
||||||
# 122 账户暂时冻结
|
# 122 账户暂时冻结
|
||||||
# 123 账户被限制
|
# 123 账户被限制
|
||||||
# 124 你今天不能再使用这个IP地址创建新的账号
|
# 124 你今天不能再使用这个IP地址创建新的账号
|
||||||
@@ -151,8 +102,13 @@ def error_return(error_code, extra={}): # 错误返回
|
|||||||
# 905 请在再次使用此功能前等待24小时
|
# 905 请在再次使用此功能前等待24小时
|
||||||
# 1001 设备数量达到上限
|
# 1001 设备数量达到上限
|
||||||
# 1002 此设备已使用过此功能
|
# 1002 此设备已使用过此功能
|
||||||
|
# 1201 房间已满
|
||||||
|
# 1202 房间号码无效
|
||||||
|
# 1203 请将Arcaea更新至最新版本
|
||||||
|
# 1205 此房间目前无法加入
|
||||||
# 9801 下载歌曲时发生问题,请再试一次
|
# 9801 下载歌曲时发生问题,请再试一次
|
||||||
# 9802 保存歌曲时发生问题,请检查设备空间容量
|
# 9802 保存歌曲时发生问题,请检查设备空间容量
|
||||||
|
# 9803 下载已取消
|
||||||
# 9905 没有在云端发现任何数据
|
# 9905 没有在云端发现任何数据
|
||||||
# 9907 更新数据时发生了问题
|
# 9907 更新数据时发生了问题
|
||||||
# 9908 服务器只支持最新的版本,请更新Arcaea
|
# 9908 服务器只支持最新的版本,请更新Arcaea
|
||||||
@@ -170,10 +126,16 @@ def error_return(error_code, extra={}): # 错误返回
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def success_return(value):
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"value": value
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def hello():
|
def hello():
|
||||||
return "Hello World!"
|
return "Hello World!"
|
||||||
# 自定义路径
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/favicon.ico', methods=['GET']) # 图标
|
@app.route('/favicon.ico', methods=['GET']) # 图标
|
||||||
@@ -190,7 +152,7 @@ def login():
|
|||||||
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(5)
|
return error_return(1203)
|
||||||
|
|
||||||
headers = request.headers
|
headers = request.headers
|
||||||
id_pwd = headers['Authorization']
|
id_pwd = headers['Authorization']
|
||||||
@@ -201,10 +163,10 @@ def login():
|
|||||||
else:
|
else:
|
||||||
device_id = 'low_version'
|
device_id = 'low_version'
|
||||||
|
|
||||||
token, error_code, extra = server.auth.arc_login(
|
token, user_id, error_code, extra = server.auth.arc_login(
|
||||||
name, password, device_id, request.remote_addr)
|
name, password, device_id, request.remote_addr)
|
||||||
if not error_code:
|
if not error_code:
|
||||||
r = {"success": True, "token_type": "Bearer"}
|
r = {"success": True, "token_type": "Bearer", 'user_id': user_id}
|
||||||
r['access_token'] = token
|
r['access_token'] = token
|
||||||
return jsonify(r)
|
return jsonify(r)
|
||||||
else:
|
else:
|
||||||
@@ -214,7 +176,7 @@ def login():
|
|||||||
return error_return(error_code)
|
return error_return(error_code)
|
||||||
|
|
||||||
|
|
||||||
@app.route(add_url_prefix('/user/'), methods=['POST']) # 注册接口
|
@app.route(add_url_prefix('/user'), methods=['POST']) # 注册接口
|
||||||
def register():
|
def register():
|
||||||
if 'AppVersion' in request.headers: # 版本检查
|
if 'AppVersion' in request.headers: # 版本检查
|
||||||
if Config.ALLOW_APPVERSION:
|
if Config.ALLOW_APPVERSION:
|
||||||
@@ -239,25 +201,83 @@ def register():
|
|||||||
return error_return(error_code)
|
return error_return(error_code)
|
||||||
|
|
||||||
|
|
||||||
# 集成式请求,没想到什么好办法处理,就先这样写着
|
@app.route(add_url_prefix('/purchase/bundle/pack'), methods=['GET']) # 曲包信息
|
||||||
@app.route(add_url_prefix('/compose/aggregate'), methods=['GET'])
|
|
||||||
@server.auth.auth_required(request)
|
@server.auth.auth_required(request)
|
||||||
def aggregate(user_id):
|
def bundle_pack(user_id):
|
||||||
calls = request.args.get('calls')
|
return success_return(server.info.get_purchase_pack(user_id))
|
||||||
if calls == '[{ "endpoint": "/user/me", "id": 0 }]': # 极其沙雕的判断,我猜get的参数就两种
|
|
||||||
r = server.info.arc_aggregate_small(user_id)
|
|
||||||
else:
|
|
||||||
r = server.info.arc_aggregate_big(user_id)
|
|
||||||
return jsonify(r)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route(add_url_prefix('/user/me'), methods=['GET']) # 用户信息,给baa查分器用的
|
@app.route(add_url_prefix('/game/info'), methods=['GET']) # 系统信息
|
||||||
|
def game_info():
|
||||||
|
return success_return(server.info.get_game_info())
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(add_url_prefix('/present/me'), methods=['GET']) # 用户奖励信息
|
||||||
|
@server.auth.auth_required(request)
|
||||||
|
def present_info(user_id):
|
||||||
|
return success_return(server.info.get_user_present(user_id))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(add_url_prefix('/compose/aggregate'), methods=['GET']) # 集成式请求
|
||||||
|
def aggregate():
|
||||||
|
try:
|
||||||
|
#global request
|
||||||
|
finally_response = {'success': True, 'value': []}
|
||||||
|
#request_ = request
|
||||||
|
get_list = json.loads(request.args.get('calls'))
|
||||||
|
if len(get_list) > 10:
|
||||||
|
# 请求太多驳回
|
||||||
|
return error_return(108)
|
||||||
|
|
||||||
|
for i in get_list:
|
||||||
|
endpoint = i['endpoint']
|
||||||
|
url = add_url_prefix(endpoint)
|
||||||
|
request.args = ImmutableMultiDict(
|
||||||
|
{key: value[0] for key, value in parse_qs(urlparse(url).query).items()})
|
||||||
|
|
||||||
|
resp_t = map_dict[urlparse(endpoint).path]()
|
||||||
|
|
||||||
|
if hasattr(resp_t, "response"):
|
||||||
|
resp_t = resp_t.response[0].decode().rstrip('\n')
|
||||||
|
resp = json.loads(resp_t)
|
||||||
|
|
||||||
|
if hasattr(resp, 'get') and resp.get('success') is False:
|
||||||
|
finally_response = {'success': False, 'error_code': 7, 'extra': {
|
||||||
|
"id": i['id'], 'error_code': resp.get('error_code')}}
|
||||||
|
if "extra" in resp:
|
||||||
|
finally_response['extra']['extra'] = resp['extra']
|
||||||
|
#request = request_
|
||||||
|
return jsonify(finally_response)
|
||||||
|
|
||||||
|
finally_response['value'].append(
|
||||||
|
{'id': i.get('id'), 'value': resp['value'] if hasattr(resp, 'get') else resp})
|
||||||
|
|
||||||
|
#request = request_
|
||||||
|
return jsonify(finally_response)
|
||||||
|
except KeyError:
|
||||||
|
return error_return(108)
|
||||||
|
|
||||||
|
|
||||||
|
# # 集成式请求,没想到什么好办法处理,就先这样写着
|
||||||
|
# @app.route(add_url_prefix('/compose/aggregate'), methods=['GET'])
|
||||||
|
# @server.auth.auth_required(request)
|
||||||
|
# def aggregate(user_id):
|
||||||
|
# calls = request.args.get('calls')
|
||||||
|
# if calls == '[{ "endpoint": "/user/me", "id": 0 }]': # 极其沙雕的判断,我猜get的参数就两种
|
||||||
|
# r = server.info.arc_aggregate_small(user_id)
|
||||||
|
# else:
|
||||||
|
# r = server.info.arc_aggregate_big(user_id)
|
||||||
|
# return jsonify(r)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(add_url_prefix('/user/me'), methods=['GET']) # 用户信息
|
||||||
@server.auth.auth_required(request)
|
@server.auth.auth_required(request)
|
||||||
def user_me(user_id):
|
def user_me(user_id):
|
||||||
r = server.info.arc_aggregate_small(user_id)
|
r = server.info.get_user_me_c(user_id)
|
||||||
if r['success']:
|
if r:
|
||||||
r['value'] = r['value'][0]['value']
|
return success_return(r)
|
||||||
return jsonify(r)
|
else:
|
||||||
|
return error_return(108)
|
||||||
|
|
||||||
|
|
||||||
@app.route(add_url_prefix('/user/me/character'), methods=['POST']) # 角色切换
|
@app.route(add_url_prefix('/user/me/character'), methods=['POST']) # 角色切换
|
||||||
@@ -662,10 +682,11 @@ def world_one(user_id, map_id):
|
|||||||
@server.auth.auth_required(request)
|
@server.auth.auth_required(request)
|
||||||
def download_song(user_id):
|
def download_song(user_id):
|
||||||
song_ids = request.args.getlist('sid')
|
song_ids = request.args.getlist('sid')
|
||||||
if server.arcdownload.is_able_download(user_id):
|
url_flag = json.loads(request.args.get('url', 'true'))
|
||||||
|
if server.arcdownload.is_able_download(user_id) or not url_flag:
|
||||||
re = {}
|
re = {}
|
||||||
if not song_ids:
|
if not song_ids:
|
||||||
re = server.arcdownload.get_all_songs(user_id)
|
re = server.arcdownload.get_all_songs(user_id, url_flag=url_flag)
|
||||||
else:
|
else:
|
||||||
re = server.arcdownload.get_some_songs(user_id, song_ids)
|
re = server.arcdownload.get_some_songs(user_id, song_ids)
|
||||||
|
|
||||||
@@ -681,7 +702,7 @@ def download_song(user_id):
|
|||||||
def download(file_path):
|
def download(file_path):
|
||||||
try:
|
try:
|
||||||
t = request.args.get('t')
|
t = request.args.get('t')
|
||||||
message = server.arcdownload.is_token_able_download(t)
|
message = server.arcdownload.is_token_able_download(t, file_path)
|
||||||
if message == 0:
|
if message == 0:
|
||||||
path = os.path.join('./database/songs', file_path)
|
path = os.path.join('./database/songs', file_path)
|
||||||
if os.path.isfile(path) and not('../' in path or '..\\' in path):
|
if os.path.isfile(path) and not('../' in path or '..\\' in path):
|
||||||
@@ -694,6 +715,88 @@ def download(file_path):
|
|||||||
return error_return(108)
|
return error_return(108)
|
||||||
|
|
||||||
|
|
||||||
|
# 创建房间
|
||||||
|
@app.route(add_url_prefix('/multiplayer/me/room/create'), methods=['POST'])
|
||||||
|
@server.auth.auth_required(request)
|
||||||
|
def room_create(user_id):
|
||||||
|
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
||||||
|
return error_return(151), 404
|
||||||
|
|
||||||
|
client_song_map = request.json['clientSongMap']
|
||||||
|
error_code, value = server.arclinkplay.create_room(
|
||||||
|
conn1, user_id, client_song_map)
|
||||||
|
|
||||||
|
if error_code == 0:
|
||||||
|
if Config.LINK_PLAY_HOST == '':
|
||||||
|
value['endPoint'] = request.host.split(':')[0]
|
||||||
|
else:
|
||||||
|
value['endPoint'] = Config.LINK_PLAY_HOST
|
||||||
|
|
||||||
|
value['port'] = int(Config.UDP_PORT)
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"value": value
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return error_return(error_code), 400
|
||||||
|
|
||||||
|
|
||||||
|
# 加入房间
|
||||||
|
@app.route(add_url_prefix('/multiplayer/me/room/join/<room_code>'), methods=['POST'])
|
||||||
|
@server.auth.auth_required(request)
|
||||||
|
def room_join(user_id, room_code):
|
||||||
|
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
||||||
|
return error_return(151), 404
|
||||||
|
|
||||||
|
client_song_map = request.json['clientSongMap']
|
||||||
|
error_code, value = server.arclinkplay.join_room(
|
||||||
|
conn1, user_id, client_song_map, room_code)
|
||||||
|
|
||||||
|
if error_code == 0:
|
||||||
|
if Config.LINK_PLAY_HOST == '':
|
||||||
|
value['endPoint'] = request.host.split(':')[0]
|
||||||
|
else:
|
||||||
|
value['endPoint'] = Config.LINK_PLAY_HOST
|
||||||
|
|
||||||
|
value['port'] = int(Config.UDP_PORT)
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"value": value
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return error_return(error_code), 400
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(add_url_prefix('/multiplayer/me/update'), methods=['POST']) # 更新房间
|
||||||
|
@server.auth.auth_required(request)
|
||||||
|
def multiplayer_update(user_id):
|
||||||
|
if not Config.UDP_PORT or Config.UDP_PORT == '':
|
||||||
|
return error_return(151), 404
|
||||||
|
|
||||||
|
token = request.json['token']
|
||||||
|
error_code, value = server.arclinkplay.update_room(conn1, user_id, token)
|
||||||
|
|
||||||
|
if error_code == 0:
|
||||||
|
if Config.LINK_PLAY_HOST == '':
|
||||||
|
value['endPoint'] = request.host.split(':')[0]
|
||||||
|
else:
|
||||||
|
value['endPoint'] = Config.LINK_PLAY_HOST
|
||||||
|
|
||||||
|
value['port'] = int(Config.UDP_PORT)
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"value": value
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return error_return(error_code), 400
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(add_url_prefix('/user/me/request_delete'), methods=['POST']) # 删除账号
|
||||||
|
@server.auth.auth_required(request)
|
||||||
|
def user_delete(user_id):
|
||||||
|
return error_return(151), 404
|
||||||
|
|
||||||
|
|
||||||
# 三个设置,写在最后降低优先级
|
# 三个设置,写在最后降低优先级
|
||||||
@app.route(add_url_prefix('/<path:path>', True), methods=['POST'])
|
@app.route(add_url_prefix('/<path:path>', True), methods=['POST'])
|
||||||
@server.auth.auth_required(request)
|
@server.auth.auth_required(request)
|
||||||
@@ -701,12 +804,92 @@ def sys_set(user_id, path):
|
|||||||
set_arg = path[5:]
|
set_arg = path[5:]
|
||||||
value = request.form['value']
|
value = request.form['value']
|
||||||
server.setme.arc_sys_set(user_id, value, set_arg)
|
server.setme.arc_sys_set(user_id, value, set_arg)
|
||||||
r = server.info.arc_aggregate_small(user_id)
|
r = server.info.get_user_me_c(user_id)
|
||||||
r['value'] = r['value'][0]['value']
|
if r:
|
||||||
return jsonify(r)
|
return success_return(r)
|
||||||
|
else:
|
||||||
|
return error_return(108)
|
||||||
|
|
||||||
|
|
||||||
|
map_dict = {'/user/me': user_me,
|
||||||
|
'/purchase/bundle/pack': bundle_pack,
|
||||||
|
'/serve/download/me/song': download_song,
|
||||||
|
'/game/info': game_info,
|
||||||
|
'/present/me': present_info,
|
||||||
|
'/world/map/me': world_all,
|
||||||
|
'/score/song/friend': song_score_friend}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
log_dict = {
|
||||||
|
'version': 1,
|
||||||
|
'root': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'handlers': ['wsgi', 'error_file']
|
||||||
|
},
|
||||||
|
'handlers': {
|
||||||
|
'wsgi': {
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
||||||
|
'formatter': 'default'
|
||||||
|
},
|
||||||
|
"error_file": {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"maxBytes": 1024 * 1024,
|
||||||
|
"backupCount": 1,
|
||||||
|
"encoding": "utf-8",
|
||||||
|
"level": "ERROR",
|
||||||
|
"formatter": "default",
|
||||||
|
"filename": "./log/error.log"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'formatters': {
|
||||||
|
'default': {
|
||||||
|
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if Config.ALLOW_LOG_INFO:
|
||||||
|
log_dict['root']['handlers'] = ['wsgi', 'info_file', 'error_file']
|
||||||
|
log_dict['handlers']['info_file'] = {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"maxBytes": 1024 * 1024,
|
||||||
|
"backupCount": 1,
|
||||||
|
"encoding": "utf-8",
|
||||||
|
"level": "INFO",
|
||||||
|
"formatter": "default",
|
||||||
|
"filename": "./log/info.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
dictConfig(log_dict)
|
||||||
|
|
||||||
|
if not server.init.check_before_run(app):
|
||||||
|
app.logger.error('Something wrong. The server will not run.')
|
||||||
|
input('Press ENTER key to exit.')
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
app.logger.info("Start to initialize data in 'songfile' table...")
|
||||||
|
try:
|
||||||
|
error = server.arcdownload.initialize_songfile()
|
||||||
|
except:
|
||||||
|
error = 'Something wrong.'
|
||||||
|
if error:
|
||||||
|
app.logger.warning(error)
|
||||||
|
else:
|
||||||
|
app.logger.info('Complete!')
|
||||||
|
|
||||||
|
if Config.UDP_PORT and Config.UDP_PORT != '':
|
||||||
|
process = [Process(target=link_play, args=(
|
||||||
|
conn2, Config.HOST, int(Config.UDP_PORT)))]
|
||||||
|
[p.start() for p in process]
|
||||||
|
app.logger.info("UDP server is running...")
|
||||||
|
if Config.SSL_CERT and Config.SSL_KEY:
|
||||||
|
app.run(Config.HOST, Config.PORT, ssl_context=(
|
||||||
|
Config.SSL_CERT, Config.SSL_KEY))
|
||||||
|
else:
|
||||||
|
app.run(Config.HOST, Config.PORT)
|
||||||
|
[p.join() for p in process]
|
||||||
|
else:
|
||||||
if Config.SSL_CERT and Config.SSL_KEY:
|
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))
|
||||||
|
|||||||
2
latest version/requirements.txt
Normal file
2
latest version/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
flask
|
||||||
|
cryptography
|
||||||
0
latest version/server/__init__.py
Normal file
0
latest version/server/__init__.py
Normal file
@@ -51,11 +51,14 @@ def get_one_song(c, user_id, song_id, file_dir='./database/songs', url_flag=True
|
|||||||
'a': user_id, 'b': song_id})
|
'a': user_id, 'b': song_id})
|
||||||
|
|
||||||
for i in dir_list:
|
for i in dir_list:
|
||||||
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg']:
|
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
|
||||||
token = hashlib.md5(
|
token = hashlib.md5(
|
||||||
(str(user_id) + song_id + i + str(now)).encode(encoding='UTF-8')).hexdigest()
|
(str(user_id) + song_id + i + str(now)).encode(encoding='UTF-8')).hexdigest()
|
||||||
|
|
||||||
if i == 'base.ogg':
|
if i == 'base.ogg':
|
||||||
|
if 'audio' not in re:
|
||||||
|
re['audio'] = {}
|
||||||
|
|
||||||
c.execute(
|
c.execute(
|
||||||
'''select md5 from songfile where song_id=:a and file_type=-1''', {'a': song_id})
|
'''select md5 from songfile where song_id=:a and file_type=-1''', {'a': song_id})
|
||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
@@ -65,11 +68,29 @@ def get_one_song(c, user_id, song_id, file_dir='./database/songs', url_flag=True
|
|||||||
checksum = get_file_md5(os.path.join(
|
checksum = get_file_md5(os.path.join(
|
||||||
file_dir, song_id, 'base.ogg'))
|
file_dir, song_id, 'base.ogg'))
|
||||||
|
|
||||||
|
re['audio']["checksum"] = checksum
|
||||||
if url_flag:
|
if url_flag:
|
||||||
re['audio'] = {"checksum": checksum, "url": get_url(
|
re['audio']["url"] = get_url(
|
||||||
file_path=song_id+'/base.ogg', t=token)}
|
file_path=song_id+'/base.ogg', t=token)
|
||||||
|
|
||||||
|
elif i == '3.ogg':
|
||||||
|
if 'audio' not in re:
|
||||||
|
re['audio'] = {}
|
||||||
|
|
||||||
|
c.execute(
|
||||||
|
'''select md5 from songfile where song_id=:a and file_type=-2''', {'a': song_id})
|
||||||
|
x = c.fetchone()
|
||||||
|
if x:
|
||||||
|
checksum = x[0]
|
||||||
else:
|
else:
|
||||||
re['audio'] = {"checksum": checksum}
|
checksum = get_file_md5(os.path.join(
|
||||||
|
file_dir, song_id, '3.ogg'))
|
||||||
|
|
||||||
|
if url_flag:
|
||||||
|
re['audio']['3'] = {"checksum": checksum, "url": get_url(
|
||||||
|
file_path=song_id+'/3.ogg', t=token)}
|
||||||
|
else:
|
||||||
|
re['audio']['3'] = {"checksum": checksum}
|
||||||
else:
|
else:
|
||||||
if 'chart' not in re:
|
if 'chart' not in re:
|
||||||
re['chart'] = {}
|
re['chart'] = {}
|
||||||
@@ -116,7 +137,7 @@ def get_some_songs(user_id, song_ids):
|
|||||||
return re
|
return re
|
||||||
|
|
||||||
|
|
||||||
def is_token_able_download(t):
|
def is_token_able_download(t, path):
|
||||||
# token是否可以下载,返回错误码,0即可以
|
# token是否可以下载,返回错误码,0即可以
|
||||||
errorcode = 108
|
errorcode = 108
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
@@ -124,7 +145,7 @@ def is_token_able_download(t):
|
|||||||
{'t': t})
|
{'t': t})
|
||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
if x and now - x[4] <= time_gap_limit:
|
if x and now - x[4] <= time_gap_limit and x[1]+'/'+x[2] == path:
|
||||||
c.execute(
|
c.execute(
|
||||||
'''select count(*) from user_download where user_id = :a''', {'a': x[0]})
|
'''select count(*) from user_download where user_id = :a''', {'a': x[0]})
|
||||||
y = c.fetchone()
|
y = c.fetchone()
|
||||||
@@ -162,10 +183,13 @@ def initialize_one_songfile(c, song_id, file_dir='./database/songs'):
|
|||||||
# 计算并添加歌曲md5到表中,无返回
|
# 计算并添加歌曲md5到表中,无返回
|
||||||
dir_list = os.listdir(os.path.join(file_dir, song_id))
|
dir_list = os.listdir(os.path.join(file_dir, song_id))
|
||||||
for i in dir_list:
|
for i in dir_list:
|
||||||
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg']:
|
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
|
||||||
if i == 'base.ogg':
|
if i == 'base.ogg':
|
||||||
c.execute('''insert into songfile values(:a,-1,:c)''', {
|
c.execute('''insert into songfile values(:a,-1,:c)''', {
|
||||||
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, 'base.ogg'))})
|
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, 'base.ogg'))})
|
||||||
|
elif i == '3.ogg':
|
||||||
|
c.execute('''insert into songfile values(:a,-2,:c)''', {
|
||||||
|
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, '3.ogg'))})
|
||||||
else:
|
else:
|
||||||
c.execute('''insert into songfile values(:a,:b,:c)''', {
|
c.execute('''insert into songfile values(:a,:b,:c)''', {
|
||||||
'a': song_id, 'b': int(i[0]), 'c': get_file_md5(os.path.join(file_dir, song_id, i))})
|
'a': song_id, 'b': int(i[0]), 'c': get_file_md5(os.path.join(file_dir, song_id, i))})
|
||||||
|
|||||||
113
latest version/server/arclinkplay.py
Normal file
113
latest version/server/arclinkplay.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
from .sql import Connect
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
|
def get_song_unlock(client_song_map):
|
||||||
|
# 处理可用歌曲bit,返回bytes
|
||||||
|
|
||||||
|
user_song_unlock = [0] * 512
|
||||||
|
for i in range(0, 1024, 2):
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
if str(i) in client_song_map:
|
||||||
|
if client_song_map[str(i)][0]:
|
||||||
|
x += 1
|
||||||
|
if client_song_map[str(i)][1]:
|
||||||
|
x += 2
|
||||||
|
if client_song_map[str(i)][2]:
|
||||||
|
x += 4
|
||||||
|
if client_song_map[str(i)][3]:
|
||||||
|
x += 8
|
||||||
|
if str(i+1) in client_song_map:
|
||||||
|
if client_song_map[str(i+1)][0]:
|
||||||
|
y += 1
|
||||||
|
if client_song_map[str(i+1)][1]:
|
||||||
|
y += 2
|
||||||
|
if client_song_map[str(i+1)][2]:
|
||||||
|
y += 4
|
||||||
|
if client_song_map[str(i+1)][3]:
|
||||||
|
y += 8
|
||||||
|
|
||||||
|
user_song_unlock[i // 2] = y*16 + x
|
||||||
|
|
||||||
|
return bytes(user_song_unlock)
|
||||||
|
|
||||||
|
|
||||||
|
def create_room(conn, user_id, client_song_map):
|
||||||
|
# 创建房间,返回错误码和房间与用户信息
|
||||||
|
error_code = 108
|
||||||
|
|
||||||
|
with Connect() as c:
|
||||||
|
c.execute('''select name from user where user_id=?''', (user_id,))
|
||||||
|
x = c.fetchone()
|
||||||
|
if x is not None:
|
||||||
|
name = x[0]
|
||||||
|
|
||||||
|
song_unlock = get_song_unlock(client_song_map)
|
||||||
|
|
||||||
|
conn.send((1, name, song_unlock))
|
||||||
|
data = conn.recv()
|
||||||
|
if data[0] == 0:
|
||||||
|
error_code = 0
|
||||||
|
return error_code, {'roomCode': data[1],
|
||||||
|
'roomId': str(data[2]),
|
||||||
|
'token': str(data[3]),
|
||||||
|
'key': (base64.b64encode(data[4])).decode(),
|
||||||
|
'playerId': str(data[5]),
|
||||||
|
'userId': user_id,
|
||||||
|
'orderedAllowedSongs': (base64.b64encode(song_unlock)).decode()
|
||||||
|
}
|
||||||
|
|
||||||
|
return error_code, None
|
||||||
|
|
||||||
|
|
||||||
|
def join_room(conn, user_id, client_song_map, room_code):
|
||||||
|
# 加入房间,返回错误码和房间与用户信息
|
||||||
|
error_code = 108
|
||||||
|
|
||||||
|
with Connect() as c:
|
||||||
|
c.execute('''select name from user where user_id=?''', (user_id,))
|
||||||
|
x = c.fetchone()
|
||||||
|
if x is not None:
|
||||||
|
name = x[0]
|
||||||
|
|
||||||
|
song_unlock = get_song_unlock(client_song_map)
|
||||||
|
|
||||||
|
conn.send((2, name, song_unlock, room_code))
|
||||||
|
data = conn.recv()
|
||||||
|
if data[0] == 0:
|
||||||
|
error_code = 0
|
||||||
|
return error_code, {'roomCode': data[1],
|
||||||
|
'roomId': str(data[2]),
|
||||||
|
'token': str(data[3]),
|
||||||
|
'key': (base64.b64encode(data[4])).decode(),
|
||||||
|
'playerId': str(data[5]),
|
||||||
|
'userId': user_id,
|
||||||
|
'orderedAllowedSongs': (base64.b64encode(data[6])).decode()
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
error_code = data[0]
|
||||||
|
|
||||||
|
return error_code, None
|
||||||
|
|
||||||
|
|
||||||
|
def update_room(conn, user_id, token):
|
||||||
|
# 更新房间,返回错误码和房间与用户信息
|
||||||
|
error_code = 108
|
||||||
|
|
||||||
|
conn.send((3, int(token)))
|
||||||
|
data = conn.recv()
|
||||||
|
if data[0] == 0:
|
||||||
|
error_code = 0
|
||||||
|
return error_code, {'roomCode': data[1],
|
||||||
|
'roomId': str(data[2]),
|
||||||
|
'token': token,
|
||||||
|
'key': (base64.b64encode(data[3])).decode(),
|
||||||
|
'playerId': str(data[4]),
|
||||||
|
'userId': user_id,
|
||||||
|
'orderedAllowedSongs': (base64.b64encode(data[5])).decode()
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
error_code = data[0]
|
||||||
|
|
||||||
|
return error_code, None
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
from server.sql import Connect
|
from server.sql import Connect
|
||||||
|
from .config import Constant
|
||||||
from setting import Config
|
from setting import Config
|
||||||
import server.item
|
import server.item
|
||||||
import server.character
|
import server.character
|
||||||
@@ -7,10 +8,7 @@ import server.info
|
|||||||
import server.arcpurchase
|
import server.arcpurchase
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
ETO_UNCAP_BONUS_PROGRESS = 7
|
|
||||||
LUNA_UNCAP_BONUS_PROGRESS = 7
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
def int2b(x):
|
||||||
@@ -24,14 +22,14 @@ def int2b(x):
|
|||||||
def calc_stamina(max_stamina_ts, curr_stamina):
|
def calc_stamina(max_stamina_ts, curr_stamina):
|
||||||
# 计算体力,返回剩余体力数值
|
# 计算体力,返回剩余体力数值
|
||||||
|
|
||||||
stamina = int(server.info.MAX_STAMINA - (max_stamina_ts -
|
stamina = int(Constant.MAX_STAMINA - (max_stamina_ts -
|
||||||
int(time.time()*1000)) / server.info.STAMINA_RECOVER_TICK)
|
int(time.time()*1000)) / Constant.STAMINA_RECOVER_TICK)
|
||||||
|
|
||||||
if stamina >= server.info.MAX_STAMINA:
|
if stamina >= Constant.MAX_STAMINA:
|
||||||
if curr_stamina >= server.info.MAX_STAMINA:
|
if curr_stamina >= Constant.MAX_STAMINA:
|
||||||
stamina = curr_stamina
|
stamina = curr_stamina
|
||||||
else:
|
else:
|
||||||
stamina = server.info.MAX_STAMINA
|
stamina = Constant.MAX_STAMINA
|
||||||
if stamina < 0:
|
if stamina < 0:
|
||||||
stamina = 0
|
stamina = 0
|
||||||
|
|
||||||
@@ -243,8 +241,8 @@ def play_world_song(user_id, args):
|
|||||||
return {}
|
return {}
|
||||||
stamina = calc_stamina(max_stamina_ts, stamina) - \
|
stamina = calc_stamina(max_stamina_ts, stamina) - \
|
||||||
info['stamina_cost'] * stamina_multiply
|
info['stamina_cost'] * stamina_multiply
|
||||||
max_stamina_ts = now + server.info.STAMINA_RECOVER_TICK * \
|
max_stamina_ts = now + Constant.STAMINA_RECOVER_TICK * \
|
||||||
(server.info.MAX_STAMINA - stamina)
|
(Constant.MAX_STAMINA - stamina)
|
||||||
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
|
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
|
||||||
(max_stamina_ts, stamina, user_id))
|
(max_stamina_ts, stamina, user_id))
|
||||||
r = {
|
r = {
|
||||||
@@ -354,7 +352,7 @@ def climb_step(user_id, map_id, step, prev_capture, prev_position):
|
|||||||
return rewards, steps, curr_position, curr_capture, info
|
return rewards, steps, curr_position, curr_capture, info
|
||||||
|
|
||||||
|
|
||||||
def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gauge, stamina_multiply=1, fragment_multiply=100, prog_boost_multiply=0):
|
def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gauge, health, stamina_multiply=1, fragment_multiply=100, prog_boost_multiply=0):
|
||||||
# 成绩上传后世界模式更新,返回字典
|
# 成绩上传后世界模式更新,返回字典
|
||||||
|
|
||||||
step_times = stamina_multiply * fragment_multiply / \
|
step_times = stamina_multiply * fragment_multiply / \
|
||||||
@@ -375,6 +373,11 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
is_skill_sealed = x[3] if x and x[3] is not None else 1
|
is_skill_sealed = x[3] if x and x[3] is not None else 1
|
||||||
skill = False
|
skill = False
|
||||||
skill_uncap = False
|
skill_uncap = False
|
||||||
|
level = 1
|
||||||
|
exp = 0
|
||||||
|
frag = 50
|
||||||
|
prog = 50
|
||||||
|
overdrive = 50
|
||||||
if not is_skill_sealed:
|
if not is_skill_sealed:
|
||||||
if x:
|
if x:
|
||||||
skill = True
|
skill = True
|
||||||
@@ -401,9 +404,10 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
exp = 0
|
exp = 0
|
||||||
|
|
||||||
if x:
|
if x:
|
||||||
flag = server.character.calc_char_value(level, x[0], x[3], x[6])
|
frag = server.character.calc_char_value(level, x[0], x[3], x[6])
|
||||||
prog = server.character.calc_char_value(level, x[1], x[4], x[7])
|
prog = server.character.calc_char_value(level, x[1], x[4], x[7])
|
||||||
overdrive = server.character.calc_char_value(level, x[2], x[5], x[8])
|
overdrive = server.character.calc_char_value(
|
||||||
|
level, x[2], x[5], x[8])
|
||||||
if x[9] is not None and x[9] != '' and skill:
|
if x[9] is not None and x[9] != '' and skill:
|
||||||
skill = x[9]
|
skill = x[9]
|
||||||
else:
|
else:
|
||||||
@@ -413,25 +417,45 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
else:
|
else:
|
||||||
skill_uncap = None
|
skill_uncap = None
|
||||||
else:
|
else:
|
||||||
flag = 0
|
frag = 0
|
||||||
prog = 0
|
prog = 0
|
||||||
overdrive = 0
|
overdrive = 0
|
||||||
skill = None
|
skill = None
|
||||||
skill_uncap = None
|
skill_uncap = None
|
||||||
|
|
||||||
|
skill_special = ''
|
||||||
|
if skill_uncap is not None and skill_uncap and skill_uncap in ['eto_uncap', 'luna_uncap', 'ayu_uncap', 'skill_vita']:
|
||||||
|
skill_special = skill_uncap
|
||||||
|
elif skill is not None and skill and skill in ['eto_uncap', 'luna_uncap', 'ayu_uncap', 'skill_vita']:
|
||||||
|
skill_special = skill
|
||||||
|
|
||||||
c.execute('''select current_map from user where user_id = :a''', {
|
c.execute('''select current_map from user where user_id = :a''', {
|
||||||
'a': user_id})
|
'a': user_id})
|
||||||
map_id = c.fetchone()[0]
|
map_id = c.fetchone()[0]
|
||||||
|
|
||||||
if beyond_gauge == 0: # 是否是beyond挑战
|
if beyond_gauge == 0: # 是否是beyond挑战
|
||||||
|
prog_tempest = 0
|
||||||
|
if not is_skill_sealed and character_id == 35:
|
||||||
|
# 风暴对立
|
||||||
|
if Config.CHARACTER_FULL_UNLOCK:
|
||||||
|
prog_tempest = 60
|
||||||
|
else:
|
||||||
|
c.execute(
|
||||||
|
'''select sum(level) from user_char where user_id=?''', (user_id,))
|
||||||
|
prog_tempest = int(x[0]) / 10 if x else 0
|
||||||
|
if prog_tempest > 60:
|
||||||
|
prog_tempest = 60
|
||||||
|
elif prog_tempest < 0:
|
||||||
|
prog_tempest = 0
|
||||||
|
|
||||||
base_step = 2.5 + 2.45*rating**0.5
|
base_step = 2.5 + 2.45*rating**0.5
|
||||||
step = base_step * (prog/50) * step_times
|
step = base_step * (prog + prog_tempest) / 50 * step_times
|
||||||
else:
|
else:
|
||||||
info = get_world_info(map_id)
|
info = get_world_info(map_id)
|
||||||
if clear_type == 0:
|
if clear_type == 0:
|
||||||
base_step = 8/9 + (rating/1.3)**0.5
|
base_step = 25/28 + (rating)**0.5 * 0.43
|
||||||
else:
|
else:
|
||||||
base_step = 8/3 + (rating/1.3)**0.5
|
base_step = 75/28 + (rating)**0.5 * 0.43
|
||||||
|
|
||||||
if character_id in info['character_affinity']:
|
if character_id in info['character_affinity']:
|
||||||
affinity_multiplier = info['affinity_multiplier'][info['character_affinity'].index(
|
affinity_multiplier = info['affinity_multiplier'][info['character_affinity'].index(
|
||||||
@@ -439,7 +463,17 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
else:
|
else:
|
||||||
affinity_multiplier = 1
|
affinity_multiplier = 1
|
||||||
|
|
||||||
step = base_step * (prog/50) * step_times * affinity_multiplier
|
if skill_special == 'skill_vita':
|
||||||
|
# vita技能,overdrive随回忆率提升,提升量最多为10
|
||||||
|
# 此处采用线性函数
|
||||||
|
overdrive_extra = 0
|
||||||
|
if 0 < health <= 100:
|
||||||
|
overdrive_extra = health / 10
|
||||||
|
|
||||||
|
overdrive += overdrive_extra
|
||||||
|
|
||||||
|
step = base_step * overdrive / 50 * \
|
||||||
|
step_times * affinity_multiplier
|
||||||
|
|
||||||
c.execute('''select * from user_world where user_id = :a and map_id =:b''',
|
c.execute('''select * from user_world where user_id = :a and map_id =:b''',
|
||||||
{'a': user_id, 'b': map_id})
|
{'a': user_id, 'b': map_id})
|
||||||
@@ -449,13 +483,9 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
|
|
||||||
rewards, steps, curr_position, curr_capture, info = climb_step(
|
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||||
user_id, map_id, step, y[3], y[2])
|
user_id, map_id, step, y[3], y[2])
|
||||||
# Eto和Luna的技能
|
|
||||||
|
# Eto、Luna、Ayu的技能
|
||||||
character_bonus_progress = None
|
character_bonus_progress = None
|
||||||
skill_special = ''
|
|
||||||
if skill_uncap is not None and skill_uncap and skill_uncap in ['eto_uncap', 'luna_uncap']:
|
|
||||||
skill_special = skill_uncap
|
|
||||||
elif skill is not None and skill and skill in ['eto_uncap', 'luna_uncap']:
|
|
||||||
skill_special = skill
|
|
||||||
if skill_special == 'eto_uncap':
|
if skill_special == 'eto_uncap':
|
||||||
# eto觉醒技能,获得残片奖励时世界模式进度加7
|
# eto觉醒技能,获得残片奖励时世界模式进度加7
|
||||||
fragment_flag = False
|
fragment_flag = False
|
||||||
@@ -467,7 +497,7 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
if fragment_flag:
|
if fragment_flag:
|
||||||
break
|
break
|
||||||
if fragment_flag:
|
if fragment_flag:
|
||||||
character_bonus_progress = ETO_UNCAP_BONUS_PROGRESS
|
character_bonus_progress = Constant.ETO_UNCAP_BONUS_PROGRESS
|
||||||
step += character_bonus_progress * step_times
|
step += character_bonus_progress * step_times
|
||||||
rewards, steps, curr_position, curr_capture, info = climb_step(
|
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||||
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
|
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
|
||||||
@@ -475,11 +505,26 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
elif skill_special == 'luna_uncap':
|
elif skill_special == 'luna_uncap':
|
||||||
# luna觉醒技能,限制格开始时世界模式进度加7
|
# luna觉醒技能,限制格开始时世界模式进度加7
|
||||||
if 'restrict_id' in steps[0] and 'restrict_type' in steps[0] and steps[0]['restrict_type'] != '' and steps[0]['restrict_id'] != '':
|
if 'restrict_id' in steps[0] and 'restrict_type' in steps[0] and steps[0]['restrict_type'] != '' and steps[0]['restrict_id'] != '':
|
||||||
character_bonus_progress = LUNA_UNCAP_BONUS_PROGRESS
|
character_bonus_progress = Constant.LUNA_UNCAP_BONUS_PROGRESS
|
||||||
step += character_bonus_progress * step_times
|
step += character_bonus_progress * step_times
|
||||||
rewards, steps, curr_position, curr_capture, info = climb_step(
|
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||||
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
|
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
|
||||||
|
|
||||||
|
elif skill_special == 'ayu_uncap':
|
||||||
|
# ayu觉醒技能,世界模式进度+5或-5,但不会小于0
|
||||||
|
if random.random() >= 0.5:
|
||||||
|
character_bonus_progress = Constant.AYU_UNCAP_BONUS_PROGRESS
|
||||||
|
else:
|
||||||
|
character_bonus_progress = -Constant.AYU_UNCAP_BONUS_PROGRESS
|
||||||
|
|
||||||
|
step += character_bonus_progress * step_times
|
||||||
|
if step < 0:
|
||||||
|
character_bonus_progress += step / step_times
|
||||||
|
step = 0
|
||||||
|
|
||||||
|
rewards, steps, curr_position, curr_capture, info = climb_step(
|
||||||
|
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
|
||||||
|
|
||||||
for i in rewards: # 物品分发
|
for i in rewards: # 物品分发
|
||||||
for j in i['items']:
|
for j in i['items']:
|
||||||
amount = j['amount'] if 'amount' in j else 1
|
amount = j['amount'] if 'amount' in j else 1
|
||||||
@@ -498,62 +543,42 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
|
|||||||
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
||||||
(level, exp, user_id, character_id))
|
(level, exp, user_id, character_id))
|
||||||
else:
|
else:
|
||||||
exp = server.character.LEVEL_STEPS[level]
|
exp = Constant.LEVEL_STEPS[level]
|
||||||
|
|
||||||
|
re = {
|
||||||
|
"rewards": rewards,
|
||||||
|
"exp": exp,
|
||||||
|
"level": level,
|
||||||
|
"base_progress": base_step,
|
||||||
|
"progress": step,
|
||||||
|
"user_map": {
|
||||||
|
"user_id": user_id,
|
||||||
|
"curr_position": curr_position,
|
||||||
|
"curr_capture": curr_capture,
|
||||||
|
"is_locked": int2b(y[4]),
|
||||||
|
"map_id": map_id,
|
||||||
|
"prev_capture": y[3],
|
||||||
|
"prev_position": y[2],
|
||||||
|
"beyond_health": info['beyond_health']
|
||||||
|
},
|
||||||
|
"char_stats": {
|
||||||
|
"character_id": character_id,
|
||||||
|
"frag": frag,
|
||||||
|
"prog": prog,
|
||||||
|
"overdrive": overdrive
|
||||||
|
},
|
||||||
|
"current_stamina": calc_stamina(max_stamina_ts, stamina),
|
||||||
|
"max_stamina_ts": max_stamina_ts
|
||||||
|
}
|
||||||
|
|
||||||
if beyond_gauge == 0:
|
if beyond_gauge == 0:
|
||||||
re = {
|
re["user_map"]["steps"] = steps
|
||||||
"rewards": rewards,
|
|
||||||
"exp": exp,
|
|
||||||
"level": level,
|
|
||||||
"base_progress": base_step,
|
|
||||||
"progress": step,
|
|
||||||
"user_map": {
|
|
||||||
"user_id": user_id,
|
|
||||||
"curr_position": curr_position,
|
|
||||||
"curr_capture": curr_capture,
|
|
||||||
"is_locked": int2b(y[4]),
|
|
||||||
"map_id": map_id,
|
|
||||||
"prev_capture": y[3],
|
|
||||||
"prev_position": y[2],
|
|
||||||
"beyond_health": info['beyond_health'],
|
|
||||||
"steps": steps
|
|
||||||
},
|
|
||||||
"char_stats": {
|
|
||||||
"character_id": character_id,
|
|
||||||
"frag": flag,
|
|
||||||
"prog": prog,
|
|
||||||
"overdrive": overdrive
|
|
||||||
},
|
|
||||||
"current_stamina": calc_stamina(max_stamina_ts, stamina),
|
|
||||||
"max_stamina_ts": max_stamina_ts
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
re = {
|
re["user_map"]["steps"] = len(steps)
|
||||||
"rewards": rewards,
|
|
||||||
"exp": exp,
|
if character_id == 35 and not is_skill_sealed:
|
||||||
"level": level,
|
re['char_stats']['prog_tempest'] = prog_tempest
|
||||||
"base_progress": base_step,
|
re['char_stats']['prog'] += prog_tempest
|
||||||
"progress": step,
|
|
||||||
"user_map": {
|
|
||||||
"user_id": user_id,
|
|
||||||
"curr_position": curr_position,
|
|
||||||
"curr_capture": curr_capture,
|
|
||||||
"is_locked": int2b(y[4]),
|
|
||||||
"map_id": map_id,
|
|
||||||
"prev_capture": y[3],
|
|
||||||
"prev_position": y[2],
|
|
||||||
"beyond_health": info['beyond_health'],
|
|
||||||
"step_count": len(steps)
|
|
||||||
},
|
|
||||||
"char_stats": {
|
|
||||||
"character_id": character_id,
|
|
||||||
"frag": flag,
|
|
||||||
"prog": prog,
|
|
||||||
"overdrive": overdrive
|
|
||||||
},
|
|
||||||
"current_stamina": calc_stamina(max_stamina_ts, stamina),
|
|
||||||
"max_stamina_ts": max_stamina_ts
|
|
||||||
}
|
|
||||||
|
|
||||||
if character_bonus_progress is not None:
|
if character_bonus_progress is not None:
|
||||||
re['character_bonus_progress'] = character_bonus_progress
|
re['character_bonus_progress'] = character_bonus_progress
|
||||||
@@ -583,11 +608,11 @@ def add_stamina(c, user_id, add_stamina):
|
|||||||
if x and x[0] is not None and x[1] is not None:
|
if x and x[0] is not None and x[1] is not None:
|
||||||
stamina = calc_stamina(x[0], x[1]) + add_stamina
|
stamina = calc_stamina(x[0], x[1]) + add_stamina
|
||||||
max_stamina_ts = now - \
|
max_stamina_ts = now - \
|
||||||
(stamina-server.info.MAX_STAMINA) * \
|
(stamina-Constant.MAX_STAMINA) * \
|
||||||
server.info.STAMINA_RECOVER_TICK
|
Constant.STAMINA_RECOVER_TICK
|
||||||
else:
|
else:
|
||||||
max_stamina_ts = now
|
max_stamina_ts = now
|
||||||
stamina = server.info.MAX_STAMINA
|
stamina = Constant.MAX_STAMINA
|
||||||
|
|
||||||
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
|
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
|
||||||
(max_stamina_ts, stamina, user_id))
|
(max_stamina_ts, stamina, user_id))
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ BAN_TIME = [1, 3, 7, 15, 31]
|
|||||||
|
|
||||||
|
|
||||||
def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
|
def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判断
|
||||||
# 查询数据库中的user表,验证账号密码,返回并记录token,多返回个error code和extra
|
# 查询数据库中的user表,验证账号密码,返回并记录token和user_id,多返回个error code和extra
|
||||||
# token采用user_id和时间戳连接后hash生成(真的是瞎想的,没用bear)
|
# token采用user_id和时间戳连接后hash生成(真的是瞎想的,没用bear)
|
||||||
# 密码和token的加密方式为 SHA-256
|
# 密码和token的加密方式为 SHA-256
|
||||||
|
|
||||||
error_code = 108
|
error_code = 108
|
||||||
token = None
|
token = None
|
||||||
|
user_id = None
|
||||||
with Connect() as c:
|
with Connect() as c:
|
||||||
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
|
||||||
c.execute('''select user_id, password, ban_flag from user where name = :name''', {
|
c.execute('''select user_id, password, ban_flag from user where name = :name''', {
|
||||||
@@ -61,7 +62,7 @@ def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判
|
|||||||
'''select count(*) from login where user_id=? and login_time>?''', (user_id, now-86400000))
|
'''select count(*) from login where user_id=? and login_time>?''', (user_id, now-86400000))
|
||||||
if c.fetchone()[0] >= Config.LOGIN_DEVICE_NUMBER_LIMIT:
|
if c.fetchone()[0] >= Config.LOGIN_DEVICE_NUMBER_LIMIT:
|
||||||
remaining_ts = arc_auto_ban(c, user_id, now)
|
remaining_ts = arc_auto_ban(c, user_id, now)
|
||||||
return None, 105, {'remaining_ts': remaining_ts}
|
return None, None, 105, {'remaining_ts': remaining_ts}
|
||||||
|
|
||||||
c.execute('''delete from login where rowid in (select rowid from login where user_id=:user_id limit :a);''',
|
c.execute('''delete from login where rowid in (select rowid from login where user_id=:user_id limit :a);''',
|
||||||
{'user_id': user_id, 'a': int(should_delete_num)})
|
{'user_id': user_id, 'a': int(should_delete_num)})
|
||||||
@@ -76,7 +77,7 @@ def arc_login(name: str, password: str, device_id: str, ip: str): # 登录判
|
|||||||
# 用户名错误
|
# 用户名错误
|
||||||
error_code = 104
|
error_code = 104
|
||||||
|
|
||||||
return token, error_code, None
|
return token, user_id, error_code, None
|
||||||
|
|
||||||
|
|
||||||
def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册
|
def arc_register(name: str, password: str, device_id: str, email: str, ip: str): # 注册
|
||||||
@@ -195,7 +196,7 @@ def auth_required(request):
|
|||||||
if 'AppVersion' in headers: # 版本检查
|
if 'AppVersion' in headers: # 版本检查
|
||||||
if Config.ALLOW_APPVERSION:
|
if Config.ALLOW_APPVERSION:
|
||||||
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
if headers['AppVersion'] not in Config.ALLOW_APPVERSION:
|
||||||
return jsonify({"success": False, "error_code": 5})
|
return jsonify({"success": False, "error_code": 1203})
|
||||||
|
|
||||||
if 'Authorization' in headers:
|
if 'Authorization' in headers:
|
||||||
token = headers['Authorization']
|
token = headers['Authorization']
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
from setting import Config
|
from setting import Config
|
||||||
from server.sql import Connect
|
from server.sql import Connect
|
||||||
|
from .config import Constant
|
||||||
import server.info
|
import server.info
|
||||||
import server.item
|
import server.item
|
||||||
import server.setme
|
import server.setme
|
||||||
|
|
||||||
LEVEL_STEPS = {1: 0, 2: 50, 3: 100, 4: 150, 5: 200, 6: 300, 7: 450, 8: 650, 9: 900, 10: 1200, 11: 1600, 12: 2100, 13: 2700, 14: 3400, 15: 4200, 16: 5100,
|
|
||||||
17: 6100, 18: 7200, 19: 8500, 20: 10000, 21: 11500, 22: 13000, 23: 14500, 24: 16000, 25: 17500, 26: 19000, 27: 20500, 28: 22000, 29: 23500, 30: 25000}
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
def int2b(x):
|
||||||
# int与布尔值转换
|
# int与布尔值转换
|
||||||
@@ -18,7 +16,7 @@ def int2b(x):
|
|||||||
|
|
||||||
def get_level_steps():
|
def get_level_steps():
|
||||||
# 返回level_steps字典数组
|
# 返回level_steps字典数组
|
||||||
return [{'level': i, 'level_exp': LEVEL_STEPS[i]} for i in LEVEL_STEPS]
|
return [{'level': i, 'level_exp': Constant.LEVEL_STEPS[i]} for i in Constant.LEVEL_STEPS]
|
||||||
|
|
||||||
|
|
||||||
def calc_char_value(level, value1, value20, value30):
|
def calc_char_value(level, value1, value20, value30):
|
||||||
@@ -114,7 +112,7 @@ def get_user_character(c, user_id):
|
|||||||
"overdrive": calc_char_value(i[2], i[11], i[14], i[17]),
|
"overdrive": calc_char_value(i[2], i[11], i[14], i[17]),
|
||||||
"prog": calc_char_value(i[2], i[10], i[13], i[16]),
|
"prog": calc_char_value(i[2], i[10], i[13], i[16]),
|
||||||
"frag": calc_char_value(i[2], i[9], i[12], i[15]),
|
"frag": calc_char_value(i[2], i[9], i[12], i[15]),
|
||||||
"level_exp": LEVEL_STEPS[i[2]],
|
"level_exp": Constant.LEVEL_STEPS[i[2]],
|
||||||
"exp": i[3],
|
"exp": i[3],
|
||||||
"level": i[2],
|
"level": i[2],
|
||||||
"name": i[7],
|
"name": i[7],
|
||||||
@@ -151,7 +149,7 @@ def get_one_character(c, user_id, character_id):
|
|||||||
"overdrive": calc_char_value(x[2], x[11], x[14], x[17]),
|
"overdrive": calc_char_value(x[2], x[11], x[14], x[17]),
|
||||||
"prog": calc_char_value(x[2], x[10], x[13], x[16]),
|
"prog": calc_char_value(x[2], x[10], x[13], x[16]),
|
||||||
"frag": calc_char_value(x[2], x[9], x[12], x[15]),
|
"frag": calc_char_value(x[2], x[9], x[12], x[15]),
|
||||||
"level_exp": LEVEL_STEPS[x[2]],
|
"level_exp": Constant.LEVEL_STEPS[x[2]],
|
||||||
"exp": x[3],
|
"exp": x[3],
|
||||||
"level": x[2],
|
"level": x[2],
|
||||||
"name": x[7],
|
"name": x[7],
|
||||||
@@ -168,18 +166,18 @@ def calc_level_up(c, user_id, character_id, exp, exp_addition):
|
|||||||
|
|
||||||
exp += exp_addition
|
exp += exp_addition
|
||||||
|
|
||||||
if exp >= LEVEL_STEPS[20]: # 未觉醒溢出
|
if exp >= Constant.LEVEL_STEPS[20]: # 未觉醒溢出
|
||||||
c.execute('''select is_uncapped from user_char where user_id=? and character_id=?''',
|
c.execute('''select is_uncapped from user_char where user_id=? and character_id=?''',
|
||||||
(user_id, character_id))
|
(user_id, character_id))
|
||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
if x and x[0] == 0:
|
if x and x[0] == 0:
|
||||||
return LEVEL_STEPS[20], 20
|
return Constant.LEVEL_STEPS[20], 20
|
||||||
|
|
||||||
a = []
|
a = []
|
||||||
b = []
|
b = []
|
||||||
for i in LEVEL_STEPS:
|
for i in Constant.LEVEL_STEPS:
|
||||||
a.append(i)
|
a.append(i)
|
||||||
b.append(LEVEL_STEPS[i])
|
b.append(Constant.LEVEL_STEPS[i])
|
||||||
|
|
||||||
if exp >= b[-1]: # 溢出
|
if exp >= b[-1]: # 溢出
|
||||||
return b[-1], a[-1]
|
return b[-1], a[-1]
|
||||||
@@ -213,7 +211,7 @@ def char_use_core(user_id, character_id, amount):
|
|||||||
x = c.fetchone()
|
x = c.fetchone()
|
||||||
if x:
|
if x:
|
||||||
exp, level = calc_level_up(
|
exp, level = calc_level_up(
|
||||||
c, user_id, character_id, x[0], amount*server.info.CORE_EXP)
|
c, user_id, character_id, x[0], amount*Config.CORE_EXP)
|
||||||
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
c.execute('''update user_char set level=?, exp=? where user_id=? and character_id=?''',
|
||||||
(level, exp, user_id, character_id))
|
(level, exp, user_id, character_id))
|
||||||
server.item.claim_user_item(
|
server.item.claim_user_item(
|
||||||
|
|||||||
1140
latest version/server/config.py
Normal file
1140
latest version/server/config.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,7 @@ import server.character
|
|||||||
import server.item
|
import server.item
|
||||||
import time
|
import time
|
||||||
from setting import Config
|
from setting import Config
|
||||||
|
from .config import Constant
|
||||||
MAX_STAMINA = 12
|
|
||||||
STAMINA_RECOVER_TICK = 1800000
|
|
||||||
CORE_EXP = 250
|
|
||||||
|
|
||||||
|
|
||||||
def int2b(x):
|
def int2b(x):
|
||||||
@@ -202,6 +199,38 @@ def get_user_me(c, user_id):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_me_c(user_id):
|
||||||
|
# user/me调用,上边没开数据库这里开一下
|
||||||
|
with Connect() as c:
|
||||||
|
return get_user_me(c, user_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_purchase_pack(user_id):
|
||||||
|
# 返回曲包数据
|
||||||
|
with Connect() as c:
|
||||||
|
return server.arcpurchase.get_purchase(c, 'pack')
|
||||||
|
|
||||||
|
|
||||||
|
def get_game_info():
|
||||||
|
# 返回游戏基本信息
|
||||||
|
r = {
|
||||||
|
"max_stamina": Constant.MAX_STAMINA,
|
||||||
|
"stamina_recover_tick": Constant.STAMINA_RECOVER_TICK,
|
||||||
|
"core_exp": Constant.CORE_EXP,
|
||||||
|
"curr_ts": int(time.time()*1000),
|
||||||
|
"level_steps": server.character.get_level_steps(),
|
||||||
|
"world_ranking_enabled": True,
|
||||||
|
"is_byd_chapter_unlocked": True
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_present(user_id):
|
||||||
|
# 返回奖励信息
|
||||||
|
with Connect() as c:
|
||||||
|
return server.arcpurchase.get_user_present(c, user_id)
|
||||||
|
|
||||||
|
|
||||||
def arc_aggregate_small(user_id):
|
def arc_aggregate_small(user_id):
|
||||||
# 返回用户数据
|
# 返回用户数据
|
||||||
r = {"success": False}
|
r = {"success": False}
|
||||||
@@ -239,9 +268,9 @@ def arc_aggregate_big(user_id):
|
|||||||
}, {
|
}, {
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"value": {
|
"value": {
|
||||||
"max_stamina": MAX_STAMINA,
|
"max_stamina": Constant.MAX_STAMINA,
|
||||||
"stamina_recover_tick": STAMINA_RECOVER_TICK,
|
"stamina_recover_tick": Constant.STAMINA_RECOVER_TICK,
|
||||||
"core_exp": CORE_EXP,
|
"core_exp": Constant.CORE_EXP,
|
||||||
"curr_ts": int(time.time()*1000),
|
"curr_ts": int(time.time()*1000),
|
||||||
"level_steps": server.character.get_level_steps(),
|
"level_steps": server.character.get_level_steps(),
|
||||||
"world_ranking_enabled": True,
|
"world_ranking_enabled": True,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from server.sql import Connect
|
from server.sql import Connect
|
||||||
from setting import Config
|
from setting import Config
|
||||||
|
from .config import Constant
|
||||||
import server.info
|
import server.info
|
||||||
import server.character
|
import server.character
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ def change_char_uncap(user_id, character_id):
|
|||||||
"overdrive": server.character.calc_char_value(y[2], y[11], y[14], y[17]),
|
"overdrive": server.character.calc_char_value(y[2], y[11], y[14], y[17]),
|
||||||
"prog": server.character.calc_char_value(y[2], y[10], y[13], y[16]),
|
"prog": server.character.calc_char_value(y[2], y[10], y[13], y[16]),
|
||||||
"frag": server.character.calc_char_value(y[2], y[9], y[12], y[15]),
|
"frag": server.character.calc_char_value(y[2], y[9], y[12], y[15]),
|
||||||
"level_exp": server.character.LEVEL_STEPS[y[2]],
|
"level_exp": Constant.LEVEL_STEPS[y[2]],
|
||||||
"exp": y[3],
|
"exp": y[3],
|
||||||
"level": y[2],
|
"level": y[2],
|
||||||
"name": y[7],
|
"name": y[7],
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Config():
|
|||||||
游戏API地址前缀
|
游戏API地址前缀
|
||||||
Game API's URL prefix
|
Game API's URL prefix
|
||||||
'''
|
'''
|
||||||
GAME_API_PREFIX = '/earlgrey/16'
|
GAME_API_PREFIX = '/bridge/18'
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
@@ -30,8 +30,29 @@ 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.5.3', '3.5.3c',
|
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.12.0', '3.12.0c']
|
||||||
'3.9.0', '3.9.0c', '3.9.1', '3.9.1c']
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
联机功能的端口号,若为空,则默认不开启联机功能
|
||||||
|
Port of your link play server
|
||||||
|
If it is blank, link play will be unavailable.
|
||||||
|
'''
|
||||||
|
UDP_PORT = '10900'
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
--------------------
|
||||||
|
联机功能地址,留空则自动获取
|
||||||
|
Link Play address
|
||||||
|
If left blank, it will be obtained automatically.
|
||||||
|
'''
|
||||||
|
LINK_PLAY_HOST = '' # ***.com
|
||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
'''
|
'''
|
||||||
@@ -215,7 +236,7 @@ class Config():
|
|||||||
'''
|
'''
|
||||||
--------------------
|
--------------------
|
||||||
是否全解锁世界场景
|
是否全解锁世界场景
|
||||||
If unlocking all world scenerys is enabled
|
If unlocking all world sceneries is enabled
|
||||||
'''
|
'''
|
||||||
WORLD_SCENERY_FULL_UNLOCK = True
|
WORLD_SCENERY_FULL_UNLOCK = True
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -20,4 +20,4 @@
|
|||||||
{% endfor %} {% block content %}{% endblock %}
|
{% endfor %} {% block content %}{% endblock %}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer id="footer" class="footer">Made by Lost@2020-2021</footer>
|
<footer id="footer" class="footer">Made by Lost@2020-2022</footer>
|
||||||
0
latest version/udpserver/__init__.py
Normal file
0
latest version/udpserver/__init__.py
Normal file
24
latest version/udpserver/aes.py
Normal file
24
latest version/udpserver/aes.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import os
|
||||||
|
from cryptography.hazmat.primitives.ciphers import (
|
||||||
|
Cipher, algorithms, modes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt(key, plaintext, associated_data):
|
||||||
|
iv = os.urandom(12)
|
||||||
|
encryptor = Cipher(
|
||||||
|
algorithms.AES(key),
|
||||||
|
modes.GCM(iv, min_tag_length=12),
|
||||||
|
).encryptor()
|
||||||
|
encryptor.authenticate_additional_data(associated_data)
|
||||||
|
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
|
||||||
|
return (iv, ciphertext, encryptor.tag)
|
||||||
|
|
||||||
|
|
||||||
|
def decrypt(key, associated_data, iv, ciphertext, tag):
|
||||||
|
decryptor = Cipher(
|
||||||
|
algorithms.AES(key),
|
||||||
|
modes.GCM(iv, tag, min_tag_length=12),
|
||||||
|
).decryptor()
|
||||||
|
decryptor.authenticate_additional_data(associated_data)
|
||||||
|
return decryptor.update(ciphertext) + decryptor.finalize()
|
||||||
174
latest version/udpserver/udp_class.py
Normal file
174
latest version/udpserver/udp_class.py
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
def b(value, length=1):
|
||||||
|
return value.to_bytes(length=length, byteorder='little')
|
||||||
|
|
||||||
|
|
||||||
|
def bi(value):
|
||||||
|
return int.from_bytes(value, byteorder='little')
|
||||||
|
|
||||||
|
|
||||||
|
class Player:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.player_id = 0
|
||||||
|
self.player_name = b'\x45\x6d\x70\x74\x79\x50\x6c\x61\x79\x65\x72\x00\x00\x00\x00\x00'
|
||||||
|
self.token = 0
|
||||||
|
|
||||||
|
self.character_id = 0xff
|
||||||
|
self.last_character_id = 0xff
|
||||||
|
self.is_uncapped = 0
|
||||||
|
|
||||||
|
self.difficulty = 0xff
|
||||||
|
self.last_difficulty = 0xff
|
||||||
|
self.score = 0
|
||||||
|
self.last_score = 0
|
||||||
|
self.timer = 0
|
||||||
|
self.last_timer = 0
|
||||||
|
self.cleartype = 0
|
||||||
|
self.last_cleartype = 0
|
||||||
|
self.best_score_flag = 0
|
||||||
|
self.best_player_flag = 0
|
||||||
|
self.finish_flag = 0
|
||||||
|
|
||||||
|
self.player_state = 1
|
||||||
|
self.download_percent = 0
|
||||||
|
self.online = 0
|
||||||
|
|
||||||
|
self.last_timestamp = 0
|
||||||
|
self.extra_command_queue = []
|
||||||
|
|
||||||
|
self.song_unlock = b'\x00' * 512
|
||||||
|
|
||||||
|
self.start_command_num = 0
|
||||||
|
|
||||||
|
def set_player_name(self, player_name: str):
|
||||||
|
self.player_name = player_name.encode('ascii')
|
||||||
|
if len(self.player_name) > 16:
|
||||||
|
self.player_name = self.player_name[:16]
|
||||||
|
else:
|
||||||
|
self.player_name += b'\x00' * (16 - len(self.player_name))
|
||||||
|
|
||||||
|
|
||||||
|
class Room:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.room_id = 0
|
||||||
|
self.room_code = 'AAAA00'
|
||||||
|
|
||||||
|
self.countdown = 0xffffffff
|
||||||
|
self.timestamp = 0
|
||||||
|
self.state = 0
|
||||||
|
self.song_idx = 0xffff
|
||||||
|
self.last_song_idx = 0xffff
|
||||||
|
|
||||||
|
self.song_unlock = b'\x00' * 512
|
||||||
|
|
||||||
|
self.host_id = 0
|
||||||
|
self.players = [Player(), Player(), Player(), Player()]
|
||||||
|
self.player_num = 0
|
||||||
|
|
||||||
|
self.interval = 1000
|
||||||
|
self.times = 100
|
||||||
|
|
||||||
|
self.round_switch = 0
|
||||||
|
|
||||||
|
self.command_queue = []
|
||||||
|
self.command_queue_length = 0
|
||||||
|
|
||||||
|
def get_players_info(self):
|
||||||
|
# 获取所有玩家信息
|
||||||
|
re = b''
|
||||||
|
for i in self.players:
|
||||||
|
re += b(i.player_id, 8) + b(i.character_id) + b(i.is_uncapped) + b(i.difficulty) + b(i.score, 4) + \
|
||||||
|
b(i.timer, 4) + b(i.cleartype) + b(i.player_state) + \
|
||||||
|
b(i.download_percent) + b(i.online) + b'\x00' + i.player_name
|
||||||
|
return re
|
||||||
|
|
||||||
|
def get_player_last_score(self):
|
||||||
|
# 获取上次曲目玩家分数,返回bytes
|
||||||
|
if self.last_song_idx == 0xffff:
|
||||||
|
return b'\xff\xff\x00\x00\x00\x00\x00\x00\x00' * 4
|
||||||
|
re = b''
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
player = self.players[i]
|
||||||
|
|
||||||
|
if player.player_id != 0:
|
||||||
|
re += b(player.last_character_id) + b(player.last_difficulty) + b(player.last_score, 4) + b(
|
||||||
|
player.last_cleartype) + b(player.best_score_flag) + b(player.best_player_flag)
|
||||||
|
else:
|
||||||
|
re += b'\xff\xff\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
|
||||||
|
return re
|
||||||
|
|
||||||
|
def make_round(self):
|
||||||
|
# 轮换房主
|
||||||
|
for i in range(4):
|
||||||
|
if self.players[i].player_id == self.host_id:
|
||||||
|
for j in range(1, 4):
|
||||||
|
if self.players[(i + j) % 4].player_id != 0:
|
||||||
|
self.host_id = self.players[(i + j) % 4].player_id
|
||||||
|
break
|
||||||
|
break
|
||||||
|
|
||||||
|
def delete_player(self, player_index: int):
|
||||||
|
# 删除某个玩家
|
||||||
|
self.player_num -= 1
|
||||||
|
if self.players[player_index].player_id == self.host_id:
|
||||||
|
self.make_round()
|
||||||
|
|
||||||
|
self.players[player_index].online = 0
|
||||||
|
self.players[player_index] = Player()
|
||||||
|
self.update_song_unlock()
|
||||||
|
|
||||||
|
def update_song_unlock(self):
|
||||||
|
# 更新房间可用歌曲
|
||||||
|
r = bi(b'\xff' * 512)
|
||||||
|
for i in self.players:
|
||||||
|
if i.player_id != 0:
|
||||||
|
r &= bi(i.song_unlock)
|
||||||
|
|
||||||
|
self.song_unlock = b(r, 512)
|
||||||
|
|
||||||
|
def is_ready(self, old_state: int, player_state: int):
|
||||||
|
# 是否全部准备就绪
|
||||||
|
if self.state == old_state:
|
||||||
|
for i in self.players:
|
||||||
|
if i.player_id != 0 and (i.player_state != player_state or i.online == 0):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_finish(self):
|
||||||
|
# 是否全部进入结算
|
||||||
|
for i in self.players:
|
||||||
|
if i.player_id != 0 and (i.finish_flag == 0 or i.online == 0):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def make_finish(self):
|
||||||
|
# 结算
|
||||||
|
self.state = 8
|
||||||
|
self.last_song_idx = self.song_idx
|
||||||
|
|
||||||
|
max_score = 0
|
||||||
|
max_score_i = []
|
||||||
|
for i in range(4):
|
||||||
|
player = self.players[i]
|
||||||
|
if player.player_id != 0:
|
||||||
|
player.finish_flag = 0
|
||||||
|
player.last_timer = player.timer
|
||||||
|
player.last_score = player.score
|
||||||
|
player.last_cleartype = player.cleartype
|
||||||
|
player.last_character_id = player.character_id
|
||||||
|
player.last_difficulty = player.difficulty
|
||||||
|
player.best_player_flag = 0
|
||||||
|
|
||||||
|
if player.last_score > max_score:
|
||||||
|
max_score = player.last_score
|
||||||
|
max_score_i = [i]
|
||||||
|
elif player.last_score == max_score:
|
||||||
|
max_score_i.append(i)
|
||||||
|
|
||||||
|
for i in max_score_i:
|
||||||
|
self.players[i].best_player_flag = 1
|
||||||
8
latest version/udpserver/udp_config.py
Normal file
8
latest version/udpserver/udp_config.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class Config:
|
||||||
|
TIME_LIMIT = 3600000
|
||||||
|
|
||||||
|
COMMAND_INTERVAL = 1000000
|
||||||
|
|
||||||
|
COUNTDOWM_TIME = 3999
|
||||||
|
|
||||||
|
PLAYER_TIMEOUT = 30000000
|
||||||
223
latest version/udpserver/udp_main.py
Normal file
223
latest version/udpserver/udp_main.py
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
import socketserver
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
# import binascii
|
||||||
|
from . import aes
|
||||||
|
from .udp_parser import CommandParser
|
||||||
|
from .udp_class import Room, Player, bi
|
||||||
|
from .udp_config import Config
|
||||||
|
|
||||||
|
|
||||||
|
# token: {'key': key, 'room': Room, 'player_index': player_index, 'player_id': player_id}
|
||||||
|
link_play_data = {}
|
||||||
|
room_id_dict = {} # 'room_id': Room
|
||||||
|
room_code_dict = {} # 'room_code': Room
|
||||||
|
player_dict = {} # 'player_id' : Player
|
||||||
|
clean_timer = 0
|
||||||
|
|
||||||
|
|
||||||
|
def random_room_code():
|
||||||
|
# 随机生成房间号
|
||||||
|
re = ''
|
||||||
|
for _ in range(4):
|
||||||
|
re += chr(random.randint(65, 90))
|
||||||
|
for _ in range(2):
|
||||||
|
re += str(random.randint(0, 9))
|
||||||
|
|
||||||
|
return re
|
||||||
|
|
||||||
|
|
||||||
|
def clear_player(token):
|
||||||
|
# 清除玩家信息和token
|
||||||
|
del player_dict[link_play_data[token]['player_id']]
|
||||||
|
del link_play_data[token]
|
||||||
|
|
||||||
|
|
||||||
|
def clear_room(room):
|
||||||
|
# 清除房间信息
|
||||||
|
room_id = room.room_id
|
||||||
|
room_code = room.room_code
|
||||||
|
del room_id_dict[room_id]
|
||||||
|
del room_code_dict[room_code]
|
||||||
|
del room
|
||||||
|
|
||||||
|
|
||||||
|
def memory_clean(now):
|
||||||
|
# 内存清理
|
||||||
|
clean_room_list = []
|
||||||
|
clean_player_list = []
|
||||||
|
for token in link_play_data:
|
||||||
|
room = link_play_data[token]['room']
|
||||||
|
if now - room.timestamp >= Config.TIME_LIMIT:
|
||||||
|
clean_room_list.append(room.room_id)
|
||||||
|
|
||||||
|
if now - room.players[link_play_data[token]['player_index']].last_timestamp // 1000 >= Config.TIME_LIMIT:
|
||||||
|
clean_player_list.append(token)
|
||||||
|
|
||||||
|
for room_id in room_id_dict:
|
||||||
|
if now - room_id_dict[room_id].timestamp >= Config.TIME_LIMIT:
|
||||||
|
clean_room_list.append(room_id)
|
||||||
|
|
||||||
|
for room_id in clean_room_list:
|
||||||
|
if room_id in room_id_dict:
|
||||||
|
clear_room(room_id_dict[room_id])
|
||||||
|
|
||||||
|
for token in clean_player_list:
|
||||||
|
clear_player(token)
|
||||||
|
|
||||||
|
|
||||||
|
class UDPhandler(socketserver.BaseRequestHandler):
|
||||||
|
def handle(self):
|
||||||
|
client_msg, server = self.request
|
||||||
|
token = client_msg[:8]
|
||||||
|
iv = client_msg[8:20]
|
||||||
|
tag = client_msg[20:32]
|
||||||
|
ciphertext = client_msg[32:]
|
||||||
|
if int.from_bytes(token, byteorder='little') in link_play_data:
|
||||||
|
user = link_play_data[bi(token)]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
plaintext = aes.decrypt(user['key'], b'', iv, ciphertext, tag)
|
||||||
|
# print(binascii.b2a_hex(plaintext))
|
||||||
|
|
||||||
|
commands = CommandParser(
|
||||||
|
user['room'], user['player_index']).get_commands(plaintext)
|
||||||
|
|
||||||
|
if user['room'].players[user['player_index']].player_id == 0:
|
||||||
|
clear_player(bi(token))
|
||||||
|
temp = []
|
||||||
|
for i in commands:
|
||||||
|
if i[:3] == b'\x06\x16\x12':
|
||||||
|
temp.append(i)
|
||||||
|
commands = temp
|
||||||
|
# 处理不能正确被踢的问题
|
||||||
|
|
||||||
|
for i in commands:
|
||||||
|
iv, ciphertext, tag = aes.encrypt(user['key'], i, b'')
|
||||||
|
# print(binascii.b2a_hex(i))
|
||||||
|
|
||||||
|
server.sendto(token + iv + tag[:12] +
|
||||||
|
ciphertext, self.client_address)
|
||||||
|
|
||||||
|
|
||||||
|
def server_run(ip, port):
|
||||||
|
server = socketserver.ThreadingUDPServer((ip, port), UDPhandler)
|
||||||
|
server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
def data_swap(conn):
|
||||||
|
clean_timer = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data = conn.recv()
|
||||||
|
except EOFError:
|
||||||
|
break
|
||||||
|
|
||||||
|
now = round(time.time() * 1000)
|
||||||
|
if now - clean_timer >= Config.TIME_LIMIT:
|
||||||
|
clean_timer = now
|
||||||
|
memory_clean(now)
|
||||||
|
|
||||||
|
if data[0] == 1:
|
||||||
|
# 开房
|
||||||
|
key = os.urandom(16)
|
||||||
|
room_id = bi(os.urandom(8))
|
||||||
|
while room_id in room_id_dict and room_id == 0:
|
||||||
|
room_id = bi(os.urandom(8))
|
||||||
|
room = Room()
|
||||||
|
room.room_id = room_id
|
||||||
|
room_id_dict[room_id] = room
|
||||||
|
|
||||||
|
player_id = bi(os.urandom(3))
|
||||||
|
while player_id in player_dict and player_id == 0:
|
||||||
|
player_id = bi(os.urandom(3))
|
||||||
|
player = Player()
|
||||||
|
player.player_id = player_id
|
||||||
|
player.set_player_name(data[1])
|
||||||
|
player_dict[player_id] = player
|
||||||
|
|
||||||
|
player.song_unlock = data[2]
|
||||||
|
room.song_unlock = data[2]
|
||||||
|
room.host_id = player_id
|
||||||
|
room.players[0] = player
|
||||||
|
room.player_num = 1
|
||||||
|
|
||||||
|
room_code = random_room_code()
|
||||||
|
while room_code in room_code_dict:
|
||||||
|
room_code = random_room_code()
|
||||||
|
room.room_code = room_code
|
||||||
|
room_code_dict[room_code] = room
|
||||||
|
|
||||||
|
token = room_id
|
||||||
|
player.token = token
|
||||||
|
|
||||||
|
link_play_data[token] = {'key': key,
|
||||||
|
'room': room,
|
||||||
|
'player_index': 0,
|
||||||
|
'player_id': player_id}
|
||||||
|
|
||||||
|
conn.send((0, room_code, room_id, token, key, player_id))
|
||||||
|
|
||||||
|
elif data[0] == 2:
|
||||||
|
room_code = data[3].upper()
|
||||||
|
if room_code not in room_code_dict:
|
||||||
|
# 房间号错误
|
||||||
|
conn.send((1202, ))
|
||||||
|
else:
|
||||||
|
room = room_code_dict[room_code]
|
||||||
|
if room.player_num == 4:
|
||||||
|
# 满人
|
||||||
|
conn.send((1201, ))
|
||||||
|
elif room.state != 2:
|
||||||
|
# 无法加入
|
||||||
|
conn.send((1205, ))
|
||||||
|
else:
|
||||||
|
key = os.urandom(16)
|
||||||
|
token = bi(os.urandom(8))
|
||||||
|
|
||||||
|
player_id = bi(os.urandom(3))
|
||||||
|
while player_id in player_dict and player_id == 0:
|
||||||
|
player_id = bi(os.urandom(3))
|
||||||
|
player = Player()
|
||||||
|
player.player_id = player_id
|
||||||
|
player.set_player_name(data[1])
|
||||||
|
player.token = token
|
||||||
|
player_dict[player_id] = player
|
||||||
|
|
||||||
|
player.song_unlock = data[2]
|
||||||
|
|
||||||
|
room.update_song_unlock()
|
||||||
|
for i in range(4):
|
||||||
|
if room.players[i].player_id == 0:
|
||||||
|
room.players[i] = player
|
||||||
|
player_index = i
|
||||||
|
break
|
||||||
|
|
||||||
|
link_play_data[token] = {'key': key,
|
||||||
|
'room': room,
|
||||||
|
'player_index': player_index,
|
||||||
|
'player_id': player_id}
|
||||||
|
|
||||||
|
conn.send((0, room_code, room.room_id,
|
||||||
|
token, key, player_id, room.song_unlock))
|
||||||
|
elif data[0] == 3:
|
||||||
|
token = data[1]
|
||||||
|
if token in link_play_data:
|
||||||
|
r = link_play_data[token]
|
||||||
|
conn.send((0, r['room'].room_code, r['room'].room_id, r['key'],
|
||||||
|
r['room'].players[r['player_index']].player_id, r['room'].song_unlock))
|
||||||
|
else:
|
||||||
|
conn.send((108, ))
|
||||||
|
|
||||||
|
|
||||||
|
def link_play(conn, ip: str, port: int):
|
||||||
|
try:
|
||||||
|
server = threading.Thread(target=server_run, args=(ip, port))
|
||||||
|
data_exchange = threading.Thread(target=data_swap, args=(conn,))
|
||||||
|
server.start()
|
||||||
|
data_exchange.start()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
333
latest version/udpserver/udp_parser.py
Normal file
333
latest version/udpserver/udp_parser.py
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
from operator import irshift
|
||||||
|
from .udp_sender import CommandSender
|
||||||
|
from .udp_class import bi, Room
|
||||||
|
from .udp_config import Config
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class CommandParser:
|
||||||
|
def __init__(self, room: Room, player_index: int = 0) -> None:
|
||||||
|
self.room = room
|
||||||
|
self.player_index = player_index
|
||||||
|
|
||||||
|
def get_commands(self, command):
|
||||||
|
self.command = command
|
||||||
|
l = {b'\x06\x16\x01': self.command_01,
|
||||||
|
b'\x06\x16\x02': self.command_02,
|
||||||
|
b'\x06\x16\x03': self.command_03,
|
||||||
|
b'\x06\x16\x04': self.command_04,
|
||||||
|
b'\x06\x16\x05': self.command_05,
|
||||||
|
b'\x06\x16\x06': self.command_06,
|
||||||
|
b'\x06\x16\x07': self.command_07,
|
||||||
|
b'\x06\x16\x08': self.command_08,
|
||||||
|
b'\x06\x16\x09': self.command_09,
|
||||||
|
b'\x06\x16\x0a': self.command_0a,
|
||||||
|
b'\x06\x16\x0b': self.command_0b
|
||||||
|
}
|
||||||
|
r = l[command[:3]]()
|
||||||
|
|
||||||
|
re = []
|
||||||
|
|
||||||
|
flag_13 = False
|
||||||
|
for i in range(max(bi(self.command[12:16]), self.room.players[self.player_index].start_command_num), self.room.command_queue_length):
|
||||||
|
if self.room.command_queue[i][:3] == b'\x06\x16\x13':
|
||||||
|
if flag_13:
|
||||||
|
break
|
||||||
|
flag_13 = True
|
||||||
|
re.append(self.room.command_queue[i])
|
||||||
|
|
||||||
|
if self.room.players[self.player_index].extra_command_queue:
|
||||||
|
re += self.room.players[self.player_index].extra_command_queue
|
||||||
|
self.room.players[self.player_index].extra_command_queue = []
|
||||||
|
|
||||||
|
if r:
|
||||||
|
re += r
|
||||||
|
|
||||||
|
return re
|
||||||
|
|
||||||
|
def command_01(self):
|
||||||
|
# 给房主
|
||||||
|
player_id = bi(self.command[24:32])
|
||||||
|
for i in self.room.players:
|
||||||
|
if i.player_id == player_id and i.online == 1:
|
||||||
|
self.room.host_id = player_id
|
||||||
|
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_10())
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def command_02(self):
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
song_idx = bi(self.command[24:26])
|
||||||
|
|
||||||
|
flag = 2
|
||||||
|
if self.room.state == 2:
|
||||||
|
flag = 0
|
||||||
|
self.room.state = 3
|
||||||
|
self.room.song_idx = song_idx
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_11())
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_13())
|
||||||
|
|
||||||
|
return [x.command_0d(flag)]
|
||||||
|
|
||||||
|
def command_03(self):
|
||||||
|
# 尝试进入结算
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
player = self.room.players[self.player_index]
|
||||||
|
player.score = bi(self.command[24:28])
|
||||||
|
player.cleartype = self.command[28]
|
||||||
|
player.difficulty = self.command[29]
|
||||||
|
player.best_score_flag = self.command[30]
|
||||||
|
player.finish_flag = 1
|
||||||
|
player.last_timestamp -= Config.COMMAND_INTERVAL
|
||||||
|
self.room.last_song_idx = self.room.song_idx
|
||||||
|
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_12(self.player_index))
|
||||||
|
|
||||||
|
if self.room.is_finish():
|
||||||
|
self.room.make_finish()
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_13())
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def command_04(self):
|
||||||
|
# 踢人
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
player_id = bi(self.command[24:32])
|
||||||
|
flag = 2
|
||||||
|
if self.room.players[self.player_index].player_id == self.room.host_id and player_id != self.room.host_id:
|
||||||
|
for i in range(4):
|
||||||
|
if self.room.players[i].player_id == player_id:
|
||||||
|
flag = 1
|
||||||
|
self.room.delete_player(i)
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_12(i))
|
||||||
|
self.room.update_song_unlock()
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_14())
|
||||||
|
break
|
||||||
|
|
||||||
|
return [x.command_0d(flag)]
|
||||||
|
|
||||||
|
def command_05(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def command_06(self):
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
self.room.state = 1
|
||||||
|
self.room.song_idx = 0xffff
|
||||||
|
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_13())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def command_07(self):
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
self.room.players[self.player_index].song_unlock = self.command[24:536]
|
||||||
|
self.room.update_song_unlock()
|
||||||
|
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_14())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def command_08(self):
|
||||||
|
self.room.round_switch = bi(self.command[24:25])
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_13())
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def command_09(self):
|
||||||
|
re = []
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
x.random_code = self.command[16:24]
|
||||||
|
player = self.room.players[self.player_index]
|
||||||
|
|
||||||
|
if bi(self.command[12:16]) == 0:
|
||||||
|
player.online = 1
|
||||||
|
self.room.state = 1
|
||||||
|
self.room.update_song_unlock()
|
||||||
|
player.start_command_num = self.room.command_queue_length
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_15())
|
||||||
|
else:
|
||||||
|
if x.timestamp - player.last_timestamp >= Config.COMMAND_INTERVAL:
|
||||||
|
re.append(x.command_0c())
|
||||||
|
player.last_timestamp = x.timestamp
|
||||||
|
|
||||||
|
# 离线判断
|
||||||
|
for i in range(4):
|
||||||
|
if i != self.player_index:
|
||||||
|
t = self.room.players[i]
|
||||||
|
if t.player_id != 0:
|
||||||
|
if t.last_timestamp != 0:
|
||||||
|
if t.online == 1 and x.timestamp - t.last_timestamp >= 5000000:
|
||||||
|
t.online = 0
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_12(i))
|
||||||
|
elif t.online == 0 and x.timestamp - t.last_timestamp >= Config.PLAYER_TIMEOUT:
|
||||||
|
self.room.delete_player(i)
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_12(i))
|
||||||
|
|
||||||
|
flag_11 = False
|
||||||
|
flag_12 = False
|
||||||
|
flag_13 = False
|
||||||
|
|
||||||
|
if player.online == 0:
|
||||||
|
flag_12 = True
|
||||||
|
player.online = 1
|
||||||
|
|
||||||
|
if self.room.is_ready(1, 1):
|
||||||
|
flag_13 = True
|
||||||
|
self.room.state = 2
|
||||||
|
|
||||||
|
if player.player_state != self.command[32]:
|
||||||
|
flag_12 = True
|
||||||
|
player.player_state = self.command[32]
|
||||||
|
|
||||||
|
if player.difficulty != self.command[33] and player.player_state != 5 and player.player_state != 6 and player.player_state != 7 and player.player_state != 8:
|
||||||
|
flag_12 = True
|
||||||
|
player.difficulty = self.command[33]
|
||||||
|
|
||||||
|
if player.cleartype != self.command[34] and player.player_state != 7 and player.player_state != 8:
|
||||||
|
flag_12 = True
|
||||||
|
player.cleartype = self.command[34]
|
||||||
|
|
||||||
|
if player.download_percent != self.command[35]:
|
||||||
|
flag_12 = True
|
||||||
|
player.download_percent = self.command[35]
|
||||||
|
|
||||||
|
if player.character_id != self.command[36]:
|
||||||
|
flag_12 = True
|
||||||
|
player.character_id = self.command[36]
|
||||||
|
|
||||||
|
if player.is_uncapped != self.command[37]:
|
||||||
|
flag_12 = True
|
||||||
|
player.is_uncapped = self.command[37]
|
||||||
|
|
||||||
|
if self.room.state == 3 and player.score != bi(self.command[24:28]):
|
||||||
|
flag_12 = True
|
||||||
|
player.score = bi(self.command[24:28])
|
||||||
|
|
||||||
|
if self.room.is_ready(3, 4):
|
||||||
|
flag_13 = True
|
||||||
|
self.room.countdown = Config.COUNTDOWM_TIME
|
||||||
|
self.room.timestamp = round(time.time() * 1000)
|
||||||
|
self.room.state = 4
|
||||||
|
|
||||||
|
if self.room.state == 4 or self.room.state == 5 or self.room.state == 6:
|
||||||
|
timestamp = round(time.time() * 1000)
|
||||||
|
self.room.countdown -= timestamp - self.room.timestamp
|
||||||
|
self.room.timestamp = timestamp
|
||||||
|
if self.room.state == 4 and self.room.countdown <= 0:
|
||||||
|
# 此处不清楚
|
||||||
|
self.room.state = 5
|
||||||
|
self.room.countdown = 5999
|
||||||
|
flag_11 = True
|
||||||
|
flag_13 = True
|
||||||
|
|
||||||
|
if self.room.state == 5 and self.room.is_ready(5, 6):
|
||||||
|
self.room.state = 6
|
||||||
|
flag_13 = True
|
||||||
|
|
||||||
|
if self.room.state == 5 and self.room.is_ready(5, 7):
|
||||||
|
self.room.state = 7
|
||||||
|
self.room.countdown = 0xffffffff
|
||||||
|
flag_13 = True
|
||||||
|
|
||||||
|
if self.room.state == 5 and self.room.countdown <= 0:
|
||||||
|
print('我怎么知道这是啥')
|
||||||
|
|
||||||
|
if self.room.state == 6 and self.room.countdown <= 0:
|
||||||
|
# 此处不清楚
|
||||||
|
self.room.state = 7
|
||||||
|
self.room.countdown = 0xffffffff
|
||||||
|
flag_13 = True
|
||||||
|
|
||||||
|
if self.room.countdown <= 0:
|
||||||
|
self.room.countdown = 0
|
||||||
|
|
||||||
|
if self.room.state == 7 or self.room.state == 8:
|
||||||
|
if player.timer < bi(self.command[28:32]) or bi(self.command[28:32]) == 0 and player.timer != 0:
|
||||||
|
player.last_timer = player.timer
|
||||||
|
player.last_score = player.score
|
||||||
|
player.timer = bi(self.command[28:32])
|
||||||
|
player.score = bi(self.command[24:28])
|
||||||
|
|
||||||
|
if player.timer != 0 or self.room.state != 8:
|
||||||
|
for i in self.room.players:
|
||||||
|
i.extra_command_queue.append(
|
||||||
|
x.command_0e(self.player_index))
|
||||||
|
|
||||||
|
if self.room.is_ready(8, 1):
|
||||||
|
flag_13 = True
|
||||||
|
self.room.state = 1
|
||||||
|
self.room.song_idx = 0xffff
|
||||||
|
if self.room.round_switch == 1:
|
||||||
|
self.room.make_round()
|
||||||
|
|
||||||
|
for i in self.room.players:
|
||||||
|
i.timer = 0
|
||||||
|
i.score = 0
|
||||||
|
|
||||||
|
if self.room.is_finish():
|
||||||
|
# 有人退房导致的结算
|
||||||
|
self.room.make_finish()
|
||||||
|
flag_13 = True
|
||||||
|
|
||||||
|
if flag_11:
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_11())
|
||||||
|
if flag_12:
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_12(self.player_index))
|
||||||
|
if flag_13:
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_13())
|
||||||
|
|
||||||
|
return re
|
||||||
|
|
||||||
|
def command_0a(self):
|
||||||
|
# 退出房间
|
||||||
|
self.room.delete_player(self.player_index)
|
||||||
|
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_12(self.player_index))
|
||||||
|
|
||||||
|
if self.room.state == 3:
|
||||||
|
self.room.state = 1
|
||||||
|
self.room.song_idx = 0xffff
|
||||||
|
# self.room.command_queue_length += 1
|
||||||
|
# self.room.command_queue.append(x.command_11())
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_13())
|
||||||
|
self.room.command_queue_length += 1
|
||||||
|
self.room.command_queue.append(x.command_14())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def command_0b(self):
|
||||||
|
# 推荐歌曲
|
||||||
|
song_idx = bi(self.command[16:18])
|
||||||
|
x = CommandSender(self.room)
|
||||||
|
for i in range(4):
|
||||||
|
if self.player_index != i and self.room.players[i].online == 1:
|
||||||
|
self.room.players[i].extra_command_queue.append(
|
||||||
|
x.command_0f(self.player_index, song_idx))
|
||||||
|
|
||||||
|
return None
|
||||||
46
latest version/udpserver/udp_sender.py
Normal file
46
latest version/udpserver/udp_sender.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import time
|
||||||
|
from .udp_class import Room, b
|
||||||
|
|
||||||
|
|
||||||
|
class CommandSender:
|
||||||
|
def __init__(self, room: Room = Room()) -> None:
|
||||||
|
self.room = room
|
||||||
|
self.timestamp = round(time.time() * 1000000)
|
||||||
|
|
||||||
|
self.random_code = b'\x11\x11\x11\x11\x00\x00\x00\x00'
|
||||||
|
|
||||||
|
def command_0c(self):
|
||||||
|
return b'\x06\x16\x0c\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.state) + b(self.room.countdown, 4) + b(self.timestamp, 8) + b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
|
||||||
|
|
||||||
|
def command_0d(self, code: int):
|
||||||
|
return b'\x06\x16\x0d\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(code) + b'\x07\x07\x07\x07\x07\x07\x07'
|
||||||
|
|
||||||
|
def command_0e(self, player_index: int):
|
||||||
|
# 分数广播
|
||||||
|
player = self.room.players[player_index]
|
||||||
|
return b'\x06\x16\x0e\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + b(player.player_id, 8) + b(player.character_id) + b(player.is_uncapped) + b(player.difficulty) + b(player.score, 4) + b(player.timer, 4) + b(player.cleartype) + b(player.player_state) + b(player.download_percent) + b'\x01' + b(player.last_score, 4) + b(player.last_timer, 4) + b(player.online)
|
||||||
|
|
||||||
|
def command_0f(self, player_index: int, song_idx: int):
|
||||||
|
# 歌曲推荐
|
||||||
|
player = self.room.players[player_index]
|
||||||
|
return b'\x06\x16\x0f\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + b(player.player_id, 8) + b(song_idx, 2) + b'\x06\x06\x06\x06\x06\x06'
|
||||||
|
|
||||||
|
def command_10(self):
|
||||||
|
# 房主宣告
|
||||||
|
return b'\x06\x16\x10\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.host_id, 8)
|
||||||
|
|
||||||
|
def command_11(self):
|
||||||
|
return b'\x06\x16\x11\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + self.room.get_players_info() + b'\x08\x08\x08\x08\x08\x08\x08\x08'
|
||||||
|
|
||||||
|
def command_12(self, player_index: int):
|
||||||
|
player = self.room.players[player_index]
|
||||||
|
return b'\x06\x16\x12\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(player_index) + b(player.player_id, 8) + b(player.character_id) + b(player.is_uncapped) + b(player.difficulty) + b(player.score, 4) + b(player.timer, 4) + b(player.cleartype) + b(player.player_state) + b(player.download_percent) + b(player.online)
|
||||||
|
|
||||||
|
def command_13(self):
|
||||||
|
return b'\x06\x16\x13\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.host_id, 8) + b(self.room.state) + b(self.room.countdown, 4) + b(self.timestamp, 8) + b(self.room.song_idx, 2) + b(self.room.interval, 2) + b(self.room.times, 7) + self.room.get_player_last_score() + b(self.room.last_song_idx, 2) + b(self.room.round_switch, 1) + b'\x01'
|
||||||
|
|
||||||
|
def command_14(self):
|
||||||
|
return b'\x06\x16\x14\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + self.room.song_unlock + b'\x08\x08\x08\x08\x08\x08\x08\x08'
|
||||||
|
|
||||||
|
def command_15(self):
|
||||||
|
return b'\x06\x16\x15\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.room.get_players_info() + self.room.song_unlock + b(self.room.host_id, 8) + b(self.room.state) + b(self.room.countdown, 4) + b(self.timestamp, 8) + b(self.room.song_idx, 2) + b(self.room.interval, 2) + b(self.room.times, 7) + self.room.get_player_last_score() + b(self.room.last_song_idx, 2) + b(self.room.round_switch, 1) + b'\x09\x09\x09\x09\x09\x09\x09\x09\x09'
|
||||||
0
latest version/web/__init__.py
Normal file
0
latest version/web/__init__.py
Normal file
@@ -418,7 +418,7 @@ def all_character():
|
|||||||
def change_character():
|
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']
|
'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']
|
||||||
return render_template('web/changechar.html', skill_ids=skill_ids)
|
return render_template('web/changechar.html', skill_ids=skill_ids)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user