mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2025-12-13 23:56:25 +08:00
[Enhance][Bug fix] Another bundle mode & skill_amane bug
- For Arcaea 5.6.0 - Fix a bug that `skill_amane` may arise error when the step type of world map is null. - Add a new bundle update mode, which is same with official server, that the server will find a update path from old version to new version, ignoring the application version restrictions.
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -21,3 +21,9 @@ latest version/config.py
|
||||
|
||||
# song data
|
||||
latest version/database/songs/
|
||||
latest version/database/bundle/
|
||||
|
||||
!latest version/database/bundle/README.md
|
||||
|
||||
# backup
|
||||
latest version/database/backup/
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from time import time
|
||||
|
||||
from flask import url_for
|
||||
|
||||
from .config_manager import Config
|
||||
from .constant import Constant
|
||||
from .error import NoAccess, RateLimit
|
||||
from .error import NoAccess, NoData, RateLimit
|
||||
from .limiter import ArcLimiter
|
||||
|
||||
|
||||
@@ -44,6 +46,8 @@ class ContentBundle:
|
||||
x.prev_version = json_data['previousVersionNumber']
|
||||
x.app_version = json_data['applicationVersionNumber']
|
||||
x.uuid = json_data['uuid']
|
||||
if x.prev_version is None:
|
||||
x.prev_version = '0.0.0'
|
||||
return x
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
@@ -69,8 +73,14 @@ class BundleParser:
|
||||
|
||||
# {app_version: [ List[ContentBundle] ]}
|
||||
bundles: 'dict[str, list[ContentBundle]]' = {}
|
||||
# {app_version: max bundle version}
|
||||
max_bundle_version: 'dict[str, str]' = {}
|
||||
|
||||
# {bundle version: [next versions]} 宽搜索引
|
||||
next_versions: 'dict[str, list[str]]' = {}
|
||||
# {(bver, b prev version): ContentBundle} 正向索引
|
||||
version_tuple_bundles: 'dict[tuple[str, str], ContentBundle]' = {}
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.parse()
|
||||
|
||||
@@ -106,15 +116,57 @@ class BundleParser:
|
||||
f'Bundle file not found: {bundle_path}')
|
||||
x.calculate_size()
|
||||
|
||||
if x.app_version not in self.bundles:
|
||||
self.bundles[x.app_version] = []
|
||||
self.bundles[x.app_version].append(x)
|
||||
self.bundles.setdefault(x.app_version, []).append(x)
|
||||
|
||||
self.version_tuple_bundles[(x.version, x.prev_version)] = x
|
||||
self.next_versions.setdefault(
|
||||
x.prev_version, []).append(x.version)
|
||||
|
||||
# sort by version
|
||||
for k, v in self.bundles.items():
|
||||
v.sort(key=lambda x: x.version_tuple)
|
||||
self.max_bundle_version[k] = v[-1].version
|
||||
|
||||
@staticmethod
|
||||
@lru_cache(maxsize=128)
|
||||
def get_bundles(app_ver: str, b_ver: str) -> 'list[ContentBundle]':
|
||||
if Config.BUNDLE_STRICT_MODE:
|
||||
return BundleParser.bundles.get(app_ver, [])
|
||||
|
||||
k = b_ver if b_ver else '0.0.0'
|
||||
|
||||
target_version = BundleParser.max_bundle_version.get(app_ver, '0.0.0')
|
||||
if k == target_version:
|
||||
return []
|
||||
|
||||
# BFS
|
||||
q = [[k]]
|
||||
ans = None
|
||||
while True:
|
||||
qq = []
|
||||
for x in q:
|
||||
if x[-1] == target_version:
|
||||
ans = x
|
||||
break
|
||||
for y in BundleParser.next_versions.get(x[-1], []):
|
||||
if y in x:
|
||||
continue
|
||||
qq.append(x + [y])
|
||||
|
||||
if ans is not None or not qq:
|
||||
break
|
||||
q = qq
|
||||
|
||||
if not ans:
|
||||
raise NoData(
|
||||
f'No bundles found for app version: {app_ver}, bundle version: {b_ver}', status=404)
|
||||
|
||||
r = []
|
||||
for i in range(1, len(ans)):
|
||||
r.append(BundleParser.version_tuple_bundles[(ans[i], ans[i-1])])
|
||||
|
||||
return r
|
||||
|
||||
|
||||
class BundleDownload:
|
||||
|
||||
@@ -134,8 +186,9 @@ class BundleDownload:
|
||||
self.device_id = device_id
|
||||
|
||||
def get_bundle_list(self) -> list:
|
||||
bundles: 'list[ContentBundle]' = BundleParser.bundles.get(
|
||||
self.client_app_version, [])
|
||||
bundles: 'list[ContentBundle]' = BundleParser.get_bundles(
|
||||
self.client_app_version, self.client_bundle_version)
|
||||
|
||||
if not bundles:
|
||||
return []
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ class Config:
|
||||
OLD_GAME_API_PREFIX = [] # str | list[str]
|
||||
|
||||
ALLOW_APPVERSION = [] # list[str]
|
||||
|
||||
BUNDLE_STRICT_MODE = True
|
||||
|
||||
SET_LINKPLAY_SERVER_AS_SUB_PROCESS = True
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from .config_manager import Config
|
||||
|
||||
ARCAEA_SERVER_VERSION = 'v2.11.3.9'
|
||||
ARCAEA_SERVER_VERSION = 'v2.11.3.10'
|
||||
ARCAEA_LOG_DATBASE_VERSION = 'v1.1'
|
||||
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ class Step:
|
||||
self.restrict_ids = d.get('restrict_ids')
|
||||
self.restrict_type = d.get('restrict_type')
|
||||
self.restrict_difficulty = d.get('restrict_difficulty')
|
||||
self.step_type = d.get('step_type')
|
||||
self.step_type = d.get('step_type', [])
|
||||
self.speed_limit_value = d.get('speed_limit_value')
|
||||
self.plus_stamina_value = d.get('plus_stamina_value')
|
||||
if 'items' in d:
|
||||
|
||||
@@ -70,7 +70,7 @@ class InitData:
|
||||
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful', 'core_course_skip_purchase', 'core_umbral', 'core_wacca', 'core_sunset', 'core_tanoc']
|
||||
|
||||
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
|
||||
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv', 'freefall3', 'partyvinyl3', 'tsukinimurakumo', 'mantis', 'worldfragments', 'astrawalkthrough', 'chronicle', 'trappola3', 'letsrock', 'shadesoflight3', 'teriqma3', 'impact3', 'lostemotion', 'gimmick', 'lawlesspoint', 'hybris', 'ultimatetaste', 'rgb', 'matenrou', 'dynitikos', 'amekagura', 'fantasy', 'aloneandlorn', 'felys', 'onandon', 'hotarubinoyuki', 'oblivia3', 'libertas3', 'einherjar3', 'purpleverse3', 'viciousheroism3', 'inkarusi3', 'cyberneciacatharsis3', 'alephzero', 'hellohell', 'ichirin', 'awakeninruins', 'morningloom', 'lethalvoltage']
|
||||
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv', 'freefall3', 'partyvinyl3', 'tsukinimurakumo', 'mantis', 'worldfragments', 'astrawalkthrough', 'chronicle', 'trappola3', 'letsrock', 'shadesoflight3', 'teriqma3', 'impact3', 'lostemotion', 'gimmick', 'lawlesspoint', 'hybris', 'ultimatetaste', 'rgb', 'matenrou', 'dynitikos', 'amekagura', 'fantasy', 'aloneandlorn', 'felys', 'onandon', 'hotarubinoyuki', 'oblivia3', 'libertas3', 'einherjar3', 'purpleverse3', 'viciousheroism3', 'inkarusi3', 'cyberneciacatharsis3', 'alephzero', 'hellohell', 'ichirin', 'awakeninruins', 'morningloom', 'lethalvoltage', 'leaveallbehind', 'desive']
|
||||
|
||||
world_unlocks = ["scenery_chap1", "scenery_chap2",
|
||||
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6", "scenery_chap7", "scenery_beyond"]
|
||||
|
||||
@@ -1720,5 +1720,59 @@
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "distortedfate",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "distortedfate",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 1,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "floatingworld",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "floatingworld",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 1,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"name": "chromafill",
|
||||
"items": [
|
||||
{
|
||||
"type": "single",
|
||||
"id": "chromafill",
|
||||
"is_available": true
|
||||
},
|
||||
{
|
||||
"type": "core",
|
||||
"amount": 1,
|
||||
"id": "core_generic",
|
||||
"is_available": true
|
||||
}
|
||||
],
|
||||
"orig_price": 100,
|
||||
"price": 100
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user