Rust入門實戰 編寫Minecraft啟動器#4下載資源

首發于Enaium的個人博客


首先我們需要添加幾個依賴。

model = { path = "../model" }
parse = { path = "../parse" }
reqwest = { version = "0.12", features = ["blocking", "json"] }
file-hashing = { version = "0.1" }
sha1 = { version = "0.10" }

reqwest用于發送請求,file-hashing用于計算文件的hashsha1用于計算sha1

之后我們需要添加下載的trait

pub trait Download {fn download(&self, game_dir: &Path) -> Result<(), Box<dyn std::error::Error>>;
}

接著我們需要使用Client::builder()來創建一個Client,因為默認的get方法會用有個超時時間,而我們需要設置超時時間為無限。

pub fn get<T: reqwest::IntoUrl>(url: T) -> reqwest::Result<reqwest::blocking::Response> {reqwest::blocking::Client::builder().timeout(None).build()?.get(url).send()
}

最后我們需要創建一個計算文件hash的函數。

pub fn sha1<P: AsRef<Path>>(path: P) -> Result<String, std::io::Error> {let mut hasher = Sha1::new();file_hashing::get_hash_file(path, &mut hasher)
}

之后需要出創建asset.rslibrary.rsversion.rs文件,分別對應下載資源、下載庫、下載游戲版本。

asset.rs

