mirror of
https://git.ethanthesleepy.one/ethanaobrien/ew
synced 2026-08-26 23:12:20 +08:00
fix clear rate deletion bug
This commit is contained in:
@@ -95,8 +95,11 @@ pub fn bump_revision() {
|
|||||||
// + 異次元フェス) along with bgm 310090xx/320090xx, so the custom range gets
|
// + 異次元フェス) along with bgm 310090xx/320090xx, so the custom range gets
|
||||||
// the 5-digit space to itself (10000 -> bgm 31010000/32010000). Ids are never
|
// 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
|
// 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 FIRST_MUSIC_ID: i64 = 10000;
|
||||||
|
pub const LAST_MUSIC_ID: i64 = 999_999;
|
||||||
|
|
||||||
pub fn next_music_id() -> i64 {
|
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);
|
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();
|
let mut ids: Vec<i64> = Vec::new();
|
||||||
for id in candidates.members() {
|
for id in candidates.members() {
|
||||||
let Some(id) = id.as_i64() else { continue; };
|
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);
|
ids.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -761,3 +761,104 @@ async fn end(req: HttpRequest, body: String) -> impl Responder {
|
|||||||
async fn skip(req: HttpRequest, body: String) -> impl Responder {
|
async fn skip(req: HttpRequest, body: String) -> impl Responder {
|
||||||
global::api(&req, Some(live_end(&req, &body, true)))
|
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 key in ["live_list", "live_mission_list"] {
|
||||||
for data in user[key].members() {
|
for data in user[key].members() {
|
||||||
let id = data["master_live_id"].as_i64().unwrap_or(0);
|
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();
|
candidates.push(id).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -672,7 +672,10 @@ mod tests {
|
|||||||
let private_id = custom_song::next_music_id();
|
let private_id = custom_song::next_music_id();
|
||||||
custom_song::insert_song(private_id, 1, &object!{music_id: private_id}, "private", &array![], false);
|
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!{
|
user["live_list"].push(object!{
|
||||||
master_live_id: id,
|
master_live_id: id,
|
||||||
level: 4,
|
level: 4,
|
||||||
@@ -699,7 +702,7 @@ mod tests {
|
|||||||
assert!(!user["live_list"].members().any(|data| data["master_live_id"] == deleted_id));
|
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));
|
assert!(!user["live_mission_list"].members().any(|data| data["master_live_id"] == deleted_id));
|
||||||
// Official records are untouchable, invisible-but-alive songs survive
|
// 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!(user["live_list"].members().any(|data| data["master_live_id"] == private_id));
|
||||||
assert_eq!(user["live_list"].len(), 2);
|
assert_eq!(user["live_list"].len(), 2);
|
||||||
assert_eq!(user["live_mission_list"].len(), 2);
|
assert_eq!(user["live_mission_list"].len(), 2);
|
||||||
|
|||||||
Reference in New Issue
Block a user