[oden] Time, path searching, game directory

This commit is contained in:
John Doty 2023-06-30 16:24:54 -07:00
parent 96e95e22ce
commit 26bfcc7a94
8 changed files with 185 additions and 13 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View file

@ -1,22 +0,0 @@
import { cls, print, spr, use_texture } from "./graphics.ts";
import { load_texture } from "./assets.ts";
let the_texture: number | undefined = undefined;
export function init() {
print("Hello world!");
// Start this load, but then...
load_texture("./src/happy-tree.png").then((n) => (the_texture = n));
}
export function update() {}
export function draw() {
cls(0.1, 0.2, 0.3);
if (the_texture != undefined) {
// ...it gets resolved here?
use_texture(the_texture);
spr((320 - 256) / 2, 0, 256, 240, 0, 0);
}
}

View file

@ -3,8 +3,8 @@ use oden_js::{
Context, ContextRef, Result, Runtime, Value,
};
use std::ffi::OsStr;
use std::path::Path;
use std::sync::mpsc::{channel, Receiver};
use std::time::Instant;
pub mod graphics;
use graphics::GraphicsCommand;
@ -13,6 +13,7 @@ mod typescript;
use typescript::transpile_to_javascript;
mod io;
mod time;
struct Loader {}
@ -25,8 +26,8 @@ impl Loader {
impl ModuleLoader for Loader {
fn load(&self, _context: &ContextRef, name: &str) -> Result<ModuleSource> {
eprintln!("Loading {name}...");
let path = Path::new(name);
let contents = std::fs::read_to_string(path)?;
let path = io::resolve_path(name, &["ts"])?;
let contents = std::fs::read_to_string(&path)?;
let contents = if path.extension().and_then(OsStr::to_str) == Some("ts") {
transpile_to_javascript(name, contents)?
} else {
@ -45,6 +46,8 @@ pub struct ScriptContext {
gfx: graphics::GraphicsAPI,
gfx_receive: Receiver<graphics::GraphicsCommand>,
time: time::TimeAPI,
}
impl ScriptContext {
@ -61,9 +64,10 @@ impl ScriptContext {
let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())
.expect("Graphics module should load without error");
let _io = io::IoAPI::define(&context).expect("IO module should load without error");
let time = time::TimeAPI::define(&context).expect("Time module should load without error");
let module = context
.import_module("./src/main.ts", "")
.import_module("./main.ts", "")
.expect("Unable to load main");
let init = module
@ -85,6 +89,8 @@ impl ScriptContext {
gfx,
gfx_receive,
time,
}
}
@ -99,6 +105,10 @@ impl ScriptContext {
}
pub fn update(&mut self) {
// Do we update the frame time before of after async completion?
// Hmmmmm.
self.time.set_frame_time(Instant::now());
// Tell the runtime to process all pending "jobs". This includes
// promise completions.
self.context

View file

@ -1,4 +1,6 @@
use oden_js::{module::native::NativeModuleBuilder, ContextRef, ValueResult};
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
@ -69,10 +71,9 @@ impl IoImpl {
let path = path.to_string();
self.thread_pool.execute(Box::new(move || {
// TODO: Actually read the path.
let path = path;
let result = std::fs::read(path);
let result = resolve_path(&path, &[]).and_then(|p| std::fs::read(p));
promise.resolve(move |ctx: &ContextRef| match result {
Ok(v) => ctx.new_array_buffer(v),
Err(err) => Err(err.into()),
@ -100,3 +101,72 @@ impl IoAPI {
Ok(IoAPI {})
}
}
fn append_ext(ext: impl AsRef<OsStr>, path: PathBuf) -> PathBuf {
let mut os_string: OsString = path.into();
os_string.push(".");
os_string.push(ext.as_ref());
os_string.into()
}
/// Resolve a path to an existing file.
pub fn resolve_path(input: &str, extensions: &[&str]) -> std::io::Result<PathBuf> {
let mut path = PathBuf::with_capacity(input.len());
for segment in input.split('/') {
if segment == "." {
continue;
} else if segment == ".." {
if path.parent().is_none() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("{input} contains too many '..'; directory underflow detected"),
));
}
path.pop();
} else if segment.contains(":") || segment.contains('\\') {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("{input} contains invalid characters"),
));
} else {
path.push(segment);
}
}
if path.has_root() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{input} is absolute"),
));
}
if path.file_name().is_none() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{input} refers to a directory"),
));
}
for prefix in ["game", "src"] {
let mut actual = PathBuf::from(prefix);
actual.push(path.clone());
//eprintln!("Trying '{}'...", actual.to_string_lossy());
match actual.try_exists() {
Ok(true) => return Ok(actual),
_ => (),
}
for extension in extensions.iter() {
let actual = append_ext(extension, actual.clone());
//eprintln!("Trying '{}'...", actual.to_string_lossy());
match actual.try_exists() {
Ok(true) => return Ok(actual),
_ => (),
}
}
}
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{input} not found or not accessible"),
));
}

65
src/script/time.rs Normal file
View file

@ -0,0 +1,65 @@
use oden_js::{module::native::NativeModuleBuilder, ContextRef};
use std::cell::RefCell;
use std::sync::Arc;
use std::time::{Duration, Instant};
struct TimeImpl {
start: Instant,
last_frame_time: Instant,
frame_time: Instant,
}
impl TimeImpl {
fn new() -> Self {
let now = Instant::now();
TimeImpl {
start: now,
frame_time: now,
last_frame_time: now - Duration::from_millis(15),
}
}
fn since_last_frame(&self) -> f64 {
(self.frame_time - self.last_frame_time).as_secs_f64()
}
fn since_start(&self) -> f64 {
(self.frame_time - self.start).as_secs_f64()
}
fn set_frame_time(&mut self, now: Instant) {
self.last_frame_time = self.frame_time;
self.frame_time = now;
}
}
pub struct TimeAPI {
time: Arc<RefCell<TimeImpl>>,
}
impl TimeAPI {
pub fn define(ctx: &ContextRef) -> oden_js::Result<Self> {
let time = Arc::new(RefCell::new(TimeImpl::new()));
let mut builder = NativeModuleBuilder::new(ctx);
{
let time = time.clone();
builder.export(
"since_last_frame",
ctx.new_fn(move |_ctx: &ContextRef| time.borrow().since_last_frame())?,
)?;
}
{
let time = time.clone();
builder.export(
"since_start",
ctx.new_fn(move |_ctx: &ContextRef| time.borrow().since_start())?,
)?;
}
builder.build("time-core")?;
Ok(TimeAPI { time })
}
pub fn set_frame_time(&mut self, now: Instant) {
self.time.borrow_mut().set_frame_time(now);
}
}

13
src/time.ts Normal file
View file

@ -0,0 +1,13 @@
import * as time from "time-core";
/**
* Get the time elasped since the start of the program.
* @returns The time since the start of the program, in fractional seconds.
*/
export const since_start = time.since_start;
/**
* Get the time elasped since the last frame.
* @returns The time since the last frame, in fractional seconds.
*/
export const since_last_frame = time.since_last_frame;