16 Commits
v2.8 ... v2.8.4

Author SHA1 Message Date
Lost-MSth
1235733ddf Update to v2.8.6 2022-04-01 13:37:38 +08:00
Lost-MSth
cb6425a0d1 Update to v2.8.3 without release
- Unlock the character **linka**.
- Update the song database.
2022-03-24 16:30:46 +08:00
Lost-MSth
3a1c731e24 Update to v2.8.2 without release
- Try to add support for Anniversary 5 ticket
- Update the song database

> Well, there may be bugs with Anniversary 5 ticket. Remember it can only be used when the pack is on sale.
2022-03-09 18:44:00 +08:00
Lost-MSth
5de274fdf5 Update to v2.8.1 2022-02-20 23:36:44 +08:00
Lost-MSth
b60457c38b Ready to update to v2.8.1
Wait for chart constants and character values.
2022-02-17 21:16:57 +08:00
Lost-MSth
35b954e549 Fix two bugs
- Fix a bug when downloading a beyond chart which has another audio file.

- Fix a safety problem when downloading.
2022-02-14 16:35:36 +08:00
Lost-MSth
ceebba4664 Fix a bug
Fix a bug that the high version of ios client cannot log in.
2022-01-27 19:26:13 +08:00
Lost-MSth
5bc9b9a3dc Fix a bug
Fix a bug about link play not getting the right address.
If you use reverse proxy, this may be helpful.
2022-01-24 22:38:43 +08:00
Lost-MSth
d9a543bc5a Change something about constant table and aggregate 2022-01-24 21:44:37 +08:00
Lost-MSth
4666c9c153 Merge pull request #35 from Young-Lord/master
加入aggregate
2022-01-24 21:28:44 +08:00
Lost-MSth
32bcbb0ccd Change something about aggregate 2022-01-24 21:22:38 +08:00
Young-Lord
34497d0638 Remove requests from requirements 2022-01-22 10:42:42 +08:00
Young-Lord
23bf3c020f Change the implement of experimental agreegate API 2022-01-22 10:40:06 +08:00
Young-Lord
e1fc1bbcd1 Minor fix 2022-01-22 07:35:57 +08:00
Young-Lord
355ec3557f Add experimental aggregate API 2022-01-21 22:55:23 +08:00
Young-Lord
cc4ac192e7 Update .gitignore 2022-01-21 22:53:22 +08:00
24 changed files with 1706 additions and 1336 deletions

4
.gitignore vendored
View File

@@ -1 +1,5 @@
*.log
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

View File

@@ -66,20 +66,20 @@ 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.
### Version 2.8
- 适用于Arcaea 3.11.2版本 For Arcaea 3.11.2
- 更新了歌曲数据库 Update the song database.
- 新增对Link Play的支持 Add support for Link Play.
- 搭档**彩梦**已觉醒 Uncap the character **ayu**.
### Version 2.8.4
- &spades;适用于Arcaea 4.0.&infin;<!--3.12.6-->版本&spades; For Arcaea 3.12.6
- &hearts;修复616==sb?PTT:PPT<!--**凛可**-->缺少Bug<!--语音-->的问题&hearts; Fix missing voices of **Linka**.
- 以下是累积更新 The following are cumulative updates:
- 修复注册接口端点多了个杠的问题 Fix an unexpected slash at the registered interface endpoint.
- 新搭档**白姬**已解锁 Unlock the character **shirahime**.
- 修正两个角色的数值问题 Fix the values of two characters.
- 新搭档**玛莉嘉**已解锁 Unlock the character **marija**.
- 以下大概可能也许不得不是累积更新 The following are cumulative updates:
- &clubs;更新了█████[](歌曲数据库)&clubs; Update the song database.
- &diams;尝试对!@#$%^<!--5周年兑换券-->提供支持&diams; Try to add support for Anniversary 5 ticket.
- &iquest;新搭档' or name = 'Taikari'--<!--**凛可**-->已解锁&iquest; Unlock the character **Linka**.
> 提示Link Play可能存在大量bug
> Tips: There may be many bugs in Link Play system.
<!--
本项目不会像lowiro一样为各位用户制造各种奇奇怪怪的麻烦即便是在愚人节这种盛大的节日大家也能放心使用
Happy April Fool's Day!
Sorry for no joke in English.
-->
## 运行环境与依赖 Running environment and requirements

Binary file not shown.

View File

