pub struct GameStorage { /* private fields */ }Expand description
Provides a stateful API for game file management.
Implementations§
Source§impl GameStorage
impl GameStorage
Sourcepub fn discover_relative(
&self,
rel_path: &Path,
mode: DiscoverMode,
) -> Result<Vec<PathBuf>>
pub fn discover_relative( &self, rel_path: &Path, mode: DiscoverMode, ) -> Result<Vec<PathBuf>>
Discover paths in a specific subdirectory relative to the root.
§Errors
Returns an error if the directory cannot be read.
Source§impl GameStorage
impl GameStorage
Sourcepub fn read_bytes(&self, path: &Path) -> Result<Vec<u8>>
pub fn read_bytes(&self, path: &Path) -> Result<Vec<u8>>
Read a game-relative file’s raw bytes, hook-aware.
§Errors
Returns an error if the resolved file cannot be read, or (in
hook-aware mode) if a matched rule blocks the read, see
GameStorage::resolve_read_path.
Sourcepub fn write_bytes(&self, path: &Path, contents: &[u8]) -> Result<()>
pub fn write_bytes(&self, path: &Path, contents: &[u8]) -> Result<()>
Write a game-relative file’s raw bytes, hook-aware: transparently
forwarded to wherever the active redirection rules say this path
should be written, same as read_bytes reads it from.
§Errors
Returns an error if the resolved location cannot be written, or (in
hook-aware mode) if a matched rule blocks the write, see
GameStorage::resolve_write_path.
Sourcepub fn read_text(&self, path: &Path) -> Result<String>
pub fn read_text(&self, path: &Path) -> Result<String>
Read text file with Windows-1252 encoding (game-specific), hook-aware.
§Errors
Returns an error if the resolved file cannot be read, or (in hook-aware mode) if a matched rule blocks the read.
Sourcepub fn write_text(&self, path: &Path, content: &str) -> Result<()>
pub fn write_text(&self, path: &Path, content: &str) -> Result<()>
Write text file with Windows-1252 encoding (game-specific),
hook-aware: transparently forwarded per the active redirection
rules, same as GameStorage::write_bytes.
§Errors
Returns an error if the resolved location cannot be written, or (in hook-aware mode) if a matched rule blocks the write.
Sourcepub fn exists(&self, path: &Path) -> bool
pub fn exists(&self, path: &Path) -> bool
True if path (relative to the game root) resolves to a file that
actually exists, the mod’s replacement if one is active and
present, the original otherwise. Never errors: a blocked
(hard_error) resolution is reported as “does not exist”, matching
what the game process would experience trying to open it.
Sourcepub fn remove_file(&self, path: &Path) -> Result<()>
pub fn remove_file(&self, path: &Path) -> Result<()>
Removes a game-relative file, hook-aware: if a mod redirect (static
replacement, or a file previously written into a mod’s target) is
active for this path, that file is removed instead of the original.
§Errors
Returns an error if the resolved file cannot be removed, or (in
hook-aware mode) if a matched rule is in hard_error mode with no
hooked file present, the delete is blocked, same as the game
process would see.
Sourcepub fn list_files(&self, rel_dir: &Path) -> Result<Vec<String>>
pub fn list_files(&self, rel_dir: &Path) -> Result<Vec<String>>
Lists the file names directly under rel_dir (relative to the game
root, non-recursive) as the game process would actually see them,
hook-aware: real on-disk files are kept unless a matched mod rule
hides/replaces them (per
[supreme_hook_config::HookResolver::should_hide_original_entry]),
and any file a mod injects into rel_dir is added (per
[supreme_hook_config::HookResolver::collect_mod_files_in_dir]),
the same two decisions the game process’s own directory-listing hook
makes. With no resolver attached, this is just the plain names of
the files physically present in rel_dir.
A missing rel_dir is not an error: it’s treated the same as an
empty one, since a mod can still inject files into a directory that
doesn’t exist on disk at all.
Result is sorted and deduplicated, so callers get a stable order regardless of the OS’s own directory-listing order or of a name appearing both physically and (redundantly) in a mod’s injection list.
§Errors
Returns an error if rel_dir exists but cannot be read.
Source§impl GameStorage
impl GameStorage
Sourcepub fn calculate_hash(&self, path: &Path) -> Result<String>
pub fn calculate_hash(&self, path: &Path) -> Result<String>
Calculate SHA256 hash of a file, for content-addressed storage.
§Errors
Returns an error if the file cannot be opened or read.
Sourcepub fn list_snapshots_profiles(&self) -> Result<Vec<String>>
pub fn list_snapshots_profiles(&self) -> Result<Vec<String>>
List all available snapshot profiles: finds and filters .json files.
§Errors
Returns an error if the profiles directory cannot be read.
Sourcepub fn clean_up_empty_dirs(&self, start_dir: &Path, limit_dir: &Path)
pub fn clean_up_empty_dirs(&self, start_dir: &Path, limit_dir: &Path)
Clean up empty directories recursively.
Sourcepub fn write_report<T: Serialize>(&self, name: &str, value: &T) -> Result<()>
pub fn write_report<T: Serialize>(&self, name: &str, value: &T) -> Result<()>
Write a JSON report to .supreme/{name}.json
§Errors
Returns an error if the report directory cannot be created or the file written.
Sourcepub fn get_report_path(&self, name: &str) -> PathBuf
pub fn get_report_path(&self, name: &str) -> PathBuf
Get the path to a report file.
Source§impl GameStorage
impl GameStorage
Sourcepub fn auth_info(&self) -> Result<AuthInfo, HybridStorageError>
pub fn auth_info(&self) -> Result<AuthInfo, HybridStorageError>
Ask the server where its auth page lives (auth_info). The page
URL is the server’s own (like the results URL), so the launcher
never has to guess how the backend is exposed to browsers.
§Errors
See GameStorage::get_auth_action; a backend predating the
auth API answers a version-mismatch signature.
Sourcepub fn auth_poll(&self, token: &str) -> Result<AuthPoll, HybridStorageError>
pub fn auth_poll(&self, token: &str) -> Result<AuthPoll, HybridStorageError>
Poll the one-time link token (auth_poll). The token is
64 hex chars (the launcher generated it), safe in a URL unencoded.
§Errors
Sourcepub fn auth_account(
&self,
user_id: i64,
auth_key: &str,
) -> Result<AccountDto, HybridStorageError>
pub fn auth_account( &self, user_id: i64, auth_key: &str, ) -> Result<AccountDto, HybridStorageError>
Re-read the linked account through its device key
(auth_account, GET with user_id + auth_key - the same pair
the room pushes carry, so the pair is URL-safe by construction
here too: a decimal id and a 64-hex key). The launcher caches
the account record from link time while the nickname and the
avatar are mutable; this is the refresh that follows a rename
(and an avatar change) without a re-link - the server links
uploads by the key’s identity, the pushed name is display only.
§Errors
See GameStorage::get_auth_action; a stale key answers the
same refusal the room pushes give, verbatim.
Sourcepub fn fetch_auth_avatar(
&self,
user_id: i64,
version_sha: &str,
) -> Result<Vec<u8>, HybridStorageError>
pub fn fetch_auth_avatar( &self, user_id: i64, version_sha: &str, ) -> Result<Vec<u8>, HybridStorageError>
Fetch one account’s avatar bytes (auth_avatar). version_sha is
the account’s current avatar_sha256 (the v= param, an
immutable-cache key: the same sha is always the same bytes); the
server answers raw image bytes, never JSON.
§Errors
Transport failures and non-2xx responses are errors; a backend predating the action answers a version-mismatch signature.
Sourcepub fn revoke_auth_key(&self, auth_key: &str) -> Result<(), HybridStorageError>
pub fn revoke_auth_key(&self, auth_key: &str) -> Result<(), HybridStorageError>
Revoke this device’s auth key (auth_revoke). Called on logout;
the key dies server-side, a stolen copy of mods.json stops
working. Fire-and-forget by design: a failed revoke (server gone,
network down) never blocks the local logout, the key still expires
server-side per the backend’s own policy.
§Errors
Source§impl GameStorage
impl GameStorage
pub fn fetch_contents_batch( &self, hashes: &[&str], ) -> Result<Vec<(String, Vec<u8>)>, HybridStorageError>
Sourcepub fn get_content(&self, hash: &str) -> Result<Vec<u8>, HybridStorageError>
pub fn get_content(&self, hash: &str) -> Result<Vec<u8>, HybridStorageError>
Fetch content by hash: the launcher-side local cache
(.supreme/contents/<hash>[.xz]) first, then up to three remote
attempts (with inter-attempt sleeps) that are saved into the cache,
xz-decoded and hash-verified.
§Errors
Transport/decoding errors surface after the retries; offline storage
without the hash cached returns HybridStorageError::NotFound.
Sourcepub fn get_content_once(
&self,
hash: &str,
) -> Result<Vec<u8>, HybridStorageError>
pub fn get_content_once( &self, hash: &str, ) -> Result<Vec<u8>, HybridStorageError>
GameStorage::get_content with a single remote attempt.
Identical semantics (local .xz/raw cache first, remote fetch saved
into the cache, xz decoded, hash verified) minus the retry loop and
its inter-attempt sleeps. For an embedded consumer whose fetches run
on a latency-sensitive path (the hook DLL’s managed files do their
cold-miss fetch on a game thread: bounded timeouts, degrade to
vanilla on failure), the retry
loop’s worst case (3 × global timeout + 1.5 s of sleeps) is not an
acceptable stall, so it gets a one-shot variant and applies its own
retry policy (or none) instead.
§Errors
Same error set as GameStorage::get_content, reached in one
attempt instead of up to three.
pub fn upload_content( &self, hash: &str, data: &[u8], ) -> Result<(), HybridStorageError>
Source§impl GameStorage
impl GameStorage
Sourcepub fn list_online_profiles(
&self,
) -> Result<Vec<OnlineProfileInfo>, HybridStorageError>
pub fn list_online_profiles( &self, ) -> Result<Vec<OnlineProfileInfo>, HybridStorageError>
List available profiles from the online server.
Sourcepub fn sync_online_profiles(
&self,
) -> Result<HashSet<String>, HybridStorageError>
pub fn sync_online_profiles( &self, ) -> Result<HashSet<String>, HybridStorageError>
Sync online profiles: downloads missing manifests and returns a set of filenames available online.
pub fn get_profile( &self, profile_name: &str, ) -> Result<Vec<u8>, HybridStorageError>
Source§impl GameStorage
impl GameStorage
Sourcepub fn create_room(
&self,
env: &str,
difficulty: &str,
host: &str,
name: &str,
starts_at: i64,
ends_at: i64,
identity: Option<RoomIdentity<'_>>,
) -> Result<RoomDto, HybridStorageError>
pub fn create_room( &self, env: &str, difficulty: &str, host: &str, name: &str, starts_at: i64, ends_at: i64, identity: Option<RoomIdentity<'_>>, ) -> Result<RoomDto, HybridStorageError>
Create a tournament room (room_create). The server generates
the room id (r_ + 12 hex chars) and created_at.
the room has a planning window, starts_at (epoch seconds;
may reach 13 h into the past, 7 days into the future) and ends_at
(after starts_at, at most 7 days out; a host may not have two
rooms sharing a UTC calendar day), and an optional display name
(empty = the server picks a snowboarding word pair; a supplied name
must pass the server’s blocklist).
identity attaches the logged-in account when present (the room
becomes account-owned and verified); None creates as a guest.
§Errors
Sourcepub fn list_rooms(&self) -> Result<Vec<RoomSummaryDto>, HybridStorageError>
pub fn list_rooms(&self) -> Result<Vec<RoomSummaryDto>, HybridStorageError>
List open rooms (room_list; the server only returns rooms whose
ends_at is in the future).
§Errors
Sourcepub fn join_room(
&self,
id: &str,
player: &str,
identity: Option<RoomIdentity<'_>>,
) -> Result<RoomDto, HybridStorageError>
pub fn join_room( &self, id: &str, player: &str, identity: Option<RoomIdentity<'_>>, ) -> Result<RoomDto, HybridStorageError>
Join a room as player (room_join), with the logged-in
account’s identity attached when present (a guest join stays
None; the server refuses a guest whose name matches a
registered nickname, that refusal arrives verbatim).
§Errors
Sourcepub fn remove_room(
&self,
id: &str,
player: &str,
identity: Option<RoomIdentity<'_>>,
) -> Result<(), HybridStorageError>
pub fn remove_room( &self, id: &str, player: &str, identity: Option<RoomIdentity<'_>>, ) -> Result<(), HybridStorageError>
Remove a room you created (room_remove). The server only
accepts the request from the room’s creator, only while no other
player has joined, and only BEFORE the room’s start time; the
player identifies as the creator by player name (guest rooms)
and/or the account identity when logged in. The room’s files are
deleted server-side; the content-addressed blobs are shared and
stay.
§Errors
See GameStorage::post_room_action; the server’s refusal
messages (not the creator, players have joined, room already started, unknown room) arrive verbatim. A backend predating
the action answers a version-mismatch signature.
Sourcepub fn fetch_room_records(
&self,
room_id: &str,
) -> Result<Vec<RoomRecordDto>, HybridStorageError>
pub fn fetch_room_records( &self, room_id: &str, ) -> Result<Vec<RoomRecordDto>, HybridStorageError>
Fetch the best record per player (room_records, the DLL’s
polling endpoint).
§Errors
Sourcepub fn fetch_room_high_times(
&self,
room_id: &str,
player: &str,
) -> Result<Option<(String, i64)>, HybridStorageError>
pub fn fetch_room_high_times( &self, room_id: &str, player: &str, ) -> Result<Option<(String, i64)>, HybridStorageError>
Fetch the server-rendered high-times file for the selected track’s
room and player (room_high_times): the exact text the game’s
Saved_Data/<env>_<diff>_high_times.txt should contain, the
FULL room leaderboard (every record the server has, ascending, plus
dummy padding only when fewer than five), so the game’s high-score
screen shows everyone. Entry rank N pairs with Replay_N.dat for the
game’s five replay slots.
Returns Ok(Some((text, entries))) on success (entries = the
rendered entry count, diagnostics), and Ok(None) when the server
predates the action (version mismatch signature), the caller falls
back to local generation (high_times_entries_from_records).
§Errors
See GameStorage::post_room_action; all non-version-mismatch
errors propagate.
Sourcepub fn fetch_room_high_times_version(
&self,
room_id: &str,
player: &str,
) -> Result<Option<String>, HybridStorageError>
pub fn fetch_room_high_times_version( &self, room_id: &str, player: &str, ) -> Result<Option<String>, HybridStorageError>
Ask the server for the CONTENT HASH of the current high-times file
for the selected track’s room and player
(room_high_times_version): the sha-256 hex digest of the exact
text GameStorage::fetch_room_high_times would return right now.
The cheap half of the CAS copy rule, hash the local file, and only
download the body when the digests differ.
Returns Ok(Some(version)) on success and Ok(None) when the server
predates the action (version-mismatch signature), the caller then
degrades to fetching the body and byte-comparing it against the local
file, which avoids spurious writes on old servers too.
§Errors
See GameStorage::post_room_action; all non-version-mismatch
errors propagate.
Sourcepub fn upload_room_record(
&self,
room_id: &str,
player: &str,
time_ticks: u32,
dat_path: &Path,
splits_ms: Option<[i64; 2]>,
) -> Result<RoomUploadResult, HybridStorageError>
pub fn upload_room_record( &self, room_id: &str, player: &str, time_ticks: u32, dat_path: &Path, splits_ms: Option<[i64; 2]>, ) -> Result<RoomUploadResult, HybridStorageError>
Upload a tournament record for player (room_upload_record).
time_ticks is the race time in the game’s 10 ms replay ticks. The
.dat at dat_path is first pushed into the server CAS through the
existing unified chunked upload, then claimed with
room_upload_record {id, player, time_ticks, hash, splits_ms?}.
splits_ms (whole-millisecond [checkpoint1_ms, checkpoint2_ms])
lets the server render the room’s high-times entries with real
split times; None uploads the record without them (the server
then falls back to finish-only splits).
accepted: false (worse or equal than the player’s existing
personal best) is a successful RoomUploadResult, not an error.
§Errors
See GameStorage::post_room_action; also fails when time_ticks
is 0 (the server would reject it and the blob upload would be
wasted) or when reading dat_path fails.
Source§impl GameStorage
impl GameStorage
pub fn upload_profile( &self, profile_name: &str, data: &[u8], tx: Option<Sender<SnapshotEvent>>, ) -> Result<(), HybridStorageError>
Sourcepub fn upload_replay(
&self,
snapshot_name: &str,
data: &[u8],
tx: Option<Sender<SnapshotEvent>>,
) -> Result<(), HybridStorageError>
pub fn upload_replay( &self, snapshot_name: &str, data: &[u8], tx: Option<Sender<SnapshotEvent>>, ) -> Result<(), HybridStorageError>
Upload a replay snapshot (json file) to the backend.
Sourcepub fn commit_snapshot(
&self,
snapshot_name: &str,
) -> Result<(), HybridStorageError>
pub fn commit_snapshot( &self, snapshot_name: &str, ) -> Result<(), HybridStorageError>
Commit a staged snapshot.
pub fn commit_snapshot_type( &self, subdirectory: &str, snapshot_filename_with_ext: &str, ) -> Result<(), HybridStorageError>
Sourcepub fn fetch_replay(
&self,
snapshot_name: &str,
) -> Result<Vec<u8>, HybridStorageError>
pub fn fetch_replay( &self, snapshot_name: &str, ) -> Result<Vec<u8>, HybridStorageError>
Fetch a replay snapshot JSON by name. Tries local .supreme/replays first, then online at /replays/.
Source§impl GameStorage
impl GameStorage
Sourcepub fn get_max_upload_size(&self) -> Result<usize, HybridStorageError>
pub fn get_max_upload_size(&self) -> Result<usize, HybridStorageError>
Get maximum upload size from server
Sourcepub fn upload_file_unified(
&self,
subdirectory: &str,
data: &[u8],
file_type: &str,
filename: Option<&str>,
tx: Option<Sender<SnapshotEvent>>,
) -> Result<(), HybridStorageError>
pub fn upload_file_unified( &self, subdirectory: &str, data: &[u8], file_type: &str, filename: Option<&str>, tx: Option<Sender<SnapshotEvent>>, ) -> Result<(), HybridStorageError>
Unified chunked upload for all files (single chunk for small files, multiple for large)
Source§impl GameStorage
impl GameStorage
Sourcepub fn new(game_path: &Path) -> Self
pub fn new(game_path: &Path) -> Self
Create a new GameStorage instance for local-only storage, with no
hook-awareness: read_text/write_text/etc. always operate on the
literal path under game_path, exactly as before mod redirection
existed. Use GameStorage::with_hooks for pages that need to see
the game the way a mod-redirected game process would.
Sourcepub fn with_hooks(
game_path: &Path,
mods_dir: &Path,
temp_dir: PathBuf,
) -> (Self, Vec<(String, HookConfigError)>)
pub fn with_hooks( game_path: &Path, mods_dir: &Path, temp_dir: PathBuf, ) -> (Self, Vec<(String, HookConfigError)>)
Create a GameStorage instance whose read_*/write_*/exists/
remove_file/list_files calls are routed through the same
redirection rules the game process would apply at runtime, per
mods.json and each enabled mod’s mod.json under mods_dir.
temp_dir is only used for staging patched replacement files
(see [HookResolver::resolve_path]), most redirection rules never
touch it. Writes always land on the original file or a mod’s own
target, never in temp_dir.
Mods listed in mods.json whose mod.json couldn’t be read or
parsed are skipped rather than failing the whole call; they’re
returned alongside the instance so the caller can surface them
(e.g. in the mod manager page) rather than have them silently
dropped.
Sourcepub fn is_hook_aware(&self) -> bool
pub fn is_hook_aware(&self) -> bool
True if this instance routes reads/writes through mod redirection
rules (i.e. was built with GameStorage::with_hooks or
GameStorage::with_hook_resolver).
Sourcepub fn hook_config(&self) -> Option<&HookConfig>
pub fn hook_config(&self) -> Option<&HookConfig>
The loaded hook resolver’s rule config, when hook-aware (see
GameStorage::with_hook_resolver).
The launcher uses this to resolve a game-relative path to its
redirect target with the exact same rule machinery the DLL applies
at runtime (e.g. the tournament instance high_times file the
selected track’s Saved_Data/…_high_times.txt rule points at),
the launcher and the hook can then never disagree about where a
redirected file lives.
Sourcepub fn with_hook_resolver(
self,
mods_dir: &Path,
temp_dir: PathBuf,
) -> (Self, Vec<(String, HookConfigError)>)
pub fn with_hook_resolver( self, mods_dir: &Path, temp_dir: PathBuf, ) -> (Self, Vec<(String, HookConfigError)>)
Attaches mod-redirection hook-awareness to an already-built
GameStorage, e.g. one built with GameStorage::with_online,
which (unlike GameStorage::new) has no hook-aware counterpart
constructor of its own. Consumes and returns self so it composes
with any other constructor rather than needing a dedicated
combined one for every combination.
Same loading contract as GameStorage::with_hooks: mods listed
in mods.json whose mod.json couldn’t be read or parsed are
skipped rather than failing the whole call, and returned alongside
the instance.
Sourcepub fn with_online(game_path: &Path, api_url: &str) -> Self
pub fn with_online(game_path: &Path, api_url: &str) -> Self
Create a new GameStorage instance with online fallback support at api_url.
Sourcepub fn with_online_timeouts(
game_path: &Path,
api_url: &str,
timeout_connect: Duration,
timeout_global: Duration,
) -> Self
pub fn with_online_timeouts( game_path: &Path, api_url: &str, timeout_connect: Duration, timeout_global: Duration, ) -> Self
GameStorage::with_online with explicit HTTP timeouts.
The launcher-facing [with_online] defaults (10 s connect / 300 s
global) are right for interactive pages, but an embedded consumer,
e.g. the tournament provider inside the hook DLL, whose cold-miss
fetch runs on a game thread that must never stall for minutes, needs
tighter bounds (bounded timeouts, blocking HTTP
only on the cold path). Platform-neutral: only the ureq agent config
differs.
Sourcepub fn clear_token(&mut self)
pub fn clear_token(&mut self)
Clear the online token for this instance. Useful for testing unauthorized access.
Sourcepub fn profiles_path(&self) -> PathBuf
pub fn profiles_path(&self) -> PathBuf
Get the profiles directory path
Sourcepub fn contents_path(&self) -> PathBuf
pub fn contents_path(&self) -> PathBuf
Get the contents directory path
Trait Implementations§
Source§impl Clone for GameStorage
impl Clone for GameStorage
Source§fn clone(&self) -> GameStorage
fn clone(&self) -> GameStorage
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more