summaryrefslogtreecommitdiffstats
path: root/src/launcher
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-01-07 03:43:43 -0600
committerLibravatar bigfoot547 <[email protected]>2025-01-07 03:43:43 -0600
commit5d68d164d9a7bff8f3015257f25eb71c44829ddf (patch)
treec6fa8bd5e7c4dc4e14268f3c34138b5bf92d3746 /src/launcher
parentidr what I changed (diff)
untested moment (remove reqwest)
Diffstat (limited to 'src/launcher')
-rw-r--r--src/launcher/constants.rs7
-rw-r--r--src/launcher/download.rs36
-rw-r--r--src/launcher/request.rs139
-rw-r--r--src/launcher/version.rs5
4 files changed, 185 insertions, 2 deletions
diff --git a/src/launcher/constants.rs b/src/launcher/constants.rs
index 8a9bd1a..698081b 100644
--- a/src/launcher/constants.rs
+++ b/src/launcher/constants.rs
@@ -1 +1,8 @@
+use const_format::formatcp;
+
+const PKG_NAME: &str = env!("CARGO_PKG_NAME");
+const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
+const CRATE_NAME: &str = env!("CARGO_CRATE_NAME");
+
+pub const USER_AGENT: &str = formatcp!("{PKG_NAME}/{PKG_VERSION} (in {CRATE_NAME})");
pub const URL_VERSION_MANIFEST: &str = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json";
diff --git a/src/launcher/download.rs b/src/launcher/download.rs
new file mode 100644
index 0000000..4294d33
--- /dev/null
+++ b/src/launcher/download.rs
@@ -0,0 +1,36 @@
+use std::path::{Path, PathBuf};
+use sha1_smol::Digest;
+
+pub trait Download {
+ fn get_url(&self) -> &str;
+ fn get_path(&self) -> &Path;
+ fn get_expect_digest(&self) -> Option<Digest>;
+ fn get_expect_size(&self) -> Option<usize>;
+
+ fn always_redownload(&self) -> bool;
+}
+
+pub type DownloadJob = dyn Download + Sync + Send;
+
+pub struct MultiDownloader<'j, 'js> {
+ jobs: &'js [&'j DownloadJob],
+ nhandles: usize
+}
+
+impl<'j, 'js> MultiDownloader<'j, 'js> {
+ pub fn new(jobs: &'js [&'j DownloadJob]) -> MultiDownloader<'j, 'js> {
+ Self::with_handles(jobs, 8)
+ }
+
+ pub fn with_handles(jobs: &'js [&'j DownloadJob], nhandles: usize) -> MultiDownloader<'j, 'js> {
+ assert!(nhandles > 0);
+
+ MultiDownloader {
+ jobs, nhandles
+ }
+ }
+
+ fn do_it(&self) {
+
+ }
+} \ No newline at end of file
diff --git a/src/launcher/request.rs b/src/launcher/request.rs
new file mode 100644
index 0000000..df89a8b
--- /dev/null
+++ b/src/launcher/request.rs
@@ -0,0 +1,139 @@
+use std::error::Error;
+use std::fmt::Display;
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+use curl::easy::{Easy};
+use tokio::sync::oneshot;
+use tokio::sync::oneshot::Receiver;
+use tokio::task;
+use crate::launcher::constants::USER_AGENT;
+
+// yeah this is basically reqwest but bad (I did not want to rely on both reqwest and curl)
+
+#[derive(Clone, Copy)]
+enum FetchState {
+ Primed,
+ Running,
+ Complete
+}
+
+pub struct EasyFetch {
+ easy: Option<Easy>,
+ state: FetchState,
+ response: Option<Receiver<Result<FetchResult, curl::Error>>>
+}
+
+impl EasyFetch {
+ fn new(easy: Easy) -> Self {
+ EasyFetch {
+ easy: Some(easy),
+ state: FetchState::Primed,
+ response: None
+ }
+ }
+
+ pub fn get(url: &str) -> Self {
+ let mut easy = Easy::new();
+ easy.useragent(USER_AGENT).expect("couldn't set user agent");
+ easy.get(true).expect("couldn't set request method");
+ easy.url(url).expect("couldn't set url");
+
+ Self::new(easy)
+ }
+}
+
+#[derive(Debug)]
+pub struct FetchResult {
+ easy: Easy,
+ response_code: u32,
+ data: Vec<u8>,
+}
+
+#[derive(Debug)]
+pub struct FetchResponseError(u32);
+
+impl FetchResponseError {
+ pub fn get_code(&self) -> u32 {
+ self.0
+ }
+}
+
+impl Display for FetchResponseError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "http response: {}", self.0)
+ }
+}
+
+impl Error for FetchResponseError {}
+
+impl FetchResult {
+ pub fn get_response_code(&self) -> u32 {
+ self.response_code
+ }
+
+ pub fn get_data(&self) -> &[u8] {
+ &self.data
+ }
+
+ pub fn get_data_string(&self) -> String {
+ String::from_utf8_lossy(&self.data).to_string()
+ }
+
+ pub fn error_for_status(self) -> Result<Self, FetchResponseError> {
+ if self.response_code / 100 == 2 {
+ Ok(self)
+ } else {
+ Err(FetchResponseError(self.response_code))
+ }
+ }
+}
+
+impl Future for EasyFetch {
+ type Output = Result<FetchResult, curl::Error>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let self_ref = self.get_mut();
+
+ match &self_ref.state {
+ FetchState::Primed => {
+ self_ref.state = FetchState::Running;
+ let mut easy = self_ref.easy.take().unwrap();
+ let waker = cx.waker().clone();
+
+ let (tx, rx) = oneshot::channel::<Result<FetchResult, curl::Error>>();
+ self_ref.response.replace(rx);
+
+ task::spawn_blocking(move || {
+ let mut out_data: Vec<u8> = Vec::new();
+ let mut transfer = easy.transfer();
+
+ transfer.write_function(|data| {
+ out_data.extend_from_slice(data);
+ Ok(data.len())
+ }).expect("infallible curl operation failed");
+
+ let res = transfer.perform();
+ drop(transfer); // have to explicitly drop to release borrow on "easy"
+
+ out_data.shrink_to_fit();
+
+ tx.send(res.map(|_| FetchResult {
+ response_code: easy.response_code().expect("querying response code should not fail"),
+ data: out_data,
+ easy
+ })).expect("curl fetch reader hangup (this shouldn't happen)");
+ waker.wake();
+ });
+
+ Poll::Pending
+ },
+ FetchState::Running => {
+ self_ref.state = FetchState::Complete;
+ Poll::Ready(self_ref.response.take().unwrap().try_recv()
+ .expect("curl fetch writer hangup or not ready (this shouldn't happen)"))
+ },
+ FetchState::Complete => panic!("fetch polled after completion")
+ }
+ }
+} \ No newline at end of file
diff --git a/src/launcher/version.rs b/src/launcher/version.rs
index f4cdd6c..0337864 100644
--- a/src/launcher/version.rs
+++ b/src/launcher/version.rs
@@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
use log::{debug, info, warn};
use sha1_smol::Digest;
+use super::request::EasyFetch;
use crate::util;
use crate::version::{*, manifest::*};
@@ -18,7 +19,7 @@ struct RemoteVersionList {
impl RemoteVersionList {
async fn new() -> Result<RemoteVersionList, Box<dyn Error>> {
- let text = reqwest::get(URL_VERSION_MANIFEST).await?.error_for_status()?.text().await?;
+ let text = EasyFetch::get(URL_VERSION_MANIFEST).await?.error_for_status()?.get_data_string();
let manifest: VersionManifest = serde_json::from_str(text.as_str())?;
let mut versions = HashMap::new();
@@ -45,7 +46,7 @@ impl RemoteVersionList {
}
// download it
- let ver_text = reqwest::get(ver.url.as_str()).await?.error_for_status()?.text().await?;
+ let ver_text = EasyFetch::get(ver.url.as_str()).await?.error_for_status()?.get_data_string();
// make sure it's valid
util::verify_sha1(ver.sha1, ver_text.as_str())