scroll.server/crates/game-service/tests/achievements_and_levels.rs
WiseDev a585b0c18d resolve the deploy birth tick and stop wedging the client's own commands
- a card lands one tick after its command fires, since the client
  hashes after LogicTime::increaseTick, not before
- answer RequestSectorState immediately except in the two ticks after
  a firing tick, where a full update would teleport the client past
  the command it just queued - the old blanket refusal left every
  card the player tapped silently deleted while it held
- carry both pending commands and queued deploys in the same delivery
  ledger so the quiet window covers what the player actually paid for
- add the regression tests for the birth tick and the deferred answer
2026-08-28 16:27:16 +03:00

97 lines
3.4 KiB
Rust

use game_service::home::build_avatar;
use logic::commands::COMMODITY_ACHIEVEMENT_PROGRESS;
use logic::data::{table, LogicDataRef, LogicDataTables};
use logic::home::LogicHomeMode;
use logic::model::LogicClientHome;
use logic::{CommandOutcome, LogicClaimAchievementRewardCommand, LogicCommandHeader};
use std::path::Path;
use std::sync::Arc;
fn tables() {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
if let Ok(t) = LogicDataTables::load_from_dir(&root) {
LogicDataTables::install(Arc::new(t));
}
}
#[test]
fn adding_exp_rolls_over_into_levels() {
tables();
let mut home = LogicHomeMode::new(LogicClientHome::default(), Default::default(), 0);
home.avatar_mut().exp_level = 1;
home.avatar_mut().exp_points = 0;
home.add_exp(25);
assert_eq!(home.avatar().exp_level, 2, "25 exp should reach level 2");
assert_eq!(
home.avatar().exp_points,
5,
"5 exp should carry into level 2"
);
home.add_exp(50);
assert_eq!(
home.avatar().exp_level,
3,
"another 50 should reach level 3"
);
assert_eq!(home.avatar().exp_points, 5);
}
#[test]
fn a_completed_findcard_achievement_can_be_claimed() {
tables();
let tabs = LogicDataTables::instance();
let achievements = tabs.table(table::ACHIEVEMENTS).unwrap();
let (name, required) = (0..achievements.count())
.filter_map(|i| achievements.get_at(i))
.find(|row| row.string("Action") == "findcard")
.map(|row| (row.string("Name").to_owned(), row.int("ActionCount")))
.expect("a findcard achievement exists");
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets");
let catalog = game_service::Catalog::load(Some(&root));
let mut profile = game_service::store::PlayerProfile::starter(
service_rpc::AccountRef::new(0, 5),
&game_service::StarterProfile::default(),
&catalog,
);
profile.collection.clear();
profile.deck.clear();
let tabs2 = LogicDataTables::instance();
let cards = tabs2.table(table::CHARACTERS).unwrap();
for i in 0..(required.max(1) as usize) {
let row = cards.get_at(i % cards.count()).unwrap();
profile.collection.push(game_service::store::OwnedCard {
card: LogicDataRef::Resolved(Arc::clone(row)),
level_index: 0,
count: 1,
});
}
let avatar = build_avatar(&profile);
let progress = avatar
.commodities
.types
.get(COMMODITY_ACHIEVEMENT_PROGRESS)
.map(|slots| slots.iter().filter(|s| s.count >= required).count())
.unwrap_or(0);
assert!(
progress > 0,
"the served progress should complete a findcard tier"
);
let mut home = LogicHomeMode::new(LogicClientHome::default(), avatar, 0);
let before_level = home.avatar().exp_level;
let before_points = home.avatar().exp_points;
let command = LogicClaimAchievementRewardCommand {
achievement: LogicDataRef::by_name(table::ACHIEVEMENTS, &name),
header: LogicCommandHeader::default(),
};
let outcome = home.execute(&command);
assert_eq!(
outcome,
CommandOutcome::Applied,
"the claim should be accepted"
);
assert!(
home.avatar().exp_points != before_points || home.avatar().exp_level != before_level,
"claiming should have granted exp"
);
assert!(matches!(
home.execute(&command),
CommandOutcome::Rejected(_)
));
}