use std::{fs, path::Path};use model::asset::*;
use parse::Parse;use crate::{get, Download};impl Download for AssetIndex {fn download(&self, game_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {println!("Downloading asset index:{}", self.id);let indexes_dir = &game_dir.join("assets").join("indexes");if !indexes_dir.exists() {std::fs::create_dir_all(indexes_dir)?;}let path = &indexes_dir.join(&format!("{}.json", self.id));std::fs::File::create(path)?;let url = &self.url;let text = &get(url)?.text()?;std::fs::write(path, text)?;let index = Index::parse(text)?;let objects_dir = &game_dir.join("assets").join("objects");if !objects_dir.exists() {std::fs::create_dir_all(objects_dir)?;}for (_, value) in index.objects {let hash = &value.hash;let hash_first_two = &hash.chars().take(2).collect::<String>();let first_two_dir = &objects_dir.join(hash_first_two);if !first_two_dir.exists() {std::fs::create_dir_all(first_two_dir)?;}let path = &first_two_dir.join(hash);if path.exists() {if crate::sha1(path)?.eq(hash) {continue;} else {std::fs::remove_file(path)?;}}std::fs::File::create(path)?;let url = format!("https://resources.download.minecraft.net/{}/{}",hash_first_two, hash);println!("Downloading:{}", url);let bytes = get(&url)?.bytes()?;fs::write(path, bytes)?;}Ok(())}
}#[cfg(test)]
mod tests {use super::*;#[test]fn test_asset_index() {let asset_index = model::asset::AssetIndex {id: "17".to_string(),sha1: "fab15439bdef669e389e25e815eee8f1b2aa915e".to_string(),size: 447033,total_size: 799252591,url: "https://piston-meta.mojang.com/v1/packages/fab15439bdef669e389e25e815eee8f1b2aa915e/17.json".to_string(),};let download_path = &std::env::temp_dir().join("rust-minecraft-client-launch");std::fs::create_dir_all(download_path).unwrap_or_else(|err| panic!("{:?}", err));if let Err(err) = asset_index.download(download_path) {panic!("{:?}", err);}}
}

library.rs

use std::path::Path;use model::{library, version::Libraries};use crate::{Download, LibraryAllowed};impl LibraryAllowed for library::Library {fn allowed(&self) -> bool {let mut allowed = true;if self.rules.is_some() {for rule in self.rules.as_ref().unwrap() {if rule.os.name == "osx" && !cfg!(target_os = "macos") {allowed = false;break;} else if rule.os.name == "linux" && !cfg!(target_os = "linux") {allowed = false;break;} else if rule.os.name == "windows" && !cfg!(target_os = "windows") {allowed = false;break;}}}if self.name.contains("natives") {if self.name.contains("x86") && !cfg!(target_arch = "x86") {allowed = false;} else if self.name.contains("arm64") && !cfg!(target_arch = "aarch64") {allowed = false;} else if !cfg!(target_arch = "x86_64") {allowed = false;}}allowed}
}impl Download for Libraries {fn download(&self, game_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {println!("Downloading libraries");let libraries_dir = &game_dir.join("libraries");if !libraries_dir.exists() {std::fs::create_dir_all(libraries_dir)?;}for library in self {if !library.allowed() {continue;}let library_file = &library.downloads.artifact.path;let library_path = &libraries_dir.join(library_file);if !library_path.parent().unwrap().exists() {std::fs::create_dir_all(library_path.parent().unwrap())?;}if library_path.exists() {if crate::sha1(library_path)? == library.downloads.artifact.sha1 {continue;} else {std::fs::remove_file(library_path)?;}}std::fs::File::create(&library_path)?;let url = &library.downloads.artifact.url;println!("Downloading: {}", url);let bytes = crate::get(url)?.bytes()?;std::fs::write(library_path, bytes)?;}Ok(())}
}#[cfg(test)]
mod tests {use super::*;use model::version::Version;#[test]fn test_download() {let game = reqwest::blocking::get("https://piston-meta.mojang.com/v1/packages/177e49d3233cb6eac42f0495c0a48e719870c2ae/1.21.json").unwrap().json::<Version>().unwrap();let download_path = &std::env::temp_dir().join("rust-minecraft-client-launch");std::fs::create_dir_all(download_path).unwrap_or_else(|err| panic!("{:?}", err));if let Err(err) = game.libraries.download(download_path) {panic!("{:?}", err);}}
}

這里我們需要添加一個trait,用于判斷庫是否允許下載。

pub trait LibraryAllowed {fn allowed(&self) -> bool;
}

version.rs

use std::path::Path;use model::version_manifest::Version;use crate::{get, sha1, Download};impl Download for Version {fn download(&self, game_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {let game = get(&self.url)?.json::<model::version::Version>()?;let versions_dir = &game_dir.join(game_dir).join("versions").join(&self.id);if !versions_dir.exists() {std::fs::create_dir_all(versions_dir)?;}game.libraries.download(game_dir)?;game.asset_index.download(game_dir)?;let version_config = &game_dir.join("versions").join(&self.id).join(&format!("{}.json", &self.id));if version_config.exists() {std::fs::remove_file(version_config).unwrap();}std::fs::File::create(version_config).unwrap();std::fs::write(version_config, get(&self.url).unwrap().bytes().unwrap()).unwrap();let path = &versions_dir.join(versions_dir).join(&format!("{}.jar", &self.id));if path.exists() {if sha1(path)? == game.downloads.client.sha1 {return Ok(());} else {std::fs::remove_file(path)?;}}std::fs::File::create(path)?;let bytes = crate::get(&game.downloads.client.url)?.bytes()?;std::fs::write(path, bytes)?;Ok(())}
}#[cfg(test)]
mod tests {use super::*;#[test]fn test_download() {let version = Version {id: "1.21".to_string(),type_: "release".to_string(),url: "https://piston-meta.mojang.com/v1/packages/177e49d3233cb6eac42f0495c0a48e719870c2ae/1.21.json".to_string(),time : "2024-06-13T08:32:38+00:00".to_string(),release_time : "2024-06-13T08:24:03+00:00".to_string(),};let download_path = &std::env::temp_dir().join("rust-minecraft-client-launch");std::fs::create_dir_all(download_path).unwrap_or_else(|err| panic!("{:?}", err));if let Err(err) = version.download(download_path) {panic!("{:?}", err);}}
}

好了,現在我們可以測試下載資源了。

項目地址

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/42860.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/42860.shtml
英文地址,請注明出處:http://en.pswp.cn/web/42860.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Xshell 和寶塔有啥區別

Xshell 和寶塔是兩種不同類型的工具&#xff0c;具有以下顯著區別&#xff1a; 1. 功能和用途 Xshell&#xff1a;主要是一款用于遠程連接服務器的終端模擬軟件。它允許用戶通過 SSH 協議安全地連接到遠程服務器&#xff0c;并在終端中執行命令&#xff0c;進行服務器的管理和…

AI論文作圖——如何表示模型參數凍結狀態

一、LOGO &#x1f525; win10win11 ?? win10win11 二、注意事項&#xff1a; 根據電腦系統&#xff0c;選擇對應的版本。 參考&#xff1a; 【AI論文作圖】如何表示模型參數凍結狀態&#xff1f;

對稱加密和非對稱加密解析

目錄 一、對稱加密二、非對稱加密三、總結 對稱加密和非對稱加密是兩種主要的加密技術&#xff0c;它們在數據安全領域扮演著重要角色。 一、對稱加密 基本原理&#xff1a;對稱加密使用同一個密鑰進行加密和解密。這意味著如果A用某個密鑰加密了信息發送給B&#xff0c;那么B…

Redis數據庫筆記

一、 認識NoSQL SQLNoSQL數據結構結構化非結構化(鍵值類型(Redis)文檔類型(MongoDB)列類型(HBase)Graph類型(Neo4j))數據關聯關聯的無關聯查詢方式SQL查詢非SQL事務特性ACIDBASE存儲方式磁盤內存擴展性垂直水平使用場景數據結構固定;相關業務對數據安全性、一致性要…

【C++中resize和reserve的區別】

1. resize的用法 改變當前容器內含有元素的數量&#xff08;size()&#xff09;比如&#xff1a; vector<int> vct;int num vct.size();//之前的元素個數為num vct.resize(len);//現在的元素個數為len如果num < len &#xff0c;那么容器vct新增len - num個元素&am…

8-選擇靜態或共享庫

在本節中&#xff0c;我們將展示如何使用BUILD_SHARED_LIBS變量來控制add_library()的默認行為&#xff0c;并允許控制如何構建沒有顯式類型的庫(STATIC、SHARED、MODULE或OBJECT)。 要做到這一點&#xff0c;我們需要將BUILD_SHARED_LIBS添加到頂級的CMakeLists.txt中。我…

神經網絡中的激活函數

目錄 一、什么是激活函數&#xff1a;二、如何選擇激活函數&#xff1a;1.Sigmoid激活函數&#xff1a;2.線性激活函數&#xff1a;3.ReLU激活函數&#xff1a; 一、什么是激活函數&#xff1a; 激活函數是神經網絡中的一種函數&#xff0c;它在神經元中起到了非線性映射的作用…

最新 Kubernetes 集群部署 + flannel 網絡插件(保姆級教程,最新 K8S 版本)

資源列表 操作系統配置主機名IP所需插件CentOS 7.92C4Gk8s-master192.168.60.143flannel-cni-plugin、flannel、coredns、etcd、kube-apiserver、kube-controller-manager、kube-proxy、 kube-scheduler 、containerd、pause 、crictlCentOS 7.92C4Gk8s-node01192.168.60.144f…

gitee上傳和下載idea項目的流程

環境&#xff1a;idea2022 一、上傳項目 1、在gitee中新建一個倉庫。 2、打開所要上傳的項目的文件夾&#xff0c;點擊Git Bash&#xff0c;生成.git文件夾。 3、在idea中打開所要上傳的項目&#xff0c;在控制臺的Terminal菜單中&#xff0c;輸入git add . (注意&#xf…

安防綜合管理/視頻匯聚平臺EasyCVR視頻監控存儲技術:高效穩定的視頻數據保障方案

隨著科技的飛速發展&#xff0c;視頻監控已成為現代社會不可或缺的一部分。無論是城市治安、交通管理&#xff0c;還是商業安保、家庭監控&#xff0c;視頻監控都發揮著至關重要的作用。而在這背后&#xff0c;視頻監控存儲技術則是確保監控數據得以長期保存、高效檢索和可靠利…

「C++系列」C++ 修飾符類型

文章目錄 一、C 修飾符類型1. 訪問修飾符&#xff08;Access Modifiers&#xff09;2. 存儲類修飾符&#xff08;Storage Class Specifiers&#xff09;3. 類型修飾符&#xff08;Type Modifiers&#xff09;4. 函數修飾符 二、C 修飾符類型-案例1. 訪問修飾符案例2. 存儲類修飾…

精講:java之多維數組的使用

一、多維數組簡介 1.為什么需要二維數組 我們看下面這個例子&#xff1f;“ 某公司2022年全年各個月份的銷售額進行登記。按月份存儲&#xff0c;可以使用一維數組。如果改寫為按季度為單位存儲怎么辦呢&#xff1f; 或許現在學習了一維數組的你只能申請四個一維數組去存儲每…

【福利】代碼公開!咸魚之王自動答題腳本

轉載請注明出處&#xff1a;小鋒學長生活大爆炸[xfxuezhagn.cn] 如果本文幫助到了你&#xff0c;歡迎[點贊、收藏、關注]哦~ 微信或QQ打開咸魚之王小程序&#xff0c;進入答題界面&#xff0c;運行main.py。期間不要動鼠標。 可自行更改代碼來適配自己的需求~ 可以按照示例圖片…

Kubernetes(k8s)和Docker Compose本質區別

Kubernetes&#xff08;k8s&#xff09;和Docker Compose是兩種不同的容器編排工具&#xff0c;它們有各自的特點和使用場景。 Kubernetes&#xff1a; Kubernetes是一個開源的容器編排平臺&#xff0c;用于自動化計算機軟件的部署、擴展和管理。它支持跨多個主機集群的容器化…

HarmonyOS Next 原生應用開發-從TS到ArkTS的適配規則(四)

一、不支持以#開頭的私有字段 規則&#xff1a;arkts-no-private-identifiers 級別&#xff1a;錯誤 ArkTS不支持使用#符號開頭聲明的私有字段。改用private關鍵字。 TypeScript class C {#foo: number 42 }ArkTS class C {private foo: number 42 }二、類型、命名空間的命…

深入了解線程鎖的使用及鎖的本質

文章目錄 線程鎖的本質局部鎖的使用 鎖的封裝及演示線程饑餓問題 線程加鎖本質可重入和線程安全死鎖問題 根據前面內容的概述, 上述我們已經知道了在linux下關于線程封裝和線程互斥,鎖的相關的概念, 下面就來介紹一下關于線程鎖的一些其他概念. 線程鎖的本質 當這個鎖是全局的…

Codeforces Round #956 (Div. 2) and ByteRace 2024

A. Array Divisibility 思路: 找出特例,發現輸出 1~&#x1d45b; 符合題意。直接輸出1~n即可. 代碼: #include<bits/stdc.h> using namespace std; typedef long long ll; #define N 1000005 ll dp[N], w[N], v[N], h[N]; ll dis[1005][1005]; ll a, b, c, n, m, t;…

iOS 開發技巧 - 使用本地 json 文件

前言 使用本地 json 文件的場景&#xff0c;在我們開發功能的階段&#xff0c;服務端接口字段定義好了后&#xff0c;有些接口響應很慢&#xff0c;請求到響應可能要 幾十秒甚至一分鐘&#xff0c;我們需要頻繁調用接口來調試功能&#xff1b;還有就是調用一些我們需要付費的三…

Ubuntu20.04下修改samba用戶密碼

Ubuntu20.04下修改samba用戶密碼 在Ubuntu系統中&#xff0c;修改samba密碼通常涉及到兩個方面&#xff1a;更改samba用戶的密碼和重置samba服務的密碼數據庫。以下是如何進行操作的步驟&#xff1a; 1、更改samba用戶密碼&#xff1a; 打開終端&#xff0c;使用以下命令更改…

vue打包terser壓縮去除控制臺打印和斷點

情況一&#xff1a; 1、vue-cli搭建 代碼壓縮工具terser在vue-cli里面是自動支持的&#xff0c;所以直接在vue.config.js里面加入下面配置&#xff1a; const {defineConfig} require(vue/cli-service) module.exportsdefineConfig({transpileDependencies:true,terser:{te…