Import Oxide engine (Stages 0–10) under MIT license
Full project snapshot migrated to new Gitea remote without history: engine, editor, physics, script, examples, tests, docs, and assets. Relicensed from GPLv3 to MIT and updated repo URLs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
//! Bundled editor assets — locating the shared `assets/` tree and seeding a
|
||||
//! new project's default content (currently the default UI font).
|
||||
//!
|
||||
//! The editor ships a small set of shared assets (icons, the default UI font, …)
|
||||
//! installed by `install.sh` to `$PREFIX/share/oxide/assets`. At runtime we have
|
||||
//! to find that tree whether the editor is *installed* or run from a *dev*
|
||||
//! checkout, so [`bundled_assets_dir`] resolves it in priority order:
|
||||
//!
|
||||
//! 1. the `OXIDE_ASSETS_DIR` environment variable, if set (explicit override);
|
||||
//! 2. `<exe>/../share/oxide/assets` — the install layout (`bin/` next to
|
||||
//! `share/`);
|
||||
//! 3. `<crate>/../assets` — the repo's top-level `assets/` for `cargo run`.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// The default UI font's path, relative to the bundled `assets/` directory and
|
||||
/// to a project's `assets/` directory (they share the typed-folder layout).
|
||||
///
|
||||
/// Inter (SIL Open Font License) — the variable font's default instance is the
|
||||
/// Regular weight. The license travels next to it as `fonts/OFL.txt`.
|
||||
pub const DEFAULT_UI_FONT_REL: &str = "fonts/InterVariable.ttf";
|
||||
|
||||
/// The default UI font's license file, copied alongside the font so a project
|
||||
/// (and any game exported from it) carries the attribution the OFL requires.
|
||||
pub const DEFAULT_UI_FONT_LICENSE_REL: &str = "fonts/OFL.txt";
|
||||
|
||||
/// Locates the editor's bundled `assets/` directory, or `None` if no candidate
|
||||
/// exists (e.g. a stripped install missing its share tree).
|
||||
pub fn bundled_assets_dir() -> Option<PathBuf> {
|
||||
// 1. Explicit override.
|
||||
if let Some(dir) = std::env::var_os("OXIDE_ASSETS_DIR") {
|
||||
let dir = PathBuf::from(dir);
|
||||
if dir.is_dir() {
|
||||
return Some(dir);
|
||||
}
|
||||
}
|
||||
// 2. Installed layout: <prefix>/bin/oxide-editor + <prefix>/share/oxide/assets.
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(bin_dir) = exe.parent() {
|
||||
if let Some(prefix) = bin_dir.parent() {
|
||||
let installed = prefix.join("share/oxide/assets");
|
||||
if installed.is_dir() {
|
||||
return Some(installed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 3. Dev checkout: the repo's top-level `assets/` sits one level above this
|
||||
// crate (`editor/`).
|
||||
let dev = Path::new(env!("CARGO_MANIFEST_DIR")).join("../assets");
|
||||
dev.is_dir().then_some(dev)
|
||||
}
|
||||
|
||||
/// The absolute path of the bundled default UI font, if the assets tree was
|
||||
/// found and the font is present.
|
||||
pub fn default_ui_font_source() -> Option<PathBuf> {
|
||||
let path = bundled_assets_dir()?.join(DEFAULT_UI_FONT_REL);
|
||||
path.is_file().then_some(path)
|
||||
}
|
||||
|
||||
/// Copies the bundled default UI font (and its license) into `project_assets_dir`
|
||||
/// under the same relative path, unless a file is already there. Returns whether
|
||||
/// the font was newly copied. A missing bundle is a no-op (returns `false`).
|
||||
///
|
||||
/// Called when a project is created so the asset browser has a usable font to
|
||||
/// pick from immediately, referenced by the project-relative path the
|
||||
/// [`AssetDatabase`](oxide_engine::asset::AssetDatabase) records.
|
||||
pub fn seed_default_font(project_assets_dir: &Path) -> std::io::Result<bool> {
|
||||
let Some(src) = default_ui_font_source() else {
|
||||
return Ok(false);
|
||||
};
|
||||
let dst = project_assets_dir.join(DEFAULT_UI_FONT_REL);
|
||||
if dst.exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
if let Some(parent) = dst.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
std::fs::copy(&src, &dst)?;
|
||||
// Best-effort: carry the license next to the font (don't fail the seed if
|
||||
// only the license is missing from the bundle).
|
||||
if let Some(bundle) = bundled_assets_dir() {
|
||||
let lic_src = bundle.join(DEFAULT_UI_FONT_LICENSE_REL);
|
||||
if lic_src.is_file() {
|
||||
let _ = std::fs::copy(
|
||||
lic_src,
|
||||
project_assets_dir.join(DEFAULT_UI_FONT_LICENSE_REL),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bundle_resolves_in_dev_checkout() {
|
||||
// Running tests from the workspace, the dev-checkout fallback (3) finds
|
||||
// the repo's top-level assets/ with the bundled font.
|
||||
let dir = bundled_assets_dir().expect("bundled assets dir should resolve in dev");
|
||||
assert!(
|
||||
dir.join(DEFAULT_UI_FONT_REL).is_file(),
|
||||
"default font present"
|
||||
);
|
||||
assert!(default_ui_font_source().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seed_copies_font_once() {
|
||||
let mut tmp = std::env::temp_dir();
|
||||
tmp.push(format!("oxide_seedfont_{}", std::process::id()));
|
||||
let assets = tmp.join("assets");
|
||||
std::fs::create_dir_all(&assets).unwrap();
|
||||
|
||||
assert!(seed_default_font(&assets).unwrap(), "first seed copies");
|
||||
assert!(assets.join(DEFAULT_UI_FONT_REL).is_file());
|
||||
// Idempotent: a second seed finds the file already present.
|
||||
assert!(
|
||||
!seed_default_font(&assets).unwrap(),
|
||||
"second seed is a no-op"
|
||||
);
|
||||
|
||||
std::fs::remove_dir_all(tmp).ok();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user