The cache system uses a dual-layer architecture to minimize network requests and improve performance.
Exports:
lighty_loaders::utils::cache::Cache
lighty_loaders::utils::cache::CachedData
Two-Layer Architecture
Layer 1: Raw Version Cache
Stores complete, unparsed data from external APIs.
Purpose: Avoid redundant API calls Key: Version identifier (String) Value: Raw data (e.g., JSON struct) TTL: 1 hour (configurable via Query::cache_ttl())
Example keys:
"vanilla-1.21.1"
"fabric-1.21.1-0.16.9"
"quilt-1.21.1-0.27.1"
Layer 2: Query Cache
Stores extracted, processed metadata for specific queries.
Purpose: Avoid re-parsing/re-processing raw data Key: QueryKey<Q> (version + query type) Value: Arc<VersionMetaData>TTL: 1 hour (configurable via Query::cache_ttl_for_query())
Example keys:
Data Flow
Cache Implementation
CachedData Structure
Cache Type
TTL Configuration
Default TTL
Per-Query TTL
Cache Behavior
First Request (Cold Cache)
What happens:
Check query cache → Miss
Check raw cache → Miss
Fetch from Mojang API → https://piston-meta.mojang.com/...
Parse JSON → VanillaMetaData
Store in raw cache (key: "vanilla-1.21.1")
Extract metadata → Version
Store in query cache (key: QueryKey { version: "vanilla-1.21.1", query: VanillaBuilder })
let instance = VersionBuilder::new("vanilla-1.21", Loader::Vanilla, "", "1.21.1", launcher_dir);
// First call: fetches from API (~500ms)
let metadata1 = instance.get_metadata().await?;
// Second call: uses query cache (~1ms)
let metadata2 = instance.get_metadata().await?;
// Get libraries only
let libraries = instance.get_libraries().await?;
// After 1 hour, cache expired
let metadata3 = instance.get_metadata().await?;
#[cfg(feature = "events")]
use lighty_event::{Event, LoaderEvent};
// Cache hit
EVENT_BUS.emit(Event::Loader(LoaderEvent::ManifestCached {
loader: "Vanilla".to_string(),
}));
// Cache miss
EVENT_BUS.emit(Event::Loader(LoaderEvent::FetchingData {
loader: "Vanilla".to_string(),
minecraft_version: "1.21.1".to_string(),
loader_version: "".to_string(),
}));
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct QueryKey<Q> {
pub version: String, // Same as raw cache key
pub query: Q, // Query enum variant
}
let mut receiver = EVENT_BUS.subscribe();
tokio::spawn(async move {
while let Ok(event) = receiver.next().await {
if let Event::Loader(LoaderEvent::ManifestCached { loader }) => {
println!("Cache hit: {}", loader);
}
if let Event::Loader(LoaderEvent::FetchingData { loader, .. }) => {
println!("Cache miss: {}", loader);
}
}
});
for _ in 0..10 {
let instance = VersionBuilder::new("vanilla", Loader::Vanilla, "", "1.21.1", launcher_dir);
instance.get_metadata().await?; // Cache still works!
}
let instance = VersionBuilder::new("vanilla", Loader::Vanilla, "", "1.21.1", launcher_dir);
for _ in 0..10 {
instance.get_metadata().await?; // Cached after first call
}
// Faster (smaller data)
let libraries = instance.get_libraries().await?;
// Slower (more data, but cached separately)
let metadata = instance.get_metadata().await?;
let libraries = &metadata.libraries;