From 03b6afade2264efb5e7ad98ebef551d5b553c19e Mon Sep 17 00:00:00 2001 From: Bluemangoo Date: Mon, 22 Jun 2026 19:12:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- entry/Cargo.toml | 2 +- entry/src/github.rs | 4 ++- entry/src/lib.rs | 70 ++++++++++++++++++++++++++++++++++++------ entry/src/main_loop.rs | 21 +++++++++++-- 4 files changed, 82 insertions(+), 15 deletions(-) diff --git a/entry/Cargo.toml b/entry/Cargo.toml index 5ea66fc..f1aba30 100644 --- a/entry/Cargo.toml +++ b/entry/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["rlib", "cdylib"] common = { path = "../common" } 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_json = { workspace = true } reqwest = { workspace = true, features = ["json"] } diff --git a/entry/src/github.rs b/entry/src/github.rs index 3d5892b..e2007fb 100644 --- a/entry/src/github.rs +++ b/entry/src/github.rs @@ -1,5 +1,5 @@ use crate::data::CurrentVersion; -use crate::{OPT_CONFIG, STORE, UA}; +use crate::{ending_point, OPT_CONFIG, STORE, UA}; use anyhow::anyhow; use reqwest::Client; use serde::Deserialize; @@ -52,12 +52,14 @@ pub async fn sync_data() -> anyhow::Result<()> { .await? .text() .await?; + ending_point!(ok); let event_stories = &client .get(raw_url("master/eventStories.json")) .send() .await? .text() .await?; + ending_point!(ok); let card_episodes = &client .get(raw_url("master/cardEpisodes.json")) .send() diff --git a/entry/src/lib.rs b/entry/src/lib.rs index 67e1845..813e283 100644 --- a/entry/src/lib.rs +++ b/entry/src/lib.rs @@ -7,19 +7,44 @@ mod store; use crate::data::{Config, FetchResult, OptConfig, State}; use crate::main_loop::main; use crate::store::Store; -use log::{LevelFilter, debug}; +use log::{LevelFilter, debug, error}; 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::thread; use std::time::Duration; use tokio::runtime::Runtime; +use tokio::sync::Notify; pub static CONFIG: OnceLock = OnceLock::new(); pub static OPT_CONFIG: RwLock = RwLock::new(OptConfig::new()); pub static PROVIDE: RwLock> = RwLock::new(None); pub static STORE: RwLock = RwLock::new(Store::new()); pub static STATE: RwLock = RwLock::new(State::new()); +pub static THREAD: RwLock>> = 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"; @@ -55,7 +80,7 @@ pub unsafe extern "C" fn boot( cwd: *const c_char, name: *const c_char, as_path: *const c_char, -) -> i32 { +) -> c_int { unsafe { CONFIG .set(Config { @@ -78,13 +103,35 @@ pub unsafe extern "C" fn boot( // ColorChoice::Auto, // ) // .unwrap(); - thread::spawn(|| { - let rt = Runtime::new().expect("Failed to create Tokio runtime"); + let handle = thread::Builder::new() + .name("sekai-sync-lib".to_string()) + .spawn(|| { + let rt = Runtime::new().expect("Failed to create Tokio runtime"); - rt.block_on(keep_loop()); - }); + rt.block_on(keep_loop()); + }); - 0 + match handle { + Ok(thread) => { + THREAD.write().unwrap().replace(thread); + 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 @@ -95,7 +142,6 @@ pub unsafe extern "C" fn fetch_data() -> *mut c_char { return std::ptr::null_mut(); } let result = provide.take().unwrap(); - *provide = None; 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() { loop { + ending_point!(); debug!("loop_start"); if let Err(err) = main().await { println!("Error: {}", err); } - tokio::time::sleep(Duration::from_secs(60)).await; + tokio::select! { + _ = tokio::time::sleep(Duration::from_secs(60)) => {}, + _ = NOTIFY.notified() => {}, + } } } diff --git a/entry/src/main_loop.rs b/entry/src/main_loop.rs index 2632f72..45f01ca 100644 --- a/entry/src/main_loop.rs +++ b/entry/src/main_loop.rs @@ -1,14 +1,15 @@ use crate::data::{FetchResult, State}; use crate::download::Downloader; 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 assets_updater::core::export_pipeline::find_files; -use std::fs; use log::debug; +use std::fs; pub async fn main() -> anyhow::Result<()> { let current_hash = fetch_last_hash().await?; + ending_point!(ok); let old_state = { let mut l = STATE.read(); if l.is_err() { @@ -33,7 +34,9 @@ pub async fn main() -> anyhow::Result<()> { } // hash updated + ending_point!(ok); sync_data().await?; + ending_point!(ok); let store = STORE.read().map_err(|e| anyhow!("{}", e))?; let event = store.find_last_event(); if event @@ -54,16 +57,28 @@ pub async fn main() -> anyhow::Result<()> { let cards = store.find_cards_by_event(event.event_id); drop(store); + ending_point!(ok); let downloader = Downloader::new().await?; + ending_point!(ok); let mut images = Vec::new(); let cache_dir = CONFIG.get().unwrap().cache_dir(); 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 .pull(&format!("character/member/{}", card.assetbundle_name)) .await?; + ending_point!(ok_with { + fs::remove_dir_all(dir.0)?; + }); let files = find_files(&dir.0)?; 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) { let new_filename = format!( "{}_{}",