1
Fork 0
mirror of https://github.com/redstrate/Auracite.git synced 2025-04-23 05:07:46 +00:00

Make the Dalamud plugin work with the WebAssembly version

This commit is contained in:
Joshua Goins 2024-10-31 23:11:26 -04:00
parent 66f641b7ca
commit acb42a7cb6
6 changed files with 109 additions and 39 deletions

81
Cargo.lock generated
View file

@ -199,14 +199,14 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]] [[package]]
name = "cssparser" name = "cssparser"
version = "0.34.0" version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be"
dependencies = [ dependencies = [
"cssparser-macros", "cssparser-macros",
"dtoa-short", "dtoa-short",
"itoa", "itoa",
"phf", "phf 0.11.2",
"smallvec", "smallvec",
] ]
@ -397,9 +397,9 @@ dependencies = [
[[package]] [[package]]
name = "ego-tree" name = "ego-tree"
version = "0.9.0" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c6ba7d4eec39eaa9ab24d44a0e73a7949a1095a8b3f3abb11eddf27dbb56a53" checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642"
[[package]] [[package]]
name = "either" name = "either"
@ -526,6 +526,15 @@ dependencies = [
"byteorder", "byteorder",
] ]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -578,9 +587,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]] [[package]]
name = "html5ever" name = "html5ever"
version = "0.29.0" version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce" checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
dependencies = [ dependencies = [
"log", "log",
"mac", "mac",
@ -812,13 +821,13 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]] [[package]]
name = "markup5ever" name = "markup5ever"
version = "0.14.0" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5" checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
dependencies = [ dependencies = [
"log", "log",
"phf", "phf 0.11.2",
"phf_codegen", "phf_codegen 0.11.2",
"string_cache", "string_cache",
"string_cache_codegen", "string_cache_codegen",
"tendril", "tendril",
@ -993,6 +1002,15 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "phf"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_shared 0.10.0",
]
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.11.2" version = "0.11.2"
@ -1003,6 +1021,16 @@ dependencies = [
"phf_shared 0.11.2", "phf_shared 0.11.2",
] ]
[[package]]
name = "phf_codegen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
]
[[package]] [[package]]
name = "phf_codegen" name = "phf_codegen"
version = "0.11.2" version = "0.11.2"
@ -1328,15 +1356,16 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "scraper" name = "scraper"
version = "0.21.0" version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0e749d29b2064585327af5038a5a8eb73aeebad4a3472e83531a436563f7208" checksum = "b90460b31bfe1fc07be8262e42c665ad97118d4585869de9345a84d501a9eaf0"
dependencies = [ dependencies = [
"ahash", "ahash",
"cssparser", "cssparser",
"ego-tree", "ego-tree",
"getopts",
"html5ever", "html5ever",
"precomputed-hash", "once_cell",
"selectors", "selectors",
"tendril", "tendril",
] ]
@ -1366,9 +1395,9 @@ dependencies = [
[[package]] [[package]]
name = "selectors" name = "selectors"
version = "0.26.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cssparser", "cssparser",
@ -1376,8 +1405,8 @@ dependencies = [
"fxhash", "fxhash",
"log", "log",
"new_debug_unreachable", "new_debug_unreachable",
"phf", "phf 0.10.1",
"phf_codegen", "phf_codegen 0.10.0",
"precomputed-hash", "precomputed-hash",
"servo_arc", "servo_arc",
"smallvec", "smallvec",
@ -1429,9 +1458,9 @@ dependencies = [
[[package]] [[package]]
name = "servo_arc" name = "servo_arc"
version = "0.4.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44"
dependencies = [ dependencies = [
"stable_deref_trait", "stable_deref_trait",
] ]
@ -1525,9 +1554,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.85" version = "2.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1599,18 +1628,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.65" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.65" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -12,7 +12,8 @@ cxx-qt-build = { git = "https://github.com/KDAB/cxx-qt", branch = "main", featur
[dependencies] [dependencies]
# Used to scrape the Lodestone HTML pages # Used to scrape the Lodestone HTML pages
scraper = { version ="0.21", default-features = false } # NOTE: Do not upgrade to 0.21, as it crashes in WebAssembly for some reason. Report this upstream.
scraper = { version ="0.20" }
# Used to serialize the JSON data we export # Used to serialize the JSON data we export
serde = { version = "1.0", features = ["derive"], default-features = false } serde = { version = "1.0", features = ["derive"], default-features = false }

View file

@ -44,10 +44,17 @@ public class EndStep : IStep
[Route(HttpVerbs.Get, "/package")] [Route(HttpVerbs.Get, "/package")]
public void GetPackage() public void GetPackage()
{ {
Response.Headers.Set(HttpHeaderNames.AccessControlAllowOrigin, "*");
Response.ContentType = MimeType.Json; Response.ContentType = MimeType.Json;
using var writer = HttpContext.OpenResponseText(Encoding.UTF8, true); using var writer = HttpContext.OpenResponseText(Encoding.UTF8, true);
writer.Write(JsonConvert.SerializeObject(Plugin.package)); writer.Write(JsonConvert.SerializeObject(Plugin.package));
}
// TODO: Make this a POST request?
// This is needed since we don't know when the CORS handshake really stops. This really shouldn't be needed though.
[Route(HttpVerbs.Get, "/stop")]
public void Stop()
{
_endStep.End(); _endStep.End();
} }
} }
@ -61,7 +68,6 @@ public class EndStep : IStep
_server = new WebServer(o => o _server = new WebServer(o => o
.WithUrlPrefix("http://localhost:42072/") .WithUrlPrefix("http://localhost:42072/")
.WithMode(HttpListenerMode.EmbedIO)) .WithMode(HttpListenerMode.EmbedIO))
.WithCors("http://localhost:42072/")
.WithWebApi("/", m => m.WithController(() => new Controller(this))); .WithWebApi("/", m => m.WithController(() => new Controller(this)));
_server.RunAsync(); _server.RunAsync();
} }

