Skip to content

Commit 8fc3835

Browse files
committed
download resources in build script instead of at runtime
1 parent 0723fb6 commit 8fc3835

File tree

6 files changed

+259
-154
lines changed

6 files changed

+259
-154
lines changed

Cargo.toml

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ serde = { version = "1.0.205", features = ["derive"] }
2020
serde_json = "1.0.122"
2121
tracing = "0.1.40"
2222
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] }
23-
ureq = { version = "2.10.1", features = ["json"] }
23+
ureq = { version = "2.10.1" }
24+
reliquary = { git = "https://github.com/IceDynamix/reliquary", tag = "v3.1.0" }
2425

25-
[dependencies.reliquary]
26-
git = "https://github.com/IceDynamix/reliquary"
27-
tag = "v3.1.0"
26+
[build-dependencies]
27+
ureq = { version = "2.10.1", features = ["json"] }
28+
reliquary = { git = "https://github.com/IceDynamix/reliquary", tag = "v3.1.0" }
2829

2930
[profile.release]
3031
opt-level = "z" # optimize for size

build.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use std::env;
2+
use std::fs::File;
3+
use std::path::Path;
4+
5+
use reliquary::resource::excel::{
6+
AvatarConfigMap, AvatarSkillTreeConfigMap, EquipmentConfigMap, MultiplePathAvatarConfigMap,
7+
RelicConfigMap, RelicMainAffixConfigMap, RelicSetConfigMap, RelicSubAffixConfigMap,
8+
};
9+
use reliquary::resource::ResourceMap;
10+
use ureq::serde_json::Value;
11+
12+
const BASE_RESOURCE_URL: &str = "https://raw.githubusercontent.com/Dimbreath/StarRailData/master";
13+
const KEY_URL: &str =
14+
"https://raw.githubusercontent.com/juliuskreutz/stardb-exporter/master/keys.json";
15+
16+
fn main() {
17+
println!("cargo::rerun-if-changed=Cargo.toml");
18+
19+
download_config::<AvatarConfigMap>();
20+
download_config::<AvatarSkillTreeConfigMap>();
21+
download_config::<EquipmentConfigMap>();
22+
download_config::<MultiplePathAvatarConfigMap>();
23+
download_config::<RelicConfigMap>();
24+
download_config::<RelicMainAffixConfigMap>();
25+
download_config::<RelicSetConfigMap>();
26+
download_config::<RelicSubAffixConfigMap>();
27+
28+
download_and_write_to_out(
29+
"TextMapEN.json",
30+
format!("{BASE_RESOURCE_URL}/TextMap/TextMapEN.json").as_str(),
31+
);
32+
download_and_write_to_out("keys.json", KEY_URL);
33+
}
34+
35+
fn download_config<T: ResourceMap>() {
36+
let file_name = T::get_json_name();
37+
38+
let url = format!("{BASE_RESOURCE_URL}/ExcelOutput/{file_name}");
39+
40+
download_and_write_to_out(file_name, &url);
41+
}
42+
43+
fn download_and_write_to_out(file: &str, url: &str) {
44+
// downloaded files are in pretty format, deserialize and serialize
45+
// to compress file size
46+
let value: Value = ureq::get(url).call().unwrap().into_json().unwrap();
47+
48+
let out_dir = env::var_os("OUT_DIR").unwrap();
49+
let out_path = Path::new(&out_dir).join(file);
50+
51+
let mut file = File::create(out_path).unwrap();
52+
53+
ureq::serde_json::to_writer(&mut file, &value).unwrap();
54+
}