@@ -4,7 +4,7 @@ import json
# 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用
ARCAEA_SERVER_VERSION = 'v2.8'
ARCAEA_SERVER_VERSION = 'v2.8.4'
def main(path='./'):
@@ -223,7 +223,8 @@ def main(path='./'):
price int,
orig_price int,
discount_from int,
discount_to int
discount_to int,
discount_reason text
);''')
c.execute('''create table if not exists purchase_item(purchase_name text,
item_id text,
@@ -299,46 +300,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',
'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']
'ayu', 'Eto & Luna', 'yume', 'Seine & Hikari', 'saya', 'Tairitsu & Chuni Penguin', 'Chuni Penguin', 'haruna', 'nono', 'MTA-XXX', 'MDA-21', 'kanae', 'Hikari(Fantasia)', 'Tairitsu(Sonata)', 'sia', 'DORO*C', 'Tairitsu(Tempest)', 'brillante', 'Ilith(Summer)', 'etude', 'Alice & Tenniel', 'Luna & Mia', 'areus', 'seele', 'isabelle', 'mir', 'lagrange', 'linka', 'nami', 'Saya & Elizabeth', 'lily', 'kanae(midsummer)', 'alice&tenniel(minuet)', 'tairitsu(elegy)', 'marija', 'vita']
skill_id = ['gauge_easy', '', '', '', 'note_mirror', '', '', 'gauge_hard', 'frag_plus_10_pack_stellights', 'gauge_easy|frag_plus_15_pst&prs', 'gauge_hard|fail_frag_minus_100', 'frag_plus_5_side_light', 'visual_hide_hp', 'frag_plus_5_side_conflict', 'challenge_fullcombo_0gauge', 'gauge_overflow', 'gauge_easy|note_mirror', 'note_mirror', 'visual_tomato_pack_tonesphere',
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes']
'frag_rng_ayu', 'gaugestart_30|gaugegain_70', 'combo_100-frag_1', 'audio_gcemptyhit_pack_groovecoaster', 'gauge_saya', 'gauge_chuni', 'kantandeshou', 'gauge_haruna', 'frags_nono', 'gauge_pandora', 'gauge_regulus', 'omatsuri_daynight', '', '', 'sometimes(note_mirror|frag_plus_5)', 'scoreclear_aa|visual_scoregauge', 'gauge_tempest', 'gauge_hard', 'gauge_ilith_summer', '', 'note_mirror|visual_hide_far', 'frags_ongeki', 'gauge_areus', 'gauge_seele', 'gauge_isabelle', 'gauge_exhaustion', 'skill_lagrange', 'gauge_safe_10', 'frags_nami', 'skill_elizabeth', 'skill_lily', 'skill_kanae_midsummer', '', '', 'visual_ghost_skynotes', 'skill_vita']
skill_id_uncap = ['', '', 'frags_kou', '', 'visual_ink', '', '', '', '', '', '', 'eto_uncap', 'luna_uncap', 'shirabe_entry_fee',
'', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
'', '', '', '', '', 'ayu_uncap', '', 'frags_yume', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
skill_unlock_level = [0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0,
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8]
0, 0, 0, 8, 0, 14, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0]
frag1 = [55, 55, 60, 50, 47, 0, 47, 57, 41, 22, 50, 54, 60, 56, 78, 42, 41, 61, 52, 50, 52, 32,
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 50, 25, 58, 50, 61, 45, 45, 38]
42, 55, 45, 58, 43, 0.5, 68, 50, 62, 45, 45, 52, 44, 27, 59, 0, 45, 50, 50, 47, 47, 61, 43, 42, 38, 25, 58, 50, 61, 45, 45, 38, 34]
prog1 = [35, 55, 47, 50, 60, 0, 60, 70, 58, 45, 70, 45, 42, 46, 61, 67, 49, 44, 28, 45, 24, 46, 52,
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58]
59, 62, 33, 58, 25, 63, 69, 50, 45, 45, 51, 34, 70, 62, 70, 45, 32, 32, 61, 47, 47, 37, 42, 50, 50, 45, 41, 61, 45, 45, 58, 50]
overdrive1 = [35, 55, 25, 50, 47, 0, 72, 57, 41, 7, 10, 32, 65, 31, 61, 53, 31, 47, 38, 12, 39, 18,
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 50, 30, 49, 15, 34, 45, 45, 38]
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67]
frag20 = [78, 80, 90, 75, 70, 0, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60]
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51]
prog20 = [61, 80, 70, 75, 90, 0, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90]
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75]
overdrive20 = [61, 80, 47, 75, 70, 0, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60]
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100]
frag30 = [88, 90, 100, 75, 80, 0, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 100, 50, 68, 60, 90, 67, 50, 60]
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51]
prog30 = [71, 90, 80, 75, 100, 0, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 100, 55, 65, 59, 90, 50, 90, 90]
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75]
overdrive30 = [71, 90, 57, 75, 80, 0, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 100, 40, 69, 62, 51, 90, 67, 60]
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100]
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, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2]
char_core = {
0: [{'core_id': 'core_hollow', 'amount': 25}, {'core_id': 'core_desolate', 'amount': 5}],
@@ -359,7 +360,7 @@ def main(path='./'):
19: [{'core_id': 'core_colorful', 'amount': 30}]
}
for i in range(0, 54):
for i in range(0, 55):
skill_requires_uncap = 1 if i == 2 else 0
if i in [0, 1, 2, 4, 13, 26, 27, 28, 29, 36, 21, 42, 43, 11, 12, 19]:
@@ -367,7 +368,7 @@ def main(path='./'):
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
else:
if i != 5 and i != 46:
if i != 5:
sql = '''insert into character values(?,?,20,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0)'''
c.execute(sql, (i, char[i], frag1[i], prog1[i], overdrive1[i], frag20[i], prog20[i], overdrive20[i],
frag30[i], prog30[i], overdrive30[i], skill_id[i], skill_unlock_level[i], skill_requires_uncap, skill_id_uncap[i], char_type[i]))
@@ -386,12 +387,12 @@ def main(path='./'):
c.execute('''insert into item values(?,"core",1,'')''', (i,))
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3"]
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3"]
for i in world_songs:
c.execute('''insert into item values(?,"world_song",1,'')''', (i,))
world_unlocks = ["scenery_chap1", "scenery_chap2",
"scenery_chap3", "scenery_chap4", "scenery_chap5"]
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6"]
for i in world_unlocks:
c.execute('''insert into item values(?,"world_unlock",1,'')''', (i,))
@@ -399,6 +400,8 @@ def main(path='./'):
('fragment', 'fragment', 1, ''))
c.execute('''insert into item values(?,?,?,?)''',
('memory', 'memory', 1, ''))
c.execute('''insert into item values(?,?,?,?)''',
('anni5tix', 'anni5tix', 1, ''))
def insert_items(c, items):
# 物品数据导入
@@ -411,8 +414,12 @@ def main(path='./'):
discount_to = -1
else:
discount_to = i['discount_to']
c.execute('''insert into purchase values(?,?,?,?,?)''',
(i['name'], i['price'], i['orig_price'], discount_from, discount_to))
if 'discount_reason' not in i:
discount_reason = ''
else:
discount_reason = i['discount_reason']
c.execute('''insert into purchase values(?,?,?,?,?,?)''',
(i['name'], i['price'], i['orig_price'], discount_from, discount_to, discount_reason))
for j in i['items']:
if "_id" not in j:
_id = ''