View file

@ -10,7 +10,7 @@
function archive() { function archive() {
init().then(() => { init().then(() => {
archive_character_base64(document.getElementById("name").value, false).then((uri) => { archive_character_base64(document.getElementById("name").value, document.getElementById("scales").checked).then((uri) => {
// Download character archive // Download character archive
window.location.replace(uri); window.location.replace(uri);
document.getElementById("statusMessage").innerText = "Archive complete!"; document.getElementById("statusMessage").innerText = "Archive complete!";

View file

@ -1,9 +1,14 @@
use reqwest::Url; use reqwest::Url;
pub async fn download(url: &Url) -> Result<Vec<u8>, reqwest::Error> { pub async fn download(url: &Url) -> Result<Vec<u8>, reqwest::Error> {
let client = reqwest::Client::builder() let mut client = reqwest::Client::builder();
.no_proxy() // This fixes localhost connections... for some reason (https://github.com/seanmonstar/reqwest/issues/913)
.build()?; #[cfg(not(target_family = "wasm"))]
{
client = client.no_proxy(); // This fixes localhost connections... for some reason (https://github.com/seanmonstar/reqwest/issues/913)
}
let client = client.build()?;
let body = client.get(url.to_string()) let body = client.get(url.to_string())
.send() .send()

View file

@ -21,8 +21,18 @@ use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
/// The main Lodestone domain
const LODESTONE_HOST: &str = "https://na.finalfantasyxiv.com"; const LODESTONE_HOST: &str = "https://na.finalfantasyxiv.com";
/// The Lodestone proxy used in WebAssembly builds. Needed for CORS and cookie injection.
const LODESTONE_TUNNEL_HOST: &str = "https://lodestone-tunnel.ryne.moe";
/// The image domain.
const IMAGE_HOST: &str = "img2.finalfantasyxiv.com";
/// The image proxy used in WebAssembly builds. Needed for CORS.
const IMAGE_TUNNEL_HOST: &str = "img-tunnel.ryne.moe";
#[derive(Default, Deserialize, Clone)] #[derive(Default, Deserialize, Clone)]
struct Package { struct Package {
playtime: String, playtime: String,
@ -66,13 +76,20 @@ impl From<ArchiveError> for JsValue {
ArchiveError::CharacterNotFound => { JsValue::from_str(&"character_not_found".to_string()) } ArchiveError::CharacterNotFound => { JsValue::from_str(&"character_not_found".to_string()) }
ArchiveError::ParsingError => { JsValue::from_str(&"parsing_error".to_string())} ArchiveError::ParsingError => { JsValue::from_str(&"parsing_error".to_string())}
ArchiveError::UnknownError => { JsValue::from_str(&"unknown_error".to_string()) } ArchiveError::UnknownError => { JsValue::from_str(&"unknown_error".to_string()) }
ArchiveError::CouldNotConnectToDalamud => { JsValue::from_str(&"could_not_connect_to_dalamud".to_string()) }
} }
} }
} }
/// Archives the character named `character_name` and gives a ZIP file as bytes that can be written to disk. /// Archives the character named `character_name` and gives a ZIP file as bytes that can be written to disk.
pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Result<Vec<u8>, ArchiveError> { pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Result<Vec<u8>, ArchiveError> {
let search_url = Url::parse_with_params(&format!("{LODESTONE_HOST}/lodestone/character?"), &[("q", character_name)]).map_err(|_| ArchiveError::UnknownError)?; let lodestone_host = if cfg!(target_family = "wasm") {
LODESTONE_TUNNEL_HOST
} else {
LODESTONE_HOST
};
let search_url = Url::parse_with_params(&format!("{lodestone_host}/lodestone/character?"), &[("q", character_name)]).map_err(|_| ArchiveError::UnknownError)?;
let search_page = download(&search_url) let search_page = download(&search_url)
.await .await
.map_err(|_| ArchiveError::DownloadFailed(search_url.to_string()))?; .map_err(|_| ArchiveError::DownloadFailed(search_url.to_string()))?;
@ -83,7 +100,7 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul
return Err(ArchiveError::CharacterNotFound); return Err(ArchiveError::CharacterNotFound);
} }
let char_page_url = Url::parse(&format!("{LODESTONE_HOST}{}", href)).map_err(|_| ArchiveError::UnknownError)?; let char_page_url = Url::parse(&format!("{lodestone_host}{}", href)).map_err(|_| ArchiveError::UnknownError)?;
let char_page = download(&char_page_url) let char_page = download(&char_page_url)
.await .await
.map_err(|_| ArchiveError::DownloadFailed(char_page_url.to_string()))?; .map_err(|_| ArchiveError::DownloadFailed(char_page_url.to_string()))?;
@ -92,13 +109,17 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul
let mut char_data = parser::parse_lodestone(&char_page); let mut char_data = parser::parse_lodestone(&char_page);
// 2 MiB, for one JSON and two images // 2 MiB, for one JSON and two images
let mut buf = vec![0; 2097152]; let mut buf = Vec::new();
let mut zip = ZipWriter::new(std::io::Cursor::new(&mut buf[..])); let mut zip = ZipWriter::new(std::io::Cursor::new(&mut buf));
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored); let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
if !char_data.portrait_url.is_empty() { if !char_data.portrait_url.is_empty() {
let portrait_url = char_data.portrait_url.replace("img2.finalfantasyxiv.com", "img-tunnel.ryne.moe"); let portrait_url = if cfg!(target_family = "wasm") {
&char_data.portrait_url.replace(IMAGE_HOST, IMAGE_TUNNEL_HOST)
} else {
&char_data.portrait_url
};
let portrait_url = Url::parse(&portrait_url).map_err(|_| ArchiveError::UnknownError)?; let portrait_url = Url::parse(&portrait_url).map_err(|_| ArchiveError::UnknownError)?;
let portrait = download(&portrait_url) let portrait = download(&portrait_url)
@ -109,7 +130,11 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul
zip.write_all(&*portrait)?; zip.write_all(&*portrait)?;
} }
if !char_data.face_url.is_empty() { if !char_data.face_url.is_empty() {
let face_url = char_data.face_url.replace("img2.finalfantasyxiv.com", "img-tunnel.ryne.moe"); let face_url = if cfg!(target_family = "wasm") {
&char_data.face_url.replace(IMAGE_HOST, IMAGE_TUNNEL_HOST)
} else {
&char_data.face_url
};
let face_url = Url::parse(&face_url).map_err(|_| ArchiveError::UnknownError)?; let face_url = Url::parse(&face_url).map_err(|_| ArchiveError::UnknownError)?;
let face = download(&face_url) let face = download(&face_url)
@ -137,6 +162,10 @@ pub async fn archive_character(character_name: &str, use_dalamud: bool) -> Resul
char_data.is_novice = package.is_novice; char_data.is_novice = package.is_novice;
char_data.is_returner = package.is_returner; char_data.is_returner = package.is_returner;
char_data.player_commendations = package.player_commendations; // TODO: fetch from the lodestone? char_data.player_commendations = package.player_commendations; // TODO: fetch from the lodestone?
// Stop the HTTP server
let stop_url = Url::parse(&"http://localhost:42072/stop").map_err(|_| ArchiveError::UnknownError)?;
download(&stop_url).await;
} }
zip.start_file("character.json", options)?; zip.start_file("character.json", options)?;