Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite tauri_plugin_localhost to fix Linux resource loading issues #39

Merged
merged 5 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bins/ayaka-gui/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ flexi_logger = { version = "0.24", default-features = false, features = ["colors
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.2", features = ["cli", "protocol-all", "window-all"] }
tauri-plugin-localhost = "0.1"
tiny_http = "0.12"
minreq = "2.6"
mime_guess = "2.0"
portpicker = "0.1"
trylog = "0.2"

Expand Down
101 changes: 101 additions & 0 deletions bins/ayaka-gui/src-tauri/src/asset_resolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::{path::PathBuf, sync::OnceLock};

Tim-Paik marked this conversation as resolved.
Show resolved Hide resolved
use tauri::{
plugin::{Builder, TauriPlugin},
Runtime,
};
use tiny_http::{Header, Server};

pub(crate) static ROOT_PATH: OnceLock<PathBuf> = OnceLock::new();

pub fn init<R: Runtime>(dev_url: String, port: u16) -> TauriPlugin<R> {
Builder::new("asset_resolver")
.setup(move |app| {
let asset_resolver = app.asset_resolver();
std::thread::spawn(move || {
let server = Server::http(format!("127.0.0.1:{port}"))
.expect("Unable to start local server");
for req in server.incoming_requests() {
let url = req.url().to_string();
Tim-Paik marked this conversation as resolved.
Show resolved Hide resolved
if cfg!(debug_assertions) {
let _ = asset_resolver;
} else if url.starts_with("/assets/")
|| url == "/"
|| url == "/live2d.min.js"
|| url == "/live2dcubismcore.min.js"
{
let asset = asset_resolver.get(url).unwrap();
let mut resp = tiny_http::Response::from_data(asset.bytes);
resp.add_header(
Header::from_bytes("Content-Type", asset.mime_type)
.expect("Unable to convert mime_type to Content-Type"),
);
req.respond(resp).expect("Unable to setup response");
continue;
}
if url.starts_with("/fs/") {
let path = ROOT_PATH
.get()
.unwrap()
.clone()
.join(url.strip_prefix("/fs/").unwrap());
let file = if path.is_file() || path.is_symlink() {
Tim-Paik marked this conversation as resolved.
Show resolved Hide resolved
std::fs::File::open(&path).unwrap()
} else if path.is_dir() {
let mut path = path.clone();
Tim-Paik marked this conversation as resolved.
Show resolved Hide resolved
path.push("index.html");
match std::fs::File::open(path) {
Ok(file) => file,
Err(_) => {
req.respond(tiny_http::Response::empty(404))
.expect("Unable to setup response");
continue;
}
}
} else {
req.respond(tiny_http::Response::empty(404))
.expect("Unable to setup response");
continue;
};
let mut resp = tiny_http::Response::from_file(file);
if let Some(mime) = mime_guess::from_path(url).first() {
resp.add_header(
Header::from_bytes("Content-Type", mime.essence_str())
.expect("Unable to convert mime_type to Content-Type"),
);
}
req.respond(resp).expect("Unable to setup response");
} else if cfg!(debug_assertions) {
let path = if url.ends_with('/') {
url + "index.html"
} else {
url
};
let resp = minreq::get(dev_url.trim_end_matches('/').to_string() + &path)
Tim-Paik marked this conversation as resolved.
Show resolved Hide resolved
.send()
.expect("Unable to send request");
req.respond(tiny_http::Response::new(
resp.status_code.into(),
resp.headers
.iter()
.map(|(k, v)| {
Header::from_bytes(k.as_bytes(), v.as_bytes())
.expect("Unable to convert Header")
})
.collect(),
resp.as_bytes(),
None,
None,
))
.expect("Unable to setup response")
} else {
let _ = dev_url;
req.respond(tiny_http::Response::empty(404))
.expect("Unable to setup response")
}
}
});
Ok(())
})
.build()
}
37 changes: 23 additions & 14 deletions bins/ayaka-gui/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
windows_subsystem = "windows"
)]
#![feature(absolute_path)]
Tim-Paik marked this conversation as resolved.
Show resolved Hide resolved
#![feature(once_cell)]

