From f05520b9589de4cccd68171b64dc553a53fb282f Mon Sep 17 00:00:00 2001 From: UnitedAirforce Date: Sat, 16 Aug 2025 11:17:02 +0800 Subject: [PATCH] Bug fixing and ready for Extra Challenge --- README.md | 8 ++--- api/misc.py | 4 +-- api/play.py | 2 +- api/ranking.py | 9 ++++-- api/shop.py | 76 +++++++++++++++++++++++++++++++++++++++++------- api/templates.py | 2 +- api/user.py | 2 +- files/style.css | 42 ++++++++++++++++++++++++++ 8 files changed, 122 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9ba34cb..3d0ecc8 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,9 @@ If you'd like to upload it somewhere else and contribute the link, please contac Download `common.zip` and a platform of your choosing. Unzip the folders within to the private server root directory. If in doubt, check the `File Structure` section at the end. -### GC4MAX Expansion +### GC4MAX Expansion and Extra Challenge Expansion -To install the `GC4MAX` expansion, which ports arcade exclusive tracks, avatars, and skins to 2OS, download the `4max expansion` folder's `common.zip` and a platform of your choosing. Unzip the folders within to the privates server root directory. Overwrite existing files. Note that the included installation `apk` or `ipa` must be used for correct avatar rendering. +To install the `GC4MAX Expansion` (which includes the `Extra Challenge Expansion`), which ports arcade exclusive tracks, avatars, and skins to 2OS, download the `4max expansion` folder's `common.zip` and a platform of your choosing. Unzip the folders within to the privates server root directory. Overwrite existing files. Note that the included installation `apk` or `ipa` must be used for correct avatar rendering. Edit `config.py`'s `MODEL`, `TUNEFILE`, and `SKIN` value to match the `pak`'s timestamp. `paks` can be found at `/files/gc2/`. @@ -313,9 +313,9 @@ Note that this data is for analytics only, and the functionality to embed this d 下载 `common.zip` 和您设备的平台。将里面的所有文件夹解压到服务器根目录。如有疑惑,请参照文末`文件结构`章节。 -## GC4MAX扩展包 +## GC4MAX扩展包和Extra Challenge扩展包 -如想下载`GC4MAX`扩展包(包含了街机独占曲目,皮肤,和角色),下载`4max expansion`里的`common.zip`和和您设备的平台。将里面的所有文件夹解压到服务器根目录。如有重复,覆盖所有文件。请注意,必须使用包含的`apk`或`ipa`安装包来正确渲染角色。 +如想下载`GC4MAX扩展包`(内含`Extra Challenge扩展包`)(包含了街机独占曲目,皮肤,和角色),下载`4max expansion`里的`common.zip`和和您设备的平台。将里面的所有文件夹解压到服务器根目录。如有重复,覆盖所有文件。请注意,必须使用包含的`apk`或`ipa`安装包来正确渲染角色。 修改`config.py`的`MODEL`, `TUNEFILE`, and `SKIN` 值至现有`pak`文件的时间戳. `paks`文件的位置在`/files/gc2/`. diff --git a/api/misc.py b/api/misc.py index e07c1ec..e5ce581 100644 --- a/api/misc.py +++ b/api/misc.py @@ -36,12 +36,12 @@ def get_4max_version_string(): def parse_res(res): parsed_data = [] - if isinstance(res, int): + if isinstance(res, int) or res == None: return "Failed to fetch version info: Error " + str(res) for item in res: if item.get("isOpen"): - version = item.get("version", "Unknown Version") + version = item.get("version", 0) changelog = "
".join(item.get("changeLog", {}).get("en", [])) parsed_data.append(f"Version: {version}

Changelog:
{changelog}

