2016-03-16 14:25:35 -04:00
|
|
|
// Copyright 2016 Matthew Collins
|
2015-09-17 11:21:56 -04:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2016-03-16 14:15:13 -04:00
|
|
|
extern crate steven_resources as internal;
|
|
|
|
|
2015-09-17 11:04:25 -04:00
|
|
|
use std::thread;
|
|
|
|
use std::path;
|
|
|
|
use std::io;
|
|
|
|
use std::fs;
|
|
|
|
use std::sync::mpsc;
|
2016-04-24 18:22:10 -04:00
|
|
|
use std::sync::{Arc, Mutex};
|
2016-04-25 08:19:58 -04:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::hash::BuildHasherDefault;
|
|
|
|
use serde_json;
|
2016-04-24 18:22:10 -04:00
|
|
|
|
Replace hyper with reqwest (#7)
An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well
* Begin updating to hyper 0.12.11
https://github.com/iceiix/steven/issues/4#issuecomment-425759778
* Use type variables for hyper::Client
* Fix setting header syntax, Content-Type: application/json, 17->13
* Parse strings into URLs with url.parse::<hyper::Uri>().unwrap()
https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25
* Use hyper::Request::post() then client.request() since client.post() removed
* wait() on the ResponseFuture to get the Result
* try! to unwrap the Result
* status() is now a method
* Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile
* Replace send() with wait() on ResponseFuture
* Parse HeaderValue to u64
* Slices implement std::io::Read trait
* Read into_bytes() instead of read_to_end()
* Disable boxed logger for now to workaround 'expected function, found macro'
* Remove unnecessary mutability, warnings
* Hack to parse twice to avoid double move
* Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper
* Start converting to reqwest: add Protocol::Error and reqwest::Error conversion
* Use reqwest, replacing hyper, in protocol
* Convert resources to use reqwest instead of hyper
* Convert skin download to reqwest, instead of hyper
* Remove hyper
* Revert unnecessary variable name change req/body to reduce diff
* Revert unnecessary whitespace change to reduce diff, align indentation on .
* Fix authenticating to server, wrong method and join URL
* Update Cargo.lock
2018-10-27 20:03:34 -04:00
|
|
|
use reqwest;
|
2016-04-24 18:22:10 -04:00
|
|
|
use zip;
|
|
|
|
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::types::hash::FNVHash;
|
|
|
|
use crate::ui;
|
2015-09-17 11:04:25 -04:00
|
|
|
|
2018-12-02 22:32:52 -05:00
|
|
|
const RESOURCES_VERSION: &str = "1.12.2";
|
|
|
|
const VANILLA_CLIENT_URL: &str = "https://launcher.mojang.com/v1/objects/0f275bc1547d01fa5f56ba34bdc87d981ee12daf/client.jar";
|
|
|
|
const ASSET_VERSION: &str = "1.12";
|
|
|
|
const ASSET_INDEX_URL: &str = "https://launchermeta.mojang.com/mc/assets/1.12/67e29e024e664064c1f04c728604f83c24cbc218/1.12.json";
|
2015-09-17 11:04:25 -04:00
|
|
|
|
2016-03-19 12:32:13 -04:00
|
|
|
pub trait Pack: Sync + Send {
|
2015-09-17 11:04:25 -04:00
|
|
|
fn open(&self, name: &str) -> Option<Box<io::Read>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Manager {
|
|
|
|
packs: Vec<Box<Pack>>,
|
2015-10-07 14:36:59 -04:00
|
|
|
version: usize,
|
2015-09-17 11:04:25 -04:00
|
|
|
|
|
|
|
vanilla_chan: Option<mpsc::Receiver<bool>>,
|
2016-04-25 08:19:58 -04:00
|
|
|
vanilla_assets_chan: Option<mpsc::Receiver<bool>>,
|
2016-04-24 18:22:10 -04:00
|
|
|
vanilla_progress: Arc<Mutex<Progress>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ManagerUI {
|
|
|
|
progress_ui: Vec<ProgressUI>,
|
2016-04-25 08:19:58 -04:00
|
|
|
num_tasks: isize,
|
2016-04-24 18:22:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ProgressUI {
|
|
|
|
task_name: String,
|
|
|
|
task_file: String,
|
|
|
|
position: f64,
|
|
|
|
closing: bool,
|
|
|
|
progress: f64,
|
|
|
|
|
|
|
|
background: ui::ImageRef,
|
|
|
|
progress_bar: ui::ImageRef,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Progress {
|
|
|
|
tasks: Vec<Task>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Task {
|
|
|
|
task_name: String,
|
|
|
|
task_file: String,
|
|
|
|
total: u64,
|
|
|
|
progress: u64,
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
|
2016-03-19 12:32:13 -04:00
|
|
|
unsafe impl Sync for Manager {}
|
|
|
|
|
2015-09-17 11:04:25 -04:00
|
|
|
impl Manager {
|
2016-04-24 18:22:10 -04:00
|
|
|
pub fn new() -> (Manager, ManagerUI) {
|
2015-09-17 11:04:25 -04:00
|
|
|
let mut m = Manager {
|
2015-10-07 14:36:59 -04:00
|
|
|
packs: Vec::new(),
|
|
|
|
version: 0,
|
|
|
|
vanilla_chan: None,
|
2016-04-25 08:19:58 -04:00
|
|
|
vanilla_assets_chan: None,
|
2016-04-24 18:22:10 -04:00
|
|
|
vanilla_progress: Arc::new(Mutex::new(Progress {
|
|
|
|
tasks: vec![],
|
|
|
|
})),
|
2015-09-17 11:04:25 -04:00
|
|
|
};
|
2015-09-19 14:08:28 -04:00
|
|
|
m.add_pack(Box::new(InternalPack));
|
2015-09-17 11:04:25 -04:00
|
|
|
m.download_vanilla();
|
2016-04-25 08:19:58 -04:00
|
|
|
m.download_assets();
|
|
|
|
(m, ManagerUI { progress_ui: vec!{}, num_tasks: 0 })
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
|
2015-10-07 14:36:59 -04:00
|
|
|
/// Returns the 'version' of the manager. The version is
|
2015-09-17 11:04:25 -04:00
|
|
|
/// increase everytime a pack is added or removed.
|
|
|
|
pub fn version(&self) -> usize {
|
|
|
|
self.version
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn open(&self, plugin: &str, name: &str) -> Option<Box<io::Read>> {
|
2016-04-25 08:19:58 -04:00
|
|
|
let path = format!("assets/{}/{}", plugin, name);
|
2015-09-17 11:04:25 -04:00
|
|
|
for pack in self.packs.iter().rev() {
|
2016-03-26 10:24:26 -04:00
|
|
|
if let Some(val) = pack.open(&path) {
|
|
|
|
return Some(val);
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn open_all(&self, plugin: &str, name: &str) -> Vec<Box<io::Read>> {
|
|
|
|
let mut ret = Vec::new();
|
2016-04-25 08:19:58 -04:00
|
|
|
let path = format!("assets/{}/{}", plugin, name);
|
2015-09-17 11:04:25 -04:00
|
|
|
for pack in self.packs.iter().rev() {
|
2016-03-26 10:24:26 -04:00
|
|
|
if let Some(val) = pack.open(&path) {
|
|
|
|
ret.push(val);
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
2016-04-24 18:22:10 -04:00
|
|
|
pub fn tick(&mut self, mui: &mut ManagerUI, ui_container: &mut ui::Container, delta: f64) {
|
2016-04-25 08:19:58 -04:00
|
|
|
let delta = delta.min(5.0);
|
2015-10-07 14:36:59 -04:00
|
|
|
// Check to see if the download of vanilla has completed
|
2015-09-17 11:04:25 -04:00
|
|
|
// (if it was started)
|
|
|
|
let mut done = false;
|
|
|
|
if let Some(ref recv) = self.vanilla_chan {
|
|
|
|
if let Ok(_) = recv.try_recv() {
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if done {
|
|
|
|
self.vanilla_chan = None;
|
|
|
|
self.load_vanilla();
|
|
|
|
}
|
2016-04-25 08:19:58 -04:00
|
|
|
let mut done = false;
|
|
|
|
if let Some(ref recv) = self.vanilla_assets_chan {
|
|
|
|
if let Ok(_) = recv.try_recv() {
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if done {
|
|
|
|
self.vanilla_assets_chan = None;
|
|
|
|
self.load_assets();
|
|
|
|
}
|
2016-04-24 18:22:10 -04:00
|
|
|
|
|
|
|
const UI_HEIGHT: f64 = 32.0;
|
|
|
|
|
|
|
|
let mut progress = self.vanilla_progress.lock().unwrap();
|
|
|
|
progress.tasks.retain(|v| v.progress < v.total);
|
|
|
|
// Find out what we have to work with
|
|
|
|
for task in &progress.tasks {
|
|
|
|
if !mui.progress_ui.iter()
|
|
|
|
.filter(|v| v.task_file == task.task_file)
|
|
|
|
.any(|v| v.task_name == task.task_name) {
|
2016-04-25 08:19:58 -04:00
|
|
|
mui.num_tasks += 1;
|
2016-04-24 18:22:10 -04:00
|
|
|
// Add a ui element for it
|
|
|
|
let background = ui::ImageBuilder::new()
|
|
|
|
.texture("steven:solid")
|
|
|
|
.position(0.0, -UI_HEIGHT)
|
2016-04-25 08:19:58 -04:00
|
|
|
.size(350.0, UI_HEIGHT)
|
2016-04-24 18:22:10 -04:00
|
|
|
.colour((0, 0, 0, 100))
|
2016-04-25 08:19:58 -04:00
|
|
|
.draw_index(0xFFFFFF - mui.num_tasks)
|
2016-04-24 18:22:10 -04:00
|
|
|
.alignment(ui::VAttach::Bottom, ui::HAttach::Left)
|
|
|
|
.create(ui_container);
|
|
|
|
|
|
|
|
ui::ImageBuilder::new()
|
|
|
|
.texture("steven:solid")
|
|
|
|
.position(0.0, 0.0)
|
2016-04-25 08:19:58 -04:00
|
|
|
.size(350.0, 10.0)
|
2016-04-24 18:22:10 -04:00
|
|
|
.colour((0, 0, 0, 200))
|
|
|
|
.attach(&mut *background.borrow_mut());
|
|
|
|
ui::TextBuilder::new()
|
|
|
|
.text(&*task.task_name)
|
|
|
|
.position(3.0, 0.0)
|
|
|
|
.scale_x(0.5)
|
|
|
|
.scale_y(0.5)
|
|
|
|
.draw_index(1)
|
|
|
|
.attach(&mut *background.borrow_mut());
|
|
|
|
ui::TextBuilder::new()
|
|
|
|
.text(&*task.task_file)
|
|
|
|
.position(3.0, 12.0)
|
|
|
|
.scale_x(0.5)
|
|
|
|
.scale_y(0.5)
|
|
|
|
.draw_index(1)
|
|
|
|
.attach(&mut *background.borrow_mut());
|
|
|
|
|
|
|
|
let progress_bar = ui::ImageBuilder::new()
|
|
|
|
.texture("steven:solid")
|
|
|
|
.position(0.0, 0.0)
|
|
|
|
.size(0.0, 10.0)
|
|
|
|
.colour((0, 255, 0, 255))
|
2016-04-25 08:19:58 -04:00
|
|
|
.draw_index(2)
|
2016-04-24 18:22:10 -04:00
|
|
|
.alignment(ui::VAttach::Bottom, ui::HAttach::Left)
|
|
|
|
.attach(&mut *background.borrow_mut());
|
|
|
|
|
|
|
|
mui.progress_ui.push(ProgressUI {
|
|
|
|
task_name: task.task_name.clone(),
|
|
|
|
task_file: task.task_file.clone(),
|
|
|
|
position: -UI_HEIGHT,
|
|
|
|
closing: false,
|
|
|
|
progress: 0.0,
|
2018-11-04 16:43:30 -05:00
|
|
|
background,
|
|
|
|
progress_bar,
|
2016-04-24 18:22:10 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for ui in &mut mui.progress_ui {
|
2016-04-25 08:19:58 -04:00
|
|
|
if ui.closing {
|
|
|
|
continue;
|
|
|
|
}
|
2016-04-24 18:22:10 -04:00
|
|
|
let mut found = false;
|
|
|
|
let mut prog = 1.0;
|
|
|
|
for task in progress.tasks.iter()
|
|
|
|
.filter(|v| v.task_file == ui.task_file)
|
|
|
|
.filter(|v| v.task_name == ui.task_name) {
|
|
|
|
found = true;
|
|
|
|
prog = task.progress as f64 / task.total as f64;
|
|
|
|
}
|
2016-04-25 08:19:58 -04:00
|
|
|
let background = ui.background.borrow();
|
|
|
|
let bar = ui.progress_bar.borrow();
|
|
|
|
// Let the progress bar finish
|
|
|
|
if !found && (background.y - ui.position).abs() < 0.7 * delta && (bar.width - 350.0).abs() < 1.0 * delta {
|
2016-04-24 18:22:10 -04:00
|
|
|
ui.closing = true;
|
|
|
|
ui.position = -UI_HEIGHT;
|
|
|
|
}
|
|
|
|
ui.progress = prog;
|
|
|
|
}
|
|
|
|
let mut offset = 0.0;
|
|
|
|
for ui in &mut mui.progress_ui {
|
|
|
|
if ui.closing {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ui.position = offset;
|
|
|
|
offset += UI_HEIGHT;
|
|
|
|
}
|
|
|
|
// Move elements
|
|
|
|
for ui in &mut mui.progress_ui {
|
|
|
|
let mut background = ui.background.borrow_mut();
|
|
|
|
if (background.y - ui.position).abs() < 0.7 * delta {
|
|
|
|
background.y = ui.position;
|
|
|
|
} else {
|
|
|
|
background.y += (ui.position - background.y).signum() * 0.7 * delta;
|
|
|
|
}
|
|
|
|
let mut bar = ui.progress_bar.borrow_mut();
|
2016-04-25 08:19:58 -04:00
|
|
|
let target_size = (350.0 * ui.progress).min(350.0);
|
2016-04-24 18:22:10 -04:00
|
|
|
if (bar.width - target_size).abs() < 1.0 * delta {
|
|
|
|
bar.width = target_size;
|
|
|
|
} else {
|
|
|
|
bar.width += ((target_size - bar.width).signum() * delta).max(0.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up dead elements
|
2016-04-25 08:19:58 -04:00
|
|
|
mui.progress_ui.retain(|v| v.position >= -UI_HEIGHT || !v.closing);
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn add_pack(&mut self, pck: Box<Pack>) {
|
|
|
|
self.packs.push(pck);
|
|
|
|
self.version += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_vanilla(&mut self) {
|
|
|
|
let loc = format!("./resources-{}", RESOURCES_VERSION);
|
|
|
|
let location = path::Path::new(&loc);
|
2016-04-25 08:19:58 -04:00
|
|
|
self.packs.insert(1, Box::new(DirPack { root: location.to_path_buf() }));
|
|
|
|
self.version += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_assets(&mut self) {
|
|
|
|
self.packs.insert(1, Box::new(ObjectPack::new()));
|
|
|
|
self.version += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn download_assets(&mut self) {
|
|
|
|
let loc = format!("./index/{}.json", ASSET_VERSION);
|
|
|
|
let location = path::Path::new(&loc).to_owned();
|
|
|
|
let progress_info = self.vanilla_progress.clone();
|
|
|
|
let (send, recv) = mpsc::channel();
|
|
|
|
if fs::metadata(&location).is_ok(){
|
|
|
|
self.load_assets();
|
|
|
|
} else {
|
|
|
|
self.vanilla_assets_chan = Some(recv);
|
|
|
|
}
|
|
|
|
thread::spawn(move || {
|
Replace hyper with reqwest (#7)
An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well
* Begin updating to hyper 0.12.11
https://github.com/iceiix/steven/issues/4#issuecomment-425759778
* Use type variables for hyper::Client
* Fix setting header syntax, Content-Type: application/json, 17->13
* Parse strings into URLs with url.parse::<hyper::Uri>().unwrap()
https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25
* Use hyper::Request::post() then client.request() since client.post() removed
* wait() on the ResponseFuture to get the Result
* try! to unwrap the Result
* status() is now a method
* Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile
* Replace send() with wait() on ResponseFuture
* Parse HeaderValue to u64
* Slices implement std::io::Read trait
* Read into_bytes() instead of read_to_end()
* Disable boxed logger for now to workaround 'expected function, found macro'
* Remove unnecessary mutability, warnings
* Hack to parse twice to avoid double move
* Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper
* Start converting to reqwest: add Protocol::Error and reqwest::Error conversion
* Use reqwest, replacing hyper, in protocol
* Convert resources to use reqwest instead of hyper
* Convert skin download to reqwest, instead of hyper
* Remove hyper
* Revert unnecessary variable name change req/body to reduce diff
* Revert unnecessary whitespace change to reduce diff, align indentation on .
* Fix authenticating to server, wrong method and join URL
* Update Cargo.lock
2018-10-27 20:03:34 -04:00
|
|
|
let client = reqwest::Client::new();
|
2016-04-25 08:19:58 -04:00
|
|
|
if fs::metadata(&location).is_err(){
|
|
|
|
fs::create_dir_all(location.parent().unwrap()).unwrap();
|
|
|
|
let res = client.get(ASSET_INDEX_URL)
|
|
|
|
.send()
|
|
|
|
.unwrap();
|
|
|
|
|
Replace hyper with reqwest (#7)
An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well
* Begin updating to hyper 0.12.11
https://github.com/iceiix/steven/issues/4#issuecomment-425759778
* Use type variables for hyper::Client
* Fix setting header syntax, Content-Type: application/json, 17->13
* Parse strings into URLs with url.parse::<hyper::Uri>().unwrap()
https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25
* Use hyper::Request::post() then client.request() since client.post() removed
* wait() on the ResponseFuture to get the Result
* try! to unwrap the Result
* status() is now a method
* Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile
* Replace send() with wait() on ResponseFuture
* Parse HeaderValue to u64
* Slices implement std::io::Read trait
* Read into_bytes() instead of read_to_end()
* Disable boxed logger for now to workaround 'expected function, found macro'
* Remove unnecessary mutability, warnings
* Hack to parse twice to avoid double move
* Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper
* Start converting to reqwest: add Protocol::Error and reqwest::Error conversion
* Use reqwest, replacing hyper, in protocol
* Convert resources to use reqwest instead of hyper
* Convert skin download to reqwest, instead of hyper
* Remove hyper
* Revert unnecessary variable name change req/body to reduce diff
* Revert unnecessary whitespace change to reduce diff, align indentation on .
* Fix authenticating to server, wrong method and join URL
* Update Cargo.lock
2018-10-27 20:03:34 -04:00
|
|
|
let length = res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap().to_str().unwrap().parse::<u64>().unwrap();
|
|
|
|
Self::add_task(&progress_info, "Downloading Asset Index", &*location.to_string_lossy(), length);
|
2016-04-25 08:19:58 -04:00
|
|
|
{
|
|
|
|
let mut file = fs::File::create(format!("index-{}.tmp", ASSET_VERSION)).unwrap();
|
|
|
|
let mut progress = ProgressRead {
|
|
|
|
read: res,
|
|
|
|
progress: &progress_info,
|
|
|
|
task_name: "Downloading Asset Index".into(),
|
|
|
|
task_file: location.to_string_lossy().into_owned(),
|
|
|
|
};
|
|
|
|
io::copy(&mut progress, &mut file).unwrap();
|
|
|
|
}
|
|
|
|
fs::rename(format!("index-{}.tmp", ASSET_VERSION), &location).unwrap();
|
|
|
|
send.send(true).unwrap();
|
|
|
|
}
|
|
|
|
let file = fs::File::open(&location).unwrap();
|
|
|
|
let index: serde_json::Value = serde_json::from_reader(&file).unwrap();
|
|
|
|
let root_location = path::Path::new("./objects/");
|
2018-10-23 21:47:21 -04:00
|
|
|
let objects = index.get("objects").and_then(|v| v.as_object()).unwrap();
|
2016-04-25 08:19:58 -04:00
|
|
|
Self::add_task(&progress_info, "Downloading Assets", "./objects", objects.len() as u64);
|
|
|
|
for (k, v) in objects {
|
2018-10-23 21:47:21 -04:00
|
|
|
let hash = v.get("hash").and_then(|v| v.as_str()).unwrap();
|
2016-04-25 08:19:58 -04:00
|
|
|
let hash_path = format!("{}/{}", &hash[..2], hash);
|
|
|
|
let location = root_location.join(&hash_path);
|
|
|
|
if fs::metadata(&location).is_err(){
|
|
|
|
fs::create_dir_all(location.parent().unwrap()).unwrap();
|
|
|
|
let res = client.get(&format!("http://resources.download.minecraft.net/{}", hash_path))
|
|
|
|
.send()
|
|
|
|
.unwrap();
|
2018-10-23 21:47:21 -04:00
|
|
|
let length = v.get("size").and_then(|v| v.as_u64()).unwrap();
|
2016-04-25 08:19:58 -04:00
|
|
|
Self::add_task(&progress_info, "Downloading Asset", k, length);
|
|
|
|
let mut tmp_file = location.to_owned();
|
|
|
|
tmp_file.set_file_name(format!("{}.tmp", hash));
|
|
|
|
{
|
|
|
|
let mut file = fs::File::create(&tmp_file).unwrap();
|
|
|
|
let mut progress = ProgressRead {
|
|
|
|
read: res,
|
|
|
|
progress: &progress_info,
|
|
|
|
task_name: "Downloading Asset".into(),
|
|
|
|
task_file: k.to_owned(),
|
|
|
|
};
|
|
|
|
io::copy(&mut progress, &mut file).unwrap();
|
|
|
|
}
|
|
|
|
fs::rename(&tmp_file, &location).unwrap();
|
|
|
|
}
|
|
|
|
Self::add_task_progress(&progress_info, "Downloading Assets", "./objects", 1);
|
|
|
|
}
|
|
|
|
});
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn download_vanilla(&mut self) {
|
|
|
|
let loc = format!("./resources-{}", RESOURCES_VERSION);
|
|
|
|
let location = path::Path::new(&loc);
|
|
|
|
if fs::metadata(location.join("steven.assets")).is_ok() {
|
|
|
|
self.load_vanilla();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let (send, recv) = mpsc::channel();
|
|
|
|
self.vanilla_chan = Some(recv);
|
|
|
|
|
2016-04-24 18:22:10 -04:00
|
|
|
let progress_info = self.vanilla_progress.clone();
|
2015-09-17 11:04:25 -04:00
|
|
|
thread::spawn(move || {
|
Replace hyper with reqwest (#7)
An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well
* Begin updating to hyper 0.12.11
https://github.com/iceiix/steven/issues/4#issuecomment-425759778
* Use type variables for hyper::Client
* Fix setting header syntax, Content-Type: application/json, 17->13
* Parse strings into URLs with url.parse::<hyper::Uri>().unwrap()
https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25
* Use hyper::Request::post() then client.request() since client.post() removed
* wait() on the ResponseFuture to get the Result
* try! to unwrap the Result
* status() is now a method
* Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile
* Replace send() with wait() on ResponseFuture
* Parse HeaderValue to u64
* Slices implement std::io::Read trait
* Read into_bytes() instead of read_to_end()
* Disable boxed logger for now to workaround 'expected function, found macro'
* Remove unnecessary mutability, warnings
* Hack to parse twice to avoid double move
* Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper
* Start converting to reqwest: add Protocol::Error and reqwest::Error conversion
* Use reqwest, replacing hyper, in protocol
* Convert resources to use reqwest instead of hyper
* Convert skin download to reqwest, instead of hyper
* Remove hyper
* Revert unnecessary variable name change req/body to reduce diff
* Revert unnecessary whitespace change to reduce diff, align indentation on .
* Fix authenticating to server, wrong method and join URL
* Update Cargo.lock
2018-10-27 20:03:34 -04:00
|
|
|
let client = reqwest::Client::new();
|
2016-04-24 18:22:10 -04:00
|
|
|
let res = client.get(VANILLA_CLIENT_URL)
|
2015-10-07 14:36:59 -04:00
|
|
|
.send()
|
|
|
|
.unwrap();
|
2015-09-17 11:04:25 -04:00
|
|
|
let mut file = fs::File::create(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
|
|
|
|
Replace hyper with reqwest (#7)
An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well
* Begin updating to hyper 0.12.11
https://github.com/iceiix/steven/issues/4#issuecomment-425759778
* Use type variables for hyper::Client
* Fix setting header syntax, Content-Type: application/json, 17->13
* Parse strings into URLs with url.parse::<hyper::Uri>().unwrap()
https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25
* Use hyper::Request::post() then client.request() since client.post() removed
* wait() on the ResponseFuture to get the Result
* try! to unwrap the Result
* status() is now a method
* Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile
* Replace send() with wait() on ResponseFuture
* Parse HeaderValue to u64
* Slices implement std::io::Read trait
* Read into_bytes() instead of read_to_end()
* Disable boxed logger for now to workaround 'expected function, found macro'
* Remove unnecessary mutability, warnings
* Hack to parse twice to avoid double move
* Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper
* Start converting to reqwest: add Protocol::Error and reqwest::Error conversion
* Use reqwest, replacing hyper, in protocol
* Convert resources to use reqwest instead of hyper
* Convert skin download to reqwest, instead of hyper
* Remove hyper
* Revert unnecessary variable name change req/body to reduce diff
* Revert unnecessary whitespace change to reduce diff, align indentation on .
* Fix authenticating to server, wrong method and join URL
* Update Cargo.lock
2018-10-27 20:03:34 -04:00
|
|
|
let length = res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap().to_str().unwrap().parse::<u64>().unwrap();
|
2016-04-24 18:22:10 -04:00
|
|
|
let task_file = format!("./resources-{}", RESOURCES_VERSION);
|
Replace hyper with reqwest (#7)
An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well
* Begin updating to hyper 0.12.11
https://github.com/iceiix/steven/issues/4#issuecomment-425759778
* Use type variables for hyper::Client
* Fix setting header syntax, Content-Type: application/json, 17->13
* Parse strings into URLs with url.parse::<hyper::Uri>().unwrap()
https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25
* Use hyper::Request::post() then client.request() since client.post() removed
* wait() on the ResponseFuture to get the Result
* try! to unwrap the Result
* status() is now a method
* Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile
* Replace send() with wait() on ResponseFuture
* Parse HeaderValue to u64
* Slices implement std::io::Read trait
* Read into_bytes() instead of read_to_end()
* Disable boxed logger for now to workaround 'expected function, found macro'
* Remove unnecessary mutability, warnings
* Hack to parse twice to avoid double move
* Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper
* Start converting to reqwest: add Protocol::Error and reqwest::Error conversion
* Use reqwest, replacing hyper, in protocol
* Convert resources to use reqwest instead of hyper
* Convert skin download to reqwest, instead of hyper
* Remove hyper
* Revert unnecessary variable name change req/body to reduce diff
* Revert unnecessary whitespace change to reduce diff, align indentation on .
* Fix authenticating to server, wrong method and join URL
* Update Cargo.lock
2018-10-27 20:03:34 -04:00
|
|
|
Self::add_task(&progress_info, "Downloading Core Assets", &task_file, length);
|
2016-04-24 18:22:10 -04:00
|
|
|
{
|
|
|
|
let mut progress = ProgressRead {
|
|
|
|
read: res,
|
|
|
|
progress: &progress_info,
|
|
|
|
task_name: "Downloading Core Assets".into(),
|
2018-11-04 16:43:30 -05:00
|
|
|
task_file,
|
2016-04-24 18:22:10 -04:00
|
|
|
};
|
|
|
|
io::copy(&mut progress, &mut file).unwrap();
|
|
|
|
}
|
2015-09-17 11:04:25 -04:00
|
|
|
|
|
|
|
// Copy the resources from the zip
|
|
|
|
let file = fs::File::open(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
|
|
|
let mut zip = zip::ZipArchive::new(file).unwrap();
|
|
|
|
|
2016-04-24 18:22:10 -04:00
|
|
|
let task_file = format!("./resources-{}", RESOURCES_VERSION);
|
|
|
|
Self::add_task(&progress_info, "Unpacking Core Assets", &task_file, zip.len() as u64);
|
|
|
|
|
2015-09-17 11:04:25 -04:00
|
|
|
let loc = format!("./resources-{}", RESOURCES_VERSION);
|
|
|
|
let location = path::Path::new(&loc);
|
|
|
|
let count = zip.len();
|
2015-10-07 14:36:59 -04:00
|
|
|
for i in 0..count {
|
2016-04-24 18:22:10 -04:00
|
|
|
Self::add_task_progress(&progress_info, "Unpacking Core Assets", &task_file, 1);
|
2015-09-17 11:04:25 -04:00
|
|
|
let mut file = zip.by_index(i).unwrap();
|
|
|
|
if !file.name().starts_with("assets/") {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let path = location.join(file.name());
|
|
|
|
fs::create_dir_all(path.parent().unwrap()).unwrap();
|
|
|
|
let mut out = fs::File::create(path).unwrap();
|
|
|
|
io::copy(&mut file, &mut out).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
fs::File::create(location.join("steven.assets")).unwrap(); // Marker file
|
|
|
|
send.send(true).unwrap();
|
|
|
|
|
|
|
|
fs::remove_file(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
|
|
|
});
|
|
|
|
}
|
2016-04-24 18:22:10 -04:00
|
|
|
|
|
|
|
fn add_task(progress: &Arc<Mutex<Progress>>, name: &str, file: &str, length: u64) {
|
|
|
|
let mut info = progress.lock().unwrap();
|
|
|
|
info.tasks.push(Task {
|
|
|
|
task_name: name.into(),
|
|
|
|
task_file: file.into(),
|
|
|
|
total: length,
|
|
|
|
progress: 0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_task_progress(progress: &Arc<Mutex<Progress>>, name: &str, file: &str, prog: u64) {
|
|
|
|
let mut progress = progress.lock().unwrap();
|
|
|
|
for task in progress.tasks.iter_mut()
|
|
|
|
.filter(|v| v.task_file == file)
|
|
|
|
.filter(|v| v.task_name == name) {
|
|
|
|
task.progress += prog as u64;
|
|
|
|
}
|
|
|
|
}
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct DirPack {
|
|
|
|
root: path::PathBuf,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Pack for DirPack {
|
|
|
|
fn open(&self, name: &str) -> Option<Box<io::Read>> {
|
|
|
|
match fs::File::open(self.root.join(name)) {
|
|
|
|
Ok(val) => Some(Box::new(val)),
|
|
|
|
Err(_) => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-19 14:08:28 -04:00
|
|
|
struct InternalPack;
|
|
|
|
|
|
|
|
impl Pack for InternalPack {
|
|
|
|
fn open(&self, name: &str) -> Option<Box<io::Read>> {
|
|
|
|
match internal::get_file(name) {
|
|
|
|
Some(val) => Some(Box::new(io::Cursor::new(val))),
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-25 08:19:58 -04:00
|
|
|
struct ObjectPack {
|
|
|
|
objects: HashMap<String, String, BuildHasherDefault<FNVHash>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ObjectPack {
|
|
|
|
fn new() -> ObjectPack {
|
|
|
|
let loc = format!("./index/{}.json", ASSET_VERSION);
|
|
|
|
let location = path::Path::new(&loc);
|
|
|
|
let file = fs::File::open(&location).unwrap();
|
|
|
|
let index: serde_json::Value = serde_json::from_reader(&file).unwrap();
|
2018-10-23 21:47:21 -04:00
|
|
|
let objects = index.get("objects").and_then(|v| v.as_object()).unwrap();
|
2016-04-25 08:19:58 -04:00
|
|
|
let mut hash_objs = HashMap::with_hasher(BuildHasherDefault::default());
|
|
|
|
for (k, v) in objects {
|
2018-10-23 21:47:21 -04:00
|
|
|
hash_objs.insert(k.clone(), v.get("hash").and_then(|v| v.as_str()).unwrap().to_owned());
|
2016-04-25 08:19:58 -04:00
|
|
|
}
|
|
|
|
ObjectPack {
|
|
|
|
objects: hash_objs,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Pack for ObjectPack {
|
|
|
|
fn open(&self, name: &str) -> Option<Box<io::Read>> {
|
|
|
|
if !name.starts_with("assets/") {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let name = &name["assets/".len()..];
|
|
|
|
if let Some(hash) = self.objects.get(name) {
|
|
|
|
let root_location = path::Path::new("./objects/");
|
|
|
|
let hash_path = format!("{}/{}", &hash[..2], hash);
|
|
|
|
let location = root_location.join(&hash_path);
|
|
|
|
match fs::File::open(location) {
|
|
|
|
Ok(val) => Some(Box::new(val)),
|
|
|
|
Err(_) => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-24 18:22:10 -04:00
|
|
|
struct ProgressRead<'a, T> {
|
2015-09-17 11:04:25 -04:00
|
|
|
read: T,
|
2016-04-24 18:22:10 -04:00
|
|
|
progress: &'a Arc<Mutex<Progress>>,
|
|
|
|
task_name: String,
|
|
|
|
task_file: String,
|
2015-09-17 11:04:25 -04:00
|
|
|
}
|
|
|
|
|
2016-04-24 18:22:10 -04:00
|
|
|
impl <'a, T: io::Read> io::Read for ProgressRead<'a, T> {
|
2015-09-17 11:04:25 -04:00
|
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
2018-11-04 14:34:53 -05:00
|
|
|
let size = self.read.read(buf)?;
|
2016-04-24 18:22:10 -04:00
|
|
|
Manager::add_task_progress(self.progress, &self.task_name, &self.task_file, size as u64);
|
2015-09-17 11:04:25 -04:00
|
|
|
Ok(size)
|
|
|
|
}
|
|
|
|
}
|