View File

@@ -1054,5 +1054,113 @@
],
"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
},
{
"name": "headbonkache",
"items": [
{
"type": "single",
"id": "headbonkache",
"is_available": true
},
{
"type": "core",
"amount": 1,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 100,
"price": 100
},
{
"name": "aurgelmir",
"items": [
{
"type": "single",
"id": "aurgelmir",
"is_available": true
},
{
"type": "core",
"amount": 1,
"id": "core_generic",
"is_available": true
}
],
"orig_price": 100,
"price": 100
}
]

View File

@@ -1,6 +1,6 @@
# encoding: utf-8
from flask import Flask, json, request, jsonify, make_response, send_from_directory
from flask import Flask, json, request, jsonify, send_from_directory
from logging.config import dictConfig
from setting import Config
import base64
@@ -23,6 +23,10 @@ import sys
from multiprocessing import Process, Pipe
from urllib.parse import parse_qs, urlparse
from werkzeug.datastructures import ImmutableMultiDict
app = Flask(__name__)
wsgi_app = app.wsgi_app
@@ -122,10 +126,16 @@ def error_return(error_code, extra={}): # 错误返回
})
def success_return(value):
return jsonify({
"success": True,
"value": value
})
@app.route('/')
def hello():
return "Hello World!"
# 自定义路径
@app.route('/favicon.ico', methods=['GET']) # 图标
@@ -153,10 +163,10 @@ def login():
else:
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)
if not error_code:
r = {"success": True, "token_type": "Bearer"}
r = {"success": True, "token_type": "Bearer", 'user_id': user_id}
r['access_token'] = token
return jsonify(r)
else:
@@ -191,25 +201,83 @@ def register():
return error_return(error_code)
# 集成式请求,没想到什么好办法处理,就先这样写着
@app.route(add_url_prefix('/compose/aggregate'), methods=['GET'])
@app.route(add_url_prefix('/purchase/bundle/pack'), 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)
def bundle_pack(user_id):
return success_return(server.info.get_purchase_pack(user_id))
@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)
def user_me(user_id):
r = server.info.arc_aggregate_small(user_id)
if r['success']:
r['value'] = r['value'][0]['value']
return jsonify(r)
r = server.info.get_user_me_c(user_id)
if r:
return success_return(r)
else:
return error_return(108)
@app.route(add_url_prefix('/user/me/character'), methods=['POST']) # 角色切换
@@ -564,10 +632,11 @@ def pack(user_id):
# 单曲购买信息获取
@app.route(add_url_prefix('/purchase/bundle/single'), methods=['GET'])
def single():
@server.auth.auth_required(request)
def single(user_id):
return jsonify({
"success": True,
"value": server.arcpurchase.get_single_purchase()
"value": server.arcpurchase.get_single_purchase(user_id)
})
@@ -614,10 +683,11 @@ def world_one(user_id, map_id):
@server.auth.auth_required(request)
def download_song(user_id):
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 = {}
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:
re = server.arcdownload.get_some_songs(user_id, song_ids)
@@ -633,7 +703,7 @@ def download_song(user_id):
def download(file_path):
try:
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:
path = os.path.join('./database/songs', file_path)
if os.path.isfile(path) and not('../' in path or '..\\' in path):
@@ -658,7 +728,11 @@ def room_create(user_id):
conn1, user_id, client_song_map)
if error_code == 0:
value['endPoint'] = request.host.split(':')[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,
@@ -680,7 +754,11 @@ def room_join(user_id, room_code):
conn1, user_id, client_song_map, room_code)
if error_code == 0:
value['endPoint'] = request.host.split(':')[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,
@@ -700,7 +778,11 @@ def multiplayer_update(user_id):
error_code, value = server.arclinkplay.update_room(conn1, user_id, token)
if error_code == 0:
value['endPoint'] = request.host.split(':')[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,
@@ -723,9 +805,20 @@ def sys_set(user_id, path):
set_arg = path[5:]
value = request.form['value']
server.setme.arc_sys_set(user_id, value, set_arg)
r = server.info.arc_aggregate_small(user_id)
r['value'] = r['value'][0]['value']
return jsonify(r)
r = server.info.get_user_me_c(user_id)
if 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():

View 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})
for i in dir_list:
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg']:
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
token = hashlib.md5(
(str(user_id) + song_id + i + str(now)).encode(encoding='UTF-8')).hexdigest()
if i == 'base.ogg':
if 'audio' not in re:
re['audio'] = {}
c.execute(
'''select md5 from songfile where song_id=:a and file_type=-1''', {'a': song_id})
x = c.fetchone()
@@ -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(
file_dir, song_id, 'base.ogg'))
re['audio']["checksum"] = checksum
if url_flag:
re['audio'] = {"checksum": checksum, "url": get_url(
file_path=song_id+'/base.ogg', t=token)}
re['audio']["url"] = get_url(
file_path=song_id+'/base.ogg', t=token)
elif i == '3.ogg':
if 'audio' not in re:
re['audio'] = {}
c.execute(
'''select md5 from songfile where song_id=:a and file_type=-2''', {'a': song_id})
x = c.fetchone()
if x:
checksum = x[0]
else:
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:
if 'chart' not in re:
re['chart'] = {}
@@ -116,7 +137,7 @@ def get_some_songs(user_id, song_ids):
return re
def is_token_able_download(t):
def is_token_able_download(t, path):
# token是否可以下载返回错误码0即可以
errorcode = 108
with Connect() as c:
@@ -124,7 +145,7 @@ def is_token_able_download(t):
{'t': t})
x = c.fetchone()
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(
'''select count(*) from user_download where user_id = :a''', {'a': x[0]})
y = c.fetchone()
@@ -162,10 +183,13 @@ def initialize_one_songfile(c, song_id, file_dir='./database/songs'):
# 计算并添加歌曲md5到表中无返回
dir_list = os.listdir(os.path.join(file_dir, song_id))
for i in dir_list:
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg']:
if os.path.isfile(os.path.join(file_dir, song_id, i)) and i in ['0.aff', '1.aff', '2.aff', '3.aff', 'base.ogg', '3.ogg']:
if i == 'base.ogg':
c.execute('''insert into songfile values(:a,-1,:c)''', {
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, 'base.ogg'))})
elif i == '3.ogg':
c.execute('''insert into songfile values(:a,-2,:c)''', {
'a': song_id, 'c': get_file_md5(os.path.join(file_dir, song_id, '3.ogg'))})
else:
c.execute('''insert into songfile values(:a,:b,:c)''', {
'a': song_id, 'b': int(i[0]), 'c': get_file_md5(os.path.join(file_dir, song_id, i))})

