mirror of
https://git.ethanthesleepy.one/ethanaobrien/ew
synced 2026-08-26 15:02:18 +08:00
Compare commits
4 Commits
ed195abb3b
...
50ccecd821
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50ccecd821 | ||
|
|
e41d4e61b9 | ||
|
|
fcaecc7b41 | ||
|
|
0bf1262b5b |
@@ -95,8 +95,11 @@ pub fn bump_revision() {
|
||||
// + 異次元フェス) along with bgm 310090xx/320090xx, so the custom range gets
|
||||
// the 5-digit space to itself (10000 -> bgm 31010000/32010000). Ids are never
|
||||
// reused after a delete, so a client's cached copy of a dead id can't get
|
||||
// confused with a new upload
|
||||
// confused with a new upload. Stock live ids (master_live_id) are 7-digit
|
||||
// (>= 1_000_000), so the custom range stops below them - a stock clear must
|
||||
// never be mistaken for a deleted custom song
|
||||
pub const FIRST_MUSIC_ID: i64 = 10000;
|
||||
pub const LAST_MUSIC_ID: i64 = 999_999;
|
||||
|
||||
pub fn next_music_id() -> i64 {
|
||||
let issued = DATABASE.lock_and_select("SELECT last_music_id FROM revision WHERE id=1", params!()).unwrap_or_default().parse::<i64>().unwrap_or(0);
|
||||
@@ -252,7 +255,7 @@ pub fn dead_music_ids(candidates: &JsonValue) -> JsonValue {
|
||||
let mut ids: Vec<i64> = Vec::new();
|
||||
for id in candidates.members() {
|
||||
let Some(id) = id.as_i64() else { continue; };
|
||||
if id >= FIRST_MUSIC_ID && !ids.contains(&id) {
|
||||
if id >= FIRST_MUSIC_ID && id <= LAST_MUSIC_ID && !ids.contains(&id) {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,13 @@ static ASSET_VERSIONS: &[AssetVersion] = &[
|
||||
AssetVersion { region: "GL", platform: "Android", version: "5260ff15dff8ba0c00ad91400f515f55", hash: "d210b28037885f3ef56b8f8aa45ac95b", latest: true },
|
||||
AssetVersion { region: "GL", platform: "iOS", version: "5260ff15dff8ba0c00ad91400f515f55", hash: "dd7175e4bcdab476f38c33c7f34b5e4d", latest: true },
|
||||
|
||||
// Re-written client versions 2.0.0 - 2.1.2
|
||||
// Re-written client versions 2.0.0 - 2.1.2 (windows only)
|
||||
AssetVersion { region: "JP", platform: "Windows", version: "4c921d2443335e574a82e04ec9ea243c", hash: "4ed1d077df2d1b29e17d25d64fb37242", latest: false },
|
||||
|
||||
// Re-written client versions 2.2.0 -
|
||||
AssetVersion { region: "JP", platform: "Windows", version: "ced44f266b4e4c8eb05fe417fd5f3d1b", hash: "52a35644bacaefe8f0ae71a1f4d1c217", latest: true },
|
||||
|
||||
// For the unity editor
|
||||
AssetVersion { region: "JP", platform: "Android", version: "ced44f266b4e4c8eb05fe417fd5f3d1b", hash: "0f09d2da7580a6e7dfe44b6094a0e433", latest: false },
|
||||
AssetVersion { region: "JP", platform: "Windows", version: "ced44f266b4e4c8eb05fe417fd5f3d1b", hash: "765a689bd642f3c4545d8d8e69c57d3e", latest: true },
|
||||
AssetVersion { region: "JP", platform: "Android", version: "ced44f266b4e4c8eb05fe417fd5f3d1b", hash: "5ba946aef8cc3fbf95867dc465cad336", latest: false },
|
||||
AssetVersion { region: "JP", platform: "iOS", version: "ced44f266b4e4c8eb05fe417fd5f3d1b", hash: "6cfbebbe32a5cd9391f4b44d1a384f62", latest: false },
|
||||
|
||||
//AssetVersion { region: "JP", platform: "WebGL", version: "4c921d2443335e574a82e04ec9ea243c", hash: "e1ff7c74b20c8d216507972b6f24b9df", latest: true },
|
||||
];
|
||||
|
||||
@@ -761,3 +761,104 @@ async fn end(req: HttpRequest, body: String) -> impl Responder {
|
||||
async fn skip(req: HttpRequest, body: String) -> impl Responder {
|
||||
global::api(&req, Some(live_end(&req, &body, true)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_web::test::TestRequest;
|
||||
|
||||
const STOCK_LIVE_ID: i64 = 1100101;
|
||||
|
||||
fn register_account(token: &str) {
|
||||
let mut user = userdata::get_acc(token);
|
||||
user["tutorial_step"] = 130.into();
|
||||
userdata::save_acc(token, user);
|
||||
}
|
||||
|
||||
fn record_clear(token: &str, live_id: i64, level: i64, score: i64, combo: i64) {
|
||||
let mut user = userdata::get_acc(token);
|
||||
update_live_data(&mut user, &object!{
|
||||
master_live_id: live_id,
|
||||
level: level,
|
||||
live_score: { score: score, max_combo: combo }
|
||||
}, true);
|
||||
userdata::save_acc(token, user);
|
||||
}
|
||||
|
||||
fn pulled_clear_count(token: &str, live_id: i64) -> i64 {
|
||||
get_clear_count(live_id, &userdata::get_acc(token))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_count_persists_with_custom_songs_disabled() {
|
||||
let _lock = crate::runtime::lock_test_data_path();
|
||||
crate::runtime::set_enable_custom_songs(false);
|
||||
|
||||
let token = "cc_control_disabled";
|
||||
register_account(token);
|
||||
|
||||
record_clear(token, STOCK_LIVE_ID, 4, 500000, 320);
|
||||
assert_eq!(pulled_clear_count(token, STOCK_LIVE_ID), 1);
|
||||
|
||||
record_clear(token, STOCK_LIVE_ID, 4, 480000, 300);
|
||||
assert_eq!(pulled_clear_count(token, STOCK_LIVE_ID), 2);
|
||||
|
||||
let user = userdata::get_acc(token);
|
||||
let live = user["live_list"].members().find(|l| l["master_live_id"] == STOCK_LIVE_ID).unwrap();
|
||||
assert_eq!(live["clear_count"], 2);
|
||||
assert_eq!(live["high_score"], 500000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_count_survives_custom_songs_enabled() {
|
||||
let _lock = crate::runtime::lock_test_data_path();
|
||||
crate::runtime::set_enable_custom_songs(true);
|
||||
|
||||
let token = "cc_custom_enabled";
|
||||
register_account(token);
|
||||
|
||||
record_clear(token, STOCK_LIVE_ID, 4, 500000, 320);
|
||||
assert_eq!(pulled_clear_count(token, STOCK_LIVE_ID), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_song_cleanup_keeps_stock_lives() {
|
||||
let _lock = crate::runtime::lock_test_data_path();
|
||||
crate::runtime::set_enable_custom_songs(true);
|
||||
|
||||
let token = "cc_mixed_ids";
|
||||
register_account(token);
|
||||
|
||||
record_clear(token, STOCK_LIVE_ID, 4, 500000, 320);
|
||||
record_clear(token, crate::database::custom_song::FIRST_MUSIC_ID, 1, 100, 10);
|
||||
|
||||
let user = userdata::get_acc(token);
|
||||
assert_eq!(get_clear_count(STOCK_LIVE_ID, &user), 1);
|
||||
}
|
||||
|
||||
fn asset_gate(version: &str, platform: &str, hash: &str) -> Option<i32> {
|
||||
let mut rb = TestRequest::default()
|
||||
.insert_header(("aoharu-asset-version", version.to_string()))
|
||||
.insert_header(("aoharu-platform", platform.to_string()));
|
||||
if !hash.is_empty() {
|
||||
rb = rb.insert_header(("aoharu-asset-hash", hash.to_string()));
|
||||
}
|
||||
let req = rb.to_http_request();
|
||||
global::check_asset_headers(req.headers(), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_gate_is_platform_conditional() {
|
||||
let _lock = crate::runtime::lock_test_data_path();
|
||||
let current = "ced44f266b4e4c8eb05fe417fd5f3d1b";
|
||||
let stock = "4c921d2443335e574a82e04ec9ea243c";
|
||||
|
||||
assert_eq!(asset_gate(current, "Windows", ""), None);
|
||||
assert_eq!(asset_gate(current, "android", ""), None);
|
||||
assert_eq!(asset_gate(current, "ios", ""), Some(global::RESULT_GAME_VERSION_UPDATED));
|
||||
|
||||
assert_eq!(asset_gate(stock, "android", ""), None);
|
||||
assert_eq!(asset_gate(stock, "ios", ""), None);
|
||||
assert_eq!(asset_gate(stock, "Windows", ""), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ fn remove_deleted_custom_songs(user: &mut JsonValue) -> bool {
|
||||
for key in ["live_list", "live_mission_list"] {
|
||||
for data in user[key].members() {
|
||||
let id = data["master_live_id"].as_i64().unwrap_or(0);
|
||||
if id >= custom_song::FIRST_MUSIC_ID && !candidates.contains(id) {
|
||||
if id >= custom_song::FIRST_MUSIC_ID && id <= custom_song::LAST_MUSIC_ID && !candidates.contains(id) {
|
||||
candidates.push(id).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -672,7 +672,10 @@ mod tests {
|
||||
let private_id = custom_song::next_music_id();
|
||||
custom_song::insert_song(private_id, 1, &object!{music_id: private_id}, "private", &array![], false);
|
||||
|
||||
for id in [1001, deleted_id, private_id] {
|
||||
// 1100101 is a real stock live-id shape (7-digit, >= FIRST_MUSIC_ID): it
|
||||
// must survive the custom-song wipe, unlike an unrealistic sub-10000 id
|
||||
let stock_id = 1100101;
|
||||
for id in [stock_id, deleted_id, private_id] {
|
||||
user["live_list"].push(object!{
|
||||
master_live_id: id,
|
||||
level: 4,
|
||||
@@ -699,7 +702,7 @@ mod tests {
|
||||
assert!(!user["live_list"].members().any(|data| data["master_live_id"] == deleted_id));
|
||||
assert!(!user["live_mission_list"].members().any(|data| data["master_live_id"] == deleted_id));
|
||||
// Official records are untouchable, invisible-but-alive songs survive
|
||||
assert!(user["live_list"].members().any(|data| data["master_live_id"] == 1001));
|
||||
assert!(user["live_list"].members().any(|data| data["master_live_id"] == stock_id));
|
||||
assert!(user["live_list"].members().any(|data| data["master_live_id"] == private_id));
|
||||
assert_eq!(user["live_list"].len(), 2);
|
||||
assert_eq!(user["live_mission_list"].len(), 2);
|
||||
|
||||
Reference in New Issue
Block a user