") return "".join(parsed_data) diff --git a/api/play.py b/api/play.py index 439f90e..253e51c 100644 --- a/api/play.py +++ b/api/play.py @@ -8,7 +8,7 @@ import xml.etree.ElementTree as ET from config import ROOT_FOLDER, START_COIN, COIN_REWARD, AUTHORIZATION_NEEDED -from api.database import database, user, daily_reward, result, result, check_blacklist, check_whitelist +from api.database import database, user, daily_reward, result, check_blacklist, check_whitelist from api.crypt import decrypt_fields from api.templates import START_STAGES, EXP_UNLOCKED_SONGS diff --git a/api/ranking.py b/api/ranking.py index cc0af7d..014af05 100644 --- a/api/ranking.py +++ b/api/ranking.py @@ -248,7 +248,10 @@ async def ranking_detail(request: Request): play_record = None if user_id: - play_record = next((record for record in play_results if int(record[3]) == user_id), None) + play_record = play_record = next( + (record for record in play_results if record[3] not in (None, '') and int(record[3]) == user_id), + None + ) if not play_record: play_record = next((record for record in play_results if record[1] == device_id and record[3] is None), None) @@ -257,10 +260,10 @@ async def ranking_detail(request: Request): avatar_index = str(play_record[7]) if play_record else "1" user_score = play_record[8] if play_record else 0 for rank, result_obj in enumerate(play_results, start=1): - if user_result and int(result_obj[3]) == user_id: + if user_result and result_obj[3] not in (None, '') and int(result_obj[3]) == user_id: player_rank = rank break - elif result_obj[1] == device_id and result_obj[3] is None: + elif result_obj[1] == device_id and result_obj[3] in (None, ''): player_rank = rank break diff --git a/api/shop.py b/api/shop.py index 930f468..3ab5c15 100644 --- a/api/shop.py +++ b/api/shop.py @@ -7,7 +7,7 @@ import math from sqlalchemy import select, update import xml.etree.ElementTree as ET -from config import START_COIN, AUTHORIZATION_NEEDED, STAGE_PRICE, START_COIN, AVATAR_PRICE, ITEM_PRICE +from config import START_COIN, AUTHORIZATION_NEEDED, STAGE_PRICE, START_COIN, AVATAR_PRICE, ITEM_PRICE, FMAX_PRICE, EX_PRICE from api.crypt import decrypt_fields from api.misc import inform_page, parse_res, FMAX_VER, FMAX_RES @@ -46,7 +46,7 @@ async def web_shop(request: Request): if cnt_type == "1": low_range = 100 - up_range = 616 + up_range = 615 if page < 0 or page > math.ceil(up_range - low_range) / 80: return HTMLResponse("""

Invalid page number