src/export/database.rs

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use base64::prelude::BASE64_STANDARD;
2+
use base64::Engine;
3+
use reliquary::resource::excel::{
4+
AvatarConfigMap, AvatarSkillTreeConfigMap, EquipmentConfigMap, MultiplePathAvatarConfigMap,
5+
RelicConfigMap, RelicMainAffixConfigMap, RelicSetConfigMap, RelicSubAffixConfigMap,
6+
};
7+
use reliquary::resource::text_map::TextMap;
8+
use serde::de::DeserializeOwned;
9+
use std::collections::HashMap;
10+
use tracing::{info, instrument};
11+
12+
pub struct Database {
13+
pub avatar_config: AvatarConfigMap,
14+
pub avatar_skill_tree_config: AvatarSkillTreeConfigMap,
15+
pub equipment_config: EquipmentConfigMap,
16+
pub multipath_avatar_config: MultiplePathAvatarConfigMap,
17+
pub relic_config: RelicConfigMap,
18+
pub relic_set_config: RelicSetConfigMap,
19+
pub relic_main_affix_config: RelicMainAffixConfigMap,
20+
pub relic_sub_affix_config: RelicSubAffixConfigMap,
21+
pub text_map: TextMap,
22+
pub keys: HashMap<u32, Vec<u8>>,
23+
}
24+
25+
impl Database {
26+
#[instrument(name = "config_map")]
27+
pub fn new() -> Self {
28+
info!("using local database");
29+
30+
// config files are downloaded by the build script
31+
//
32+
// i *would* create a fn load_local_config<T: ResourceMap + DeserializeOwned>()
33+
// to avoid duplicating the json file names by using T::get_json_name,
34+
// but concat!() only takes string literals. it doesn't even take `&'static str`!!
35+
Database {
36+
avatar_config: Self::parse_json(include_str!(concat!(
37+
env!("OUT_DIR"),
38+
"/AvatarConfig.json"
39+
))),
40+
avatar_skill_tree_config: Self::parse_json(include_str!(concat!(
41+
env!("OUT_DIR"),
42+
"/AvatarSkillTreeConfig.json"
43+
))),
44+
equipment_config: Self::parse_json(include_str!(concat!(
45+
env!("OUT_DIR"),
46+
"/EquipmentConfig.json"
47+
))),
48+
multipath_avatar_config: Self::parse_json(include_str!(concat!(
49+
env!("OUT_DIR"),
50+
"/MultiplePathAvatarConfig.json"
51+
))),
52+
relic_config: Self::parse_json(include_str!(concat!(
53+
env!("OUT_DIR"),
54+
"/RelicConfig.json"
55+
))),
56+
relic_set_config: Self::parse_json(include_str!(concat!(
57+
env!("OUT_DIR"),
58+
"/RelicSetConfig.json"
59+
))),
60+
relic_main_affix_config: Self::parse_json(include_str!(concat!(
61+
env!("OUT_DIR"),
62+
"/RelicMainAffixConfig.json"
63+
))),
64+
relic_sub_affix_config: Self::parse_json(include_str!(concat!(
65+
env!("OUT_DIR"),
66+
"/RelicSubAffixConfig.json"
67+
))),
68+
text_map: Self::parse_json(include_str!(concat!(env!("OUT_DIR"), "/TextMapEN.json"))),
69+
keys: Self::load_local_keys(),
70+
}
71+
}
72+
73+
fn parse_json<T: DeserializeOwned>(str: &'static str) -> T {
74+
serde_json::de::from_str(str).unwrap()
75+
}
76+
77+
fn load_local_keys() -> HashMap<u32, Vec<u8>> {
78+
let keys: HashMap<u32, String> =
79+
Self::parse_json(include_str!(concat!(env!("OUT_DIR"), "/keys.json")));
80+
let mut keys_bytes = HashMap::new();
81+
82+
for (k, v) in keys {
83+
keys_bytes.insert(k, BASE64_STANDARD.decode(v).unwrap());
84+
}
85+
86+
keys_bytes
87+
}
88+
89+
pub(crate) fn lookup_avatar_name(&self, avatar_id: u32) -> Option<String> {
90+
if avatar_id == 0 {
91+
return None;
92+
}
93+
94+
if avatar_id >= 8000 {
95+
Some("Trailblazer".to_owned())
96+
} else {
97+
let cfg = self.avatar_config.get(&avatar_id)?;
98+
cfg.AvatarName.lookup(&self.text_map).map(|s| s.to_string())
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)