mod asset_resolver;

use ayaka_runtime::{
anyhow::{self, anyhow, Result},
Expand All @@ -14,9 +17,11 @@ use serde::{Deserialize, Serialize};
use std::{
collections::{HashMap, HashSet},
fmt::Display,
path::PathBuf,
path::Path,
};
use tauri::{
async_runtime::Mutex, command, utils::config::AppUrl, AppHandle, Manager, State, WindowUrl,
};
use tauri::{async_runtime::Mutex, command, AppHandle, Manager, State};
use trylog::TryLog;

type CommandResult<T> = std::result::Result<T, CommandError>;
Expand Down Expand Up @@ -70,7 +75,6 @@ impl OpenGameStatus {
struct Storage {
ident: String,
config: String,
root_path: PathBuf,
records: Mutex<Vec<ActionRecord>>,
context: Mutex<Option<Context>>,
current: Mutex<Option<RawContext>>,
Expand All @@ -81,15 +85,9 @@ struct Storage {
impl Storage {
pub fn new(ident: impl Into<String>, config: impl Into<String>) -> Self {
let config = config.into();
let root_path = std::path::absolute(&config)
.unwrap()
.parent()
.unwrap()
.to_path_buf();
Self {
ident: ident.into(),
config,
root_path,
..Default::default()
}
}
Expand All @@ -113,8 +111,8 @@ impl GameInfo {
}

#[command]
fn absolute_path(storage: State<'_, Storage>, path: String) -> CommandResult<String> {
Ok(storage.root_path.join(path).to_string_lossy().into_owned())
fn absolute_path(_storage: State<'_, Storage>, path: String) -> CommandResult<String> {
Tim-Paik marked this conversation as resolved.
Show resolved Hide resolved
Ok(Path::new("/fs/").join(path).to_string_lossy().into_owned())
}

#[command]
Expand Down Expand Up @@ -426,9 +424,14 @@ fn main() -> Result<()> {
let port =
portpicker::pick_unused_port().ok_or_else(|| anyhow!("failed to find unused port"))?;
info!("Picked port {}", port);
let mut context = tauri::generate_context!();
let window_url = WindowUrl::External(format!("http://127.0.0.1:{port}").parse().unwrap());
let dev_url = context.config().build.dev_path.to_string();
context.config_mut().build.dist_dir = AppUrl::Url(window_url.clone());
context.config_mut().build.dev_path = AppUrl::Url(window_url);
tauri::Builder::default()
.plugin(tauri_plugin_localhost::Builder::new(port).build())
.setup(|app| {
.plugin(asset_resolver::init(dev_url, port))
.setup(move |app| {
let ident = app.config().tauri.bundle.identifier.clone();
let spec = LogSpecification::parse("warn,ayaka=debug")?;
let log_handle = if cfg!(debug_assertions) {
Expand Down Expand Up @@ -467,6 +470,12 @@ fn main() -> Result<()> {
.to_string_lossy()
.into_owned()
});
let root_path = std::path::absolute(&config)
.unwrap()
.parent()
.unwrap()
.to_path_buf();
asset_resolver::ROOT_PATH.set(root_path).unwrap();
app.manage(Storage::new(ident, config));
Ok(())
})
Expand All @@ -493,6 +502,6 @@ fn main() -> Result<()> {
switch,
history,
])
.run(tauri::generate_context!())?;
.run(context)?;
Ok(())
}
4 changes: 2 additions & 2 deletions bins/ayaka-gui/src/interop/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { convertFileSrc, invoke } from "@tauri-apps/api/tauri"
import { invoke } from "@tauri-apps/api/tauri"
import { Locale } from 'vue-i18n'

export async function conv_src(path?: string): Promise<string | undefined> {
if (path) {
return decodeURIComponent(convertFileSrc(await invoke("absolute_path", { path: path })))
return decodeURIComponent(await invoke("absolute_path", { path: path }))
}
return undefined
}
Expand Down