""", status_code=400) @@ -59,17 +59,31 @@ async def web_shop(request: Request): low_range = low_range + page * 80 up_range = min(up_range, low_range + 80) - if 700 not in my_stage and os.path.isfile('./files/dlc_4max.html'): + if 700 not in my_stage and os.path.isfile('./files/dlc_4max.html') and not spawn_prev_page: buttons_html += """ - +
""" fmax_inc = 1 - elif 700 in my_stage and os.path.isfile('./files/dlc_4max.html'): + elif 700 in my_stage and os.path.isfile('./files/dlc_4max.html') and not spawn_prev_page: + buttons_html += """ + + +
+ """ + + if 980 not in my_stage and os.path.isfile('./files/dlc_extra.html') and not spawn_prev_page: buttons_html += """ - + +
+ """ + fmax_inc = 1 + elif 980 in my_stage and os.path.isfile('./files/dlc_4max.html') and not spawn_prev_page: + buttons_html += """ + +
""" @@ -204,7 +218,7 @@ async def web_shop_detail(request: Request): {song_stage_price} """ - elif cnt_id == -2: + elif cnt_id == -3: log = parse_res(FMAX_RES) html = f"""
@@ -218,6 +232,36 @@ async def web_shop_detail(request: Request):

{log}


""" + elif cnt_id == -4: + html = f""" +
+

You have unlocked the EXTRA Challenge!

+

Please report bugs/missing tracks to Discord: #AnTcfgss, or QQ 3421587952.

+ +
+ """ + elif cnt_id == -2: + html = f""" +
+

Brace the Ultimate - Extra - Challenge.

+

170+ Arcade Extra difficulty charts await you.

+

You have been warned.

+
+ + +

+ + """ elif cnt_id == -1: html = f"""
@@ -230,7 +274,7 @@ async def web_shop_detail(request: Request): Buy
Coin Icon - 300 + {FMAX_PRICE}


@@ -279,8 +323,10 @@ async def web_shop_detail(request: Request): else: html = "

Item not found.

" - if cnt_type == "1" and cnt_id < 0: + if cnt_type == "1" and (cnt_id == -1 or cnt_id == -3): source_html = f"files/dlc_4max.html" + elif cnt_type == "1" and (cnt_id == -2 or cnt_id == -4): + source_html = f"files/dlc_extra.html" else: source_html = f"files/web_shop_detail.html" html += f""" @@ -331,11 +377,19 @@ async def buy_by_coin(request: Request): if cnt_type == "1": if cnt_id == -1: - song_stage_price = 300 + song_stage_price = FMAX_PRICE if coin < song_stage_price: return Response(fail_url, media_type="application/xml") - for i in range(616, 950): + for i in range(615, 926): + my_stage.add(i) + coin -= song_stage_price + elif cnt_id == -2: + song_stage_price = EX_PRICE + if coin < song_stage_price: + return Response(fail_url, media_type="application/xml") + + for i in range(926, 985): my_stage.add(i) coin -= song_stage_price else: diff --git a/api/templates.py b/api/templates.py index 8d09c5e..ec55552 100644 --- a/api/templates.py +++ b/api/templates.py @@ -11,7 +11,7 @@ START_STAGES = [7,23,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,55,56,57 START_AVATARS = [] -EXCLUDE_STAGE_EXP = [121,134,166,167,168,169,170,213,214,215,225,277,397] # 134 and 170 unoccupied dummy tracks (filled with Departure -Remix-), +EXCLUDE_STAGE_EXP = [121,134,166,167,168,169,170,213,214,215,225,277,396] # 134 and 170 unoccupied dummy tracks (filled with Departure -Remix-), #121 (and 93-96 lady gaga songs) removed (can be enabled by patching stageParam:isAvailable, or change the last byte before next song's name - 1 from 01 to 03 in stage_param.dat. # Rest are exp unlocked songs. EXCLUDE_AVATAR_EXP = [28,29] diff --git a/api/user.py b/api/user.py index 51fe4c4..d5b7c91 100644 --- a/api/user.py +++ b/api/user.py @@ -606,7 +606,7 @@ async def bonus(request: Request): await database.execute(update_query) elif cnt_type == 2: - avatars = set(json.loads(my_avatar)) if my_avatar else set() + avatars = set(my_avatar) if my_avatar else set() if cnt_id not in avatars: avatars.add(cnt_id) my_avatar = json.dumps(list(avatars)) diff --git a/files/style.css b/files/style.css index 0a5a72d..ab96a04 100644 --- a/files/style.css +++ b/files/style.css @@ -13,11 +13,26 @@ margin: 0px auto; padding: 20px; } +.dlc_container_extra { + max-width: 800px; + height: 70vh; + margin: 0px auto; + padding: 20px; +} .dlc_logo { width: 100%; max-width: 500px; margin-bottom: 10px; } +.dlc_extra_body { + background: url('/files/web/extra_bg.jpg') no-repeat center center fixed; + background-size: cover; + color: red; + font-family: Arial, sans-serif; + text-align: center; + margin: 0; + padding: 0; +} .text-content { font-size: 18px; line-height: 1.6; @@ -50,6 +65,33 @@ cursor: pointer; position: relative; } +.buy-button-extra { + display: inline-block; + width: 160px; + height: 110px; + background: url('/files/web/frame_buy_extra.png') no-repeat center center; + background-size: contain; + border: none; + color: white; + font-size: 22px; + font-weight: bold; + padding: 15px; + cursor: pointer; + position: relative; +} +.quit-button-extra { + display: inline-block; + width: 150px; + height: 30px; + background: url('/files/web/quit_button_extra.png') no-repeat center center; + background-size: contain; + border: none; + color: white; + font-size: 20px; + font-weight: bold; + cursor: pointer; + position: relative; +} .coin-container { display: flex; justify-content: center;