View File

@@ -2,7 +2,6 @@ from server.sql import Connect
import server.item
import server.character
import time
import json
def int2b(x):
@@ -13,7 +12,7 @@ def int2b(x):
return True
def get_purchase(c, type='pack'):
def get_purchase(c, user_id, type='pack'):
# 读取packs内容返回字典列表
c.execute(
'''select * from purchase where purchase_name in (select purchase_name from purchase_item where type = :a)''', {'a': type})
@@ -50,7 +49,6 @@ def get_purchase(c, type='pack'):
"amount": amount
})
if t is not None:
# 放到列表头
items = [t, items]
@@ -62,19 +60,27 @@ def get_purchase(c, type='pack'):
if i[3] > 0:
r['discount_from'] = i[3]
if i[4] > 0:
r['discount_to'] = i[4]
if i[4] > 0:
r['discount_to'] = i[4]
if i[5] == 'anni5tix' and i[3] <= int(time.time() * 1000) <= i[4]:
c.execute(
'''select amount from user_item where user_id=? and item_id="anni5tix"''', (user_id,))
z = c.fetchone()
if z and z[0] >= 1:
r['discount_reason'] = 'anni5tix'
r['price'] = 0
re.append(r)
return re
def get_single_purchase():
def get_single_purchase(user_id):
# main里面没开数据库这里写一下代替
re = []
with Connect() as c:
re = get_purchase(c, type='single')
re = get_purchase(c, user_id, 'single')
return re
@@ -98,6 +104,25 @@ def buy_item(c, user_id, price):
return True, ticket - price
def buy_item_with_anni5tix(c, user_id):
# 兑换券购买接口,返回成功与否标志
c.execute('''select amount from user_item where user_id = :a and item_id = "anni5tix"''',
{'a': user_id})
amount = c.fetchone()
if amount:
amount = amount[0]
else:
return False
if amount <= 0:
return False
c.execute('''update user_item set amount = :b where user_id = :a and item_id = "anni5tix"''',
{'a': user_id, 'b': amount-1})
return True
def buy_thing(user_id, purchase_id):
# 购买物品接口,返回字典
success_flag = False
@@ -107,7 +132,7 @@ def buy_thing(user_id, purchase_id):
characters = []
with Connect() as c:
c.execute('''select price, orig_price, discount_from, discount_to from purchase where purchase_name=:a''',
c.execute('''select price, orig_price, discount_from, discount_to, discount_reason from purchase where purchase_name=:a''',
{'a': purchase_id})
x = c.fetchone()
price = 0
@@ -117,6 +142,7 @@ def buy_thing(user_id, purchase_id):
orig_price = x[1]
discount_from = x[2]
discount_to = x[3]
discount_reason = x[4]
else:
return {
"success": False,
@@ -130,6 +156,8 @@ def buy_thing(user_id, purchase_id):
now = int(time.time() * 1000)
if not(discount_from <= now <= discount_to):
price = orig_price
elif discount_reason == 'anni5tix' and buy_item_with_anni5tix(c, user_id):
price = 0
flag, ticket = buy_item(c, user_id, price)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
import json
from server.sql import Connect
from .config import Constant
from setting import Config
import server.item
import server.character
@@ -10,11 +11,6 @@ import time
import random
ETO_UNCAP_BONUS_PROGRESS = 7
LUNA_UNCAP_BONUS_PROGRESS = 7
AYU_UNCAP_BONUS_PROGRESS = 5
def int2b(x):
# int与布尔值转换
if x is None or x == 0:
@@ -26,14 +22,14 @@ def int2b(x):
def calc_stamina(max_stamina_ts, curr_stamina):
# 计算体力,返回剩余体力数值
stamina = int(server.info.MAX_STAMINA - (max_stamina_ts -
int(time.time()*1000)) / server.info.STAMINA_RECOVER_TICK)
stamina = int(Constant.MAX_STAMINA - (max_stamina_ts -
int(time.time()*1000)) / Constant.STAMINA_RECOVER_TICK)
if stamina >= server.info.MAX_STAMINA:
if curr_stamina >= server.info.MAX_STAMINA:
if stamina >= Constant.MAX_STAMINA:
if curr_stamina >= Constant.MAX_STAMINA:
stamina = curr_stamina
else:
stamina = server.info.MAX_STAMINA
stamina = Constant.MAX_STAMINA
if stamina < 0:
stamina = 0
@@ -245,8 +241,8 @@ def play_world_song(user_id, args):
return {}
stamina = calc_stamina(max_stamina_ts, stamina) - \
info['stamina_cost'] * stamina_multiply
max_stamina_ts = now + server.info.STAMINA_RECOVER_TICK * \
(server.info.MAX_STAMINA - stamina)
max_stamina_ts = now + Constant.STAMINA_RECOVER_TICK * \
(Constant.MAX_STAMINA - stamina)
c.execute('''update user set max_stamina_ts=?, stamina=? where user_id=?''',
(max_stamina_ts, stamina, user_id))
r = {
@@ -356,7 +352,7 @@ def climb_step(user_id, map_id, step, prev_capture, prev_position):
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 / \
@@ -377,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
skill = False
skill_uncap = False
level = 1
exp = 0
frag = 50
prog = 50
overdrive = 50
if not is_skill_sealed:
if x:
skill = True
@@ -385,55 +386,76 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
if x[5] is not None and x[5] == 1:
skill_uncap = False
c.execute('''select frag1,prog1,overdrive1,frag20,prog20,overdrive20,frag30,prog30,overdrive30,skill_id,skill_id_uncap from character where character_id=?''', (character_id,))
x = c.fetchone()
c.execute('''select frag1,prog1,overdrive1,frag20,prog20,overdrive20,frag30,prog30,overdrive30,skill_id,skill_id_uncap from character where character_id=?''', (character_id,))
x = c.fetchone()
if Config.CHARACTER_FULL_UNLOCK:
c.execute('''select level, exp from user_char_full where user_id = :a and character_id = :b''', {
'a': user_id, 'b': character_id})
else:
c.execute('''select level, exp from user_char where user_id = :a and character_id = :b''', {
'a': user_id, 'b': character_id})
y = c.fetchone()
if y:
level = y[0]
exp = y[1]
else:
level = 1
exp = 0
if x:
flag = server.character.calc_char_value(level, x[0], x[3], x[6])
prog = server.character.calc_char_value(level, x[1], x[4], x[7])
overdrive = server.character.calc_char_value(level, x[2], x[5], x[8])
if x[9] is not None and x[9] != '' and skill:
skill = x[9]
if Config.CHARACTER_FULL_UNLOCK:
c.execute('''select level, exp from user_char_full where user_id = :a and character_id = :b''', {
'a': user_id, 'b': character_id})
else:
c.execute('''select level, exp from user_char where user_id = :a and character_id = :b''', {
'a': user_id, 'b': character_id})
y = c.fetchone()
if y:
level = y[0]
exp = y[1]
else:
level = 1
exp = 0
if x:
frag = server.character.calc_char_value(level, x[0], x[3], x[6])
prog = server.character.calc_char_value(level, x[1], x[4], x[7])
overdrive = server.character.calc_char_value(
level, x[2], x[5], x[8])
if x[9] is not None and x[9] != '' and skill:
skill = x[9]
else:
skill = None
if x[10] is not None and x[9] != '' and skill_uncap:
skill_uncap = x[10]
else:
skill_uncap = None
else:
frag = 0
prog = 0
overdrive = 0
skill = None
if x[10] is not None and x[9] != '' and skill_uncap:
skill_uncap = x[10]
else:
skill_uncap = None
else:
flag = 0
prog = 0
overdrive = 0
skill = None
skill_uncap = None
skill_special = ''
if skill_uncap is not None and skill_uncap and skill_uncap in ['eto_uncap', 'luna_uncap', 'ayu_uncap', 'skill_vita']:
skill_special = skill_uncap
elif skill is not None and skill and skill in ['eto_uncap', 'luna_uncap', 'ayu_uncap', 'skill_vita']:
skill_special = skill
c.execute('''select current_map from user where user_id = :a''', {
'a': user_id})
map_id = c.fetchone()[0]
if beyond_gauge == 0: # 是否是beyond挑战
prog_tempest = 0
if not is_skill_sealed and character_id == 35:
# 风暴对立
if Config.CHARACTER_FULL_UNLOCK:
prog_tempest = 60
else:
c.execute(
'''select sum(level) from user_char where user_id=?''', (user_id,))
prog_tempest = int(x[0]) / 10 if x else 0
if prog_tempest > 60:
prog_tempest = 60
elif prog_tempest < 0:
prog_tempest = 0
base_step = 2.5 + 2.45*rating**0.5
step = base_step * (prog/50) * step_times
step = base_step * (prog + prog_tempest) / 50 * step_times
else:
info = get_world_info(map_id)
if clear_type == 0:
base_step = 8/9 + (rating/1.3)**0.5
base_step = 25/28 + (rating)**0.5 * 0.43
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']:
affinity_multiplier = info['affinity_multiplier'][info['character_affinity'].index(
@@ -441,7 +463,17 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
else:
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''',
{'a': user_id, 'b': map_id})
@@ -451,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(
user_id, map_id, step, y[3], y[2])
# Eto和Luna的技能
# Eto、Luna、Ayu的技能
character_bonus_progress = None
skill_special = ''
if skill_uncap is not None and skill_uncap and skill_uncap in ['eto_uncap', 'luna_uncap', 'ayu_uncap']:
skill_special = skill_uncap
elif skill is not None and skill and skill in ['eto_uncap', 'luna_uncap', 'ayu_uncap']:
skill_special = skill
if skill_special == 'eto_uncap':
# eto觉醒技能获得残片奖励时世界模式进度加7
fragment_flag = False
@@ -469,7 +497,7 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
if fragment_flag:
break
if fragment_flag:
character_bonus_progress = ETO_UNCAP_BONUS_PROGRESS
character_bonus_progress = Constant.ETO_UNCAP_BONUS_PROGRESS
step += character_bonus_progress * step_times
rewards, steps, curr_position, curr_capture, info = climb_step(
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
@@ -477,7 +505,7 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
elif skill_special == 'luna_uncap':
# luna觉醒技能限制格开始时世界模式进度加7
if 'restrict_id' in steps[0] and 'restrict_type' in steps[0] and steps[0]['restrict_type'] != '' and steps[0]['restrict_id'] != '':
character_bonus_progress = LUNA_UNCAP_BONUS_PROGRESS
character_bonus_progress = Constant.LUNA_UNCAP_BONUS_PROGRESS
step += character_bonus_progress * step_times
rewards, steps, curr_position, curr_capture, info = climb_step(
user_id, map_id, step, y[3], y[2]) # 二次爬梯,重新计算
@@ -485,9 +513,9 @@ def world_update(c, user_id, song_id, difficulty, rating, clear_type, beyond_gau
elif skill_special == 'ayu_uncap':
# ayu觉醒技能世界模式进度+5或-5但不会小于0
if random.random() >= 0.5:
character_bonus_progress = AYU_UNCAP_BONUS_PROGRESS
character_bonus_progress = Constant.AYU_UNCAP_BONUS_PROGRESS
else:
character_bonus_progress = -AYU_UNCAP_BONUS_PROGRESS
character_bonus_progress = -Constant.AYU_UNCAP_BONUS_PROGRESS
step += character_bonus_progress * step_times
if step < 0:
@@ -515,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=?''',
(level, exp, user_id, character_id))
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:
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'],
"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
}
re["user_map"]["steps"] = steps
else:
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'],
"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
}
re["user_map"]["steps"] = len(steps)
if character_id == 35 and not is_skill_sealed:
re['char_stats']['prog_tempest'] = prog_tempest
re['char_stats']['prog'] += prog_tempest
if character_bonus_progress is not None:
re['character_bonus_progress'] = character_bonus_progress
@@ -600,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:
stamina = calc_stamina(x[0], x[1]) + add_stamina
max_stamina_ts = now - \
(stamina-server.info.MAX_STAMINA) * \
server.info.STAMINA_RECOVER_TICK
(stamina-Constant.MAX_STAMINA) * \
Constant.STAMINA_RECOVER_TICK
else:
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=?''',
(max_stamina_ts, stamina, user_id))

View File

@@ -9,12 +9,13 @@ BAN_TIME = [1, 3, 7, 15, 31]
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的加密方式为 SHA-256
error_code = 108
token = None
user_id = None
with Connect() as c:
hash_pwd = hashlib.sha256(password.encode("utf8")).hexdigest()
c.execute('''select user_id, password, ban_flag from user where name = :name''', {
@@ -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))
if c.fetchone()[0] >= Config.LOGIN_DEVICE_NUMBER_LIMIT:
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);''',
{'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
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): # 注册

View File

@@ -1,12 +1,10 @@
from setting import Config
from server.sql import Connect
from .config import Constant
import server.info
import server.item
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):
# int与布尔值转换
@@ -18,7 +16,7 @@ def int2b(x):
def get_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):
@@ -114,7 +112,7 @@ def get_user_character(c, user_id):
"overdrive": calc_char_value(i[2], i[11], i[14], i[17]),
"prog": calc_char_value(i[2], i[10], i[13], i[16]),
"frag": calc_char_value(i[2], i[9], i[12], i[15]),
"level_exp": LEVEL_STEPS[i[2]],
"level_exp": Constant.LEVEL_STEPS[i[2]],
"exp": i[3],
"level": i[2],
"name": i[7],
@@ -151,13 +149,13 @@ def get_one_character(c, user_id, character_id):
"overdrive": calc_char_value(x[2], x[11], x[14], x[17]),
"prog": calc_char_value(x[2], x[10], x[13], x[16]),
"frag": calc_char_value(x[2], x[9], x[12], x[15]),
"level_exp": LEVEL_STEPS[x[2]],
"level_exp": Constant.LEVEL_STEPS[x[2]],
"exp": x[3],
"level": x[2],
"name": x[7],
"character_id": x[1]
}
if x[1] == 21:
if x[1] == 21 or x[1] == 46:
r["voice"] = [0, 1, 2, 3, 100, 1000, 1001]
return r
@@ -168,18 +166,18 @@ def calc_level_up(c, user_id, character_id, 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=?''',
(user_id, character_id))
x = c.fetchone()
if x and x[0] == 0:
return LEVEL_STEPS[20], 20
return Constant.LEVEL_STEPS[20], 20
a = []
b = []
for i in LEVEL_STEPS:
for i in Constant.LEVEL_STEPS:
a.append(i)
b.append(LEVEL_STEPS[i])
b.append(Constant.LEVEL_STEPS[i])
if exp >= b[-1]: # 溢出
return b[-1], a[-1]
@@ -213,11 +211,11 @@ def char_use_core(user_id, character_id, amount):
x = c.fetchone()
if x:
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=?''',
(level, exp, user_id, character_id))
(level, exp, user_id, character_id))
server.item.claim_user_item(
c, user_id, 'core_generic', 'core', -amount)
c, user_id, 'core_generic', 'core', -amount)
r = {'character': [get_one_character(c, user_id, character_id)]}
r['cores'] = server.item.get_user_cores(c, user_id)

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,7 @@ import server.character
import server.item
import time
from setting import Config
MAX_STAMINA = 12
STAMINA_RECOVER_TICK = 1800000
CORE_EXP = 250
from .config import Constant
def int2b(x):
@@ -202,6 +199,38 @@ def get_user_me(c, user_id):
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, user_id)
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):
# 返回用户数据
r = {"success": False}
@@ -232,16 +261,16 @@ def arc_aggregate_big(user_id):
"value": get_user_me(c, user_id)
}, {
"id": 1,
"value": server.arcpurchase.get_purchase(c, 'pack')
"value": server.arcpurchase.get_purchase(c, user_id)
}, {
"id": 2,
"value": id_2
}, {
"id": 3,
"value": {
"max_stamina": MAX_STAMINA,
"stamina_recover_tick": STAMINA_RECOVER_TICK,
"core_exp": CORE_EXP,
"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,

View File

@@ -55,20 +55,20 @@ def claim_user_item(c, user_id, item_id, item_type, amount=1):
else:
return False
if item_type == 'core':
if item_type in ['core', 'anni5tix']:
c.execute(
'''select amount from user_item where user_id=? and item_id=? and type="core"''', (user_id, item_id))
'''select amount from user_item where user_id=? and item_id=? and type=?''', (user_id, item_id, item_type))
x = c.fetchone()
if x:
if x[0] + amount < 0: # 数量不足
return False
c.execute('''update user_item set amount=? where user_id=? and item_id=? and type="core"''',
(x[0]+amount, user_id, item_id))
c.execute('''update user_item set amount=? where user_id=? and item_id=? and type=?''',
(x[0]+amount, user_id, item_id, item_type))
else:
if amount < 0: # 添加数量错误
return False
c.execute('''insert into user_item values(?,?,"core",?)''',
(user_id, item_id, amount))
c.execute('''insert into user_item values(?,?,?,?)''',
(user_id, item_id, item_type, amount))
elif item_type == 'memory':
c.execute('''select ticket from user where user_id=?''', (user_id,))

View File

@@ -1,5 +1,6 @@
from server.sql import Connect
from setting import Config
from .config import Constant
import server.info
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]),
"prog": server.character.calc_char_value(y[2], y[10], y[13], y[16]),
"frag": server.character.calc_char_value(y[2], y[9], y[12], y[15]),
"level_exp": server.character.LEVEL_STEPS[y[2]],
"level_exp": Constant.LEVEL_STEPS[y[2]],
"exp": y[3],
"level": y[2],
"name": y[7],

