Compare commits

...

2 Commits

Author SHA1 Message Date
355cb36025
换源 2026-06-29 15:37:23 +08:00
03b6afade2
清理线程 2026-06-22 19:12:37 +08:00
5 changed files with 88 additions and 28 deletions

View File

@ -11,7 +11,7 @@ crate-type = ["rlib", "cdylib"]
common = { path = "../common" } common = { path = "../common" }
assets-updater = { path = "../assets-updater" } assets-updater = { path = "../assets-updater" }
tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] } tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread", "sync"] }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
reqwest = { workspace = true, features = ["json"] } reqwest = { workspace = true, features = ["json"] }

View File

@ -102,17 +102,10 @@ pub struct EventCard {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CurrentVersion { pub struct CurrentVersion {
#[serde(rename = "appHash")]
pub app_hash: String, pub app_hash: String,
#[serde(rename = "systemProfile")]
pub system_profile: String,
#[serde(rename = "appVersion")]
pub app_version: String, pub app_version: String,
#[serde(rename = "assetVersion")]
pub asset_version: String, pub asset_version: String,
#[serde(rename = "dataVersion")]
pub data_version: String, pub data_version: String,
#[serde(rename = "assetHash")]
pub asset_hash: String, pub asset_hash: String,
} }

View File

@ -1,10 +1,10 @@
use crate::data::CurrentVersion; use crate::data::CurrentVersion;
use crate::{OPT_CONFIG, STORE, UA}; use crate::{ending_point, OPT_CONFIG, STORE, UA};
use anyhow::anyhow; use anyhow::anyhow;
use reqwest::Client; use reqwest::Client;
use serde::Deserialize; use serde::Deserialize;
const COMMIT_URL: &str = "https://api.github.com/repos/Team-Haruki/haruki-sekai-master/commits?path=master/events.json&per_page=1"; const COMMIT_URL: &str = "https://api.github.com/repos/kotori8823/sekai-master-db/commits?path=events.json&per_page=1";
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct CommitResponse { struct CommitResponse {
@ -40,26 +40,28 @@ pub async fn fetch_last_hash() -> anyhow::Result<String> {
} }
pub fn raw_url(path: &str) -> String { pub fn raw_url(path: &str) -> String {
"https://raw.githubusercontent.com/Team-Haruki/haruki-sekai-master/refs/heads/main/".to_owned() "https://raw.githubusercontent.com/kotori8823/sekai-master-db/refs/heads/master/".to_owned()
+ path + path
} }
pub async fn sync_data() -> anyhow::Result<()> { pub async fn sync_data() -> anyhow::Result<()> {
let client = http_client()?; let client = http_client()?;
let events_cards = &client let events_cards = &client
.get(raw_url("master/eventCards.json")) .get(raw_url("eventCards.json"))
.send() .send()
.await? .await?
.text() .text()
.await?; .await?;
ending_point!(ok);
let event_stories = &client let event_stories = &client
.get(raw_url("master/eventStories.json")) .get(raw_url("eventStories.json"))
.send() .send()
.await? .await?
.text() .text()
.await?; .await?;
ending_point!(ok);
let card_episodes = &client let card_episodes = &client
.get(raw_url("master/cardEpisodes.json")) .get(raw_url("cardEpisodes.json"))
.send() .send()
.await? .await?
.text() .text()
@ -75,7 +77,7 @@ pub async fn sync_data() -> anyhow::Result<()> {
pub async fn get_master_data() -> anyhow::Result<CurrentVersion> { pub async fn get_master_data() -> anyhow::Result<CurrentVersion> {
let client = http_client()?; let client = http_client()?;
let text = client let text = client
.get(raw_url("versions/current_version.json")) .get(raw_url("versions.json"))
.send() .send()
.await? .await?
.text() .text()

View File

@ -7,19 +7,44 @@ mod store;
use crate::data::{Config, FetchResult, OptConfig, State}; use crate::data::{Config, FetchResult, OptConfig, State};
use crate::main_loop::main; use crate::main_loop::main;
use crate::store::Store; use crate::store::Store;
use log::{LevelFilter, debug}; use log::{LevelFilter, debug, error};
use simplelog::{ColorChoice, TermLogger, TerminalMode}; use simplelog::{ColorChoice, TermLogger, TerminalMode};
use std::ffi::{CStr, CString, c_char}; use std::ffi::{CStr, CString, c_char, c_int};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{OnceLock, RwLock}; use std::sync::{OnceLock, RwLock};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use tokio::sync::Notify;
pub static CONFIG: OnceLock<Config> = OnceLock::new(); pub static CONFIG: OnceLock<Config> = OnceLock::new();
pub static OPT_CONFIG: RwLock<OptConfig> = RwLock::new(OptConfig::new()); pub static OPT_CONFIG: RwLock<OptConfig> = RwLock::new(OptConfig::new());
pub static PROVIDE: RwLock<Option<FetchResult>> = RwLock::new(None); pub static PROVIDE: RwLock<Option<FetchResult>> = RwLock::new(None);
pub static STORE: RwLock<Store> = RwLock::new(Store::new()); pub static STORE: RwLock<Store> = RwLock::new(Store::new());
pub static STATE: RwLock<State> = RwLock::new(State::new()); pub static STATE: RwLock<State> = RwLock::new(State::new());
pub static THREAD: RwLock<Option<thread::JoinHandle<()>>> = RwLock::new(None);
pub static KILLING: AtomicBool = AtomicBool::new(false);
pub static NOTIFY: Notify = Notify::const_new();
#[macro_export]
macro_rules! ending_point {
(ok)=>{
ending_point!(Err(anyhow!("Killed")))
};
(ok_with $b:block)=>{
if $crate::KILLING.load(::std::sync::atomic::Ordering::SeqCst){
$b;
return Err(anyhow!("Killed"));
}
};
($ret: expr) => {
if $crate::KILLING.load(::std::sync::atomic::Ordering::SeqCst){
return $ret;
}
};
() => {
ending_point!(())
}
}
pub const UA: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"; pub const UA: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
@ -55,7 +80,7 @@ pub unsafe extern "C" fn boot(
cwd: *const c_char, cwd: *const c_char,
name: *const c_char, name: *const c_char,
as_path: *const c_char, as_path: *const c_char,
) -> i32 { ) -> c_int {
unsafe { unsafe {
CONFIG CONFIG
.set(Config { .set(Config {
@ -78,14 +103,36 @@ pub unsafe extern "C" fn boot(
// ColorChoice::Auto, // ColorChoice::Auto,
// ) // )
// .unwrap(); // .unwrap();
thread::spawn(|| { let handle = thread::Builder::new()
.name("sekai-sync-lib".to_string())
.spawn(|| {
let rt = Runtime::new().expect("Failed to create Tokio runtime"); let rt = Runtime::new().expect("Failed to create Tokio runtime");
rt.block_on(keep_loop()); rt.block_on(keep_loop());
}); });
match handle {
Ok(thread) => {
THREAD.write().unwrap().replace(thread);
0 0
} }
Err(error) => {
error!("{}", error);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn stop() -> c_int {
if let Some(thread) = THREAD.write().unwrap().take() {
KILLING.store(true, Ordering::SeqCst);
NOTIFY.notify_waiters();
thread.join().expect("Failed to join thread");
return 0;
}
-1
}
/// # Safety /// # Safety
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
@ -95,7 +142,6 @@ pub unsafe extern "C" fn fetch_data() -> *mut c_char {
return std::ptr::null_mut(); return std::ptr::null_mut();
} }
let result = provide.take().unwrap(); let result = provide.take().unwrap();
*provide = None;
let json_string = serde_json::to_string(&result).unwrap(); let json_string = serde_json::to_string(&result).unwrap();
@ -116,10 +162,14 @@ pub unsafe extern "C" fn free_string(s: *mut c_char) {
async fn keep_loop() { async fn keep_loop() {
loop { loop {
ending_point!();
debug!("loop_start"); debug!("loop_start");
if let Err(err) = main().await { if let Err(err) = main().await {
println!("Error: {}", err); println!("Error: {}", err);
} }
tokio::time::sleep(Duration::from_secs(60)).await; tokio::select! {
_ = tokio::time::sleep(Duration::from_secs(60)) => {},
_ = NOTIFY.notified() => {},
}
} }
} }

View File

@ -1,14 +1,15 @@
use crate::data::{FetchResult, State}; use crate::data::{FetchResult, State};
use crate::download::Downloader; use crate::download::Downloader;
use crate::github::{fetch_last_hash, sync_data}; use crate::github::{fetch_last_hash, sync_data};
use crate::{CONFIG, PROVIDE, STATE, STORE}; use crate::{CONFIG, PROVIDE, STATE, STORE, ending_point};
use anyhow::anyhow; use anyhow::anyhow;
use assets_updater::core::export_pipeline::find_files; use assets_updater::core::export_pipeline::find_files;
use std::fs;
use log::debug; use log::debug;
use std::fs;
pub async fn main() -> anyhow::Result<()> { pub async fn main() -> anyhow::Result<()> {
let current_hash = fetch_last_hash().await?; let current_hash = fetch_last_hash().await?;
ending_point!(ok);
let old_state = { let old_state = {
let mut l = STATE.read(); let mut l = STATE.read();
if l.is_err() { if l.is_err() {
@ -33,7 +34,9 @@ pub async fn main() -> anyhow::Result<()> {
} }
// hash updated // hash updated
ending_point!(ok);
sync_data().await?; sync_data().await?;
ending_point!(ok);
let store = STORE.read().map_err(|e| anyhow!("{}", e))?; let store = STORE.read().map_err(|e| anyhow!("{}", e))?;
let event = store.find_last_event(); let event = store.find_last_event();
if event if event
@ -54,16 +57,28 @@ pub async fn main() -> anyhow::Result<()> {
let cards = store.find_cards_by_event(event.event_id); let cards = store.find_cards_by_event(event.event_id);
drop(store); drop(store);
ending_point!(ok);
let downloader = Downloader::new().await?; let downloader = Downloader::new().await?;
ending_point!(ok);
let mut images = Vec::new(); let mut images = Vec::new();
let cache_dir = CONFIG.get().unwrap().cache_dir(); let cache_dir = CONFIG.get().unwrap().cache_dir();
for card in cards { for card in cards {
debug!("Pulling card {} from bundle {}", card.card_id, card.assetbundle_name); ending_point!(ok);
debug!(
"Pulling card {} from bundle {}",
card.card_id, card.assetbundle_name
);
let dir = downloader let dir = downloader
.pull(&format!("character/member/{}", card.assetbundle_name)) .pull(&format!("character/member/{}", card.assetbundle_name))
.await?; .await?;
ending_point!(ok_with {
fs::remove_dir_all(dir.0)?;
});
let files = find_files(&dir.0)?; let files = find_files(&dir.0)?;
for file in files { for file in files {
ending_point!(ok_with {
fs::remove_dir_all(dir.0)?;
});
if file.is_file() && file.extension().map(|ext| ext == "png").unwrap_or(false) { if file.is_file() && file.extension().map(|ext| ext == "png").unwrap_or(false) {
let new_filename = format!( let new_filename = format!(
"{}_{}", "{}_{}",