View File

@@ -19,7 +19,7 @@ class Config():
游戏API地址前缀
Game API's URL prefix
'''
GAME_API_PREFIX = '/merikuri/17'
GAME_API_PREFIX = '/years/19'
'''
--------------------
'''
@@ -30,7 +30,7 @@ class Config():
Allowed game versions
If it is blank, all are allowed.
'''
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.11.2', '3.11.2c']
ALLOW_APPVERSION = ['3.5.3', '3.5.3c', '3.12.6', '3.12.6c']
'''
--------------------
'''
@@ -46,6 +46,17 @@ class Config():
--------------------
'''
'''
--------------------
联机功能地址,留空则自动获取
Link Play address
If left blank, it will be obtained automatically.
'''
LINK_PLAY_HOST = '' # ***.com
'''
--------------------
'''
'''
--------------------
SSL证书路径
@@ -225,7 +236,7 @@ class Config():
'''
--------------------
是否全解锁世界场景
If unlocking all world scenerys is enabled
If unlocking all world sceneries is enabled
'''
WORLD_SCENERY_FULL_UNLOCK = True
'''

View File

@@ -20,4 +20,4 @@
{% endfor %} {% block content %}{% endblock %}
</section>
<footer id="footer" class="footer">Made by Lost@2020-2021</footer>
<footer id="footer" class="footer">Made by Lost@2020-2022</footer>

View File

@@ -27,6 +27,10 @@
<span>Discount to: </span>
<span class="char-num">{{item['discount_to']}}</span>
<br />
<span>Whether it is allowed to purchase with exchange certificate: </span>
<span class="char-num">{{item['discount_reason']}}</span>
<br />
{% if item['items'] %}<br />
{% for x in item['items'] %}
<span>Item id: </span>

View File

@@ -27,6 +27,7 @@
<option value='core'>Character core</option>
<option value='fragment'>Fragment</option>
<option value='memory'>Memory</option>
<option value='anni5tix'>Anniversary 5 ticket</option>
</select>
</div>
<label for="amount">Amount</label>

View File

@@ -16,6 +16,10 @@
<input type="datetime-local" name="discount_from" id="discount_from">
<label for="discount_to">Discount to</label>
<input type="datetime-local" name="discount_to" id="discount_to">
<div>是否允许使用兑换券购买 Whether it is allowed to purchase with Anniversary 5 ticket:
<label><input type="radio" name="discount_reason" value="0">No</label>
<label><input type="radio" name="discount_reason" value="anni5tix">Yes</label>
</div>
<div class="content">时间填写是一个HTML5控件</div>
<div class="content">Time filling is an HTML5 control.</div>
<br />

View File

@@ -38,6 +38,7 @@
<option value='core'>Character core</option>
<option value='fragment'>Fragment</option>
<option value='memory'>Memory</option>
<option value='anni5tix'>Anniversary 5 ticket</option>
</select>
</div>
<label for="amount">Amount</label>

View File

@@ -418,7 +418,7 @@ def all_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',
'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']
'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)
@@ -764,6 +764,7 @@ def change_purchase():
orig_price = request.form['orig_price']
discount_from = request.form['discount_from']
discount_to = request.form['discount_to']
discount_reason = request.form['discount_reason']
if price:
price = int(price)
@@ -783,6 +784,10 @@ def change_purchase():
discount_to, "%Y-%m-%dT%H:%M"))) * 1000
else:
discount_to = -1
if not discount_reason:
discount_reason = ''
except:
error = '数据错误 Wrong data.'
flash(error)
@@ -792,8 +797,8 @@ def change_purchase():
c.execute(
'''select exists(select * from purchase where purchase_name=:a)''', {'a': purchase_name})
if c.fetchone() == (0,):
c.execute('''insert into purchase values(?,?,?,?,?)''',
(purchase_name, price, orig_price, discount_from, discount_to))
c.execute('''insert into purchase values(?,?,?,?,?,?)''',
(purchase_name, price, orig_price, discount_from, discount_to, discount_reason))
flash('购买项目添加成功 Successfully add the purchase.')
else:

View File

@@ -288,6 +288,7 @@ def get_all_purchase():
discount_from = None
discount_to = None
discount_reason = 'Yes' if i[5] == 'anni5tix' else 'No'
if i[3] and i[3] >= 0:
discount_from = time.strftime(
@@ -302,13 +303,15 @@ def get_all_purchase():
items = []
if y:
for j in y:
items.append({'item_id': j[1], 'type': j[2], 'amount':j[3]})
items.append(
{'item_id': j[1], 'type': j[2], 'amount': j[3]})
re.append({'purchase_name': i[0],
'price': i[1],
'orig_price': i[2],
'discount_from': discount_from,
'discount_to': discount_to,
'discount_reason': discount_reason,
'items': items
})