Update client url and add an in-game progress bar for the download (Fixes #22)
This commit is contained in:
parent
f8be801eac
commit
f23c16bff2
18
src/main.rs
18
src/main.rs
|
@ -17,6 +17,7 @@
|
||||||
#![feature(rc_would_unwrap)]
|
#![feature(rc_would_unwrap)]
|
||||||
|
|
||||||
extern crate sdl2;
|
extern crate sdl2;
|
||||||
|
extern crate zip;
|
||||||
extern crate image;
|
extern crate image;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
@ -179,10 +180,8 @@ fn main() {
|
||||||
|
|
||||||
info!("Starting steven");
|
info!("Starting steven");
|
||||||
|
|
||||||
let resource_manager = Arc::new(RwLock::new(resources::Manager::new()));
|
let (res, mut resui) = resources::Manager::new();
|
||||||
{
|
let resource_manager = Arc::new(RwLock::new(res));
|
||||||
resource_manager.write().unwrap().tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
let sdl = sdl2::init().unwrap();
|
let sdl = sdl2::init().unwrap();
|
||||||
let sdl_video = sdl.video().unwrap();
|
let sdl_video = sdl.video().unwrap();
|
||||||
|
@ -232,11 +231,6 @@ fn main() {
|
||||||
|
|
||||||
let mut events = sdl.event_pump().unwrap();
|
let mut events = sdl.event_pump().unwrap();
|
||||||
while !game.should_close {
|
while !game.should_close {
|
||||||
let version = {
|
|
||||||
let mut res = game.resource_manager.write().unwrap();
|
|
||||||
res.tick();
|
|
||||||
res.version()
|
|
||||||
};
|
|
||||||
|
|
||||||
let now = time::now();
|
let now = time::now();
|
||||||
let diff = now - last_frame;
|
let diff = now - last_frame;
|
||||||
|
@ -244,6 +238,12 @@ fn main() {
|
||||||
let delta = (diff.num_nanoseconds().unwrap() as f64) / frame_time;
|
let delta = (diff.num_nanoseconds().unwrap() as f64) / frame_time;
|
||||||
let (width, height) = window.drawable_size();
|
let (width, height) = window.drawable_size();
|
||||||
|
|
||||||
|
let version = {
|
||||||
|
let mut res = game.resource_manager.write().unwrap();
|
||||||
|
res.tick(&mut resui, &mut ui_container, delta);
|
||||||
|
res.version()
|
||||||
|
};
|
||||||
|
|
||||||
let vsync_changed = *game.vars.get(settings::R_VSYNC);
|
let vsync_changed = *game.vars.get(settings::R_VSYNC);
|
||||||
if vsync != vsync_changed {
|
if vsync != vsync_changed {
|
||||||
vsync = vsync_changed;
|
vsync = vsync_changed;
|
||||||
|
|
209
src/resources.rs
209
src/resources.rs
|
@ -12,9 +12,6 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
extern crate hyper;
|
|
||||||
extern crate zip;
|
|
||||||
|
|
||||||
extern crate steven_resources as internal;
|
extern crate steven_resources as internal;
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -22,8 +19,15 @@ use std::path;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use hyper;
|
||||||
|
use zip;
|
||||||
|
|
||||||
|
use ui;
|
||||||
|
|
||||||
const RESOURCES_VERSION: &'static str = "1.9.2";
|
const RESOURCES_VERSION: &'static str = "1.9.2";
|
||||||
|
const VANILLA_CLIENT_URL: &'static str = "https://launcher.mojang.com/mc/game/1.9.2/client/19106fd5e222dca0f2dde9f66db8384c9a7db957/client.jar";
|
||||||
|
|
||||||
pub trait Pack: Sync + Send {
|
pub trait Pack: Sync + Send {
|
||||||
fn open(&self, name: &str) -> Option<Box<io::Read>>;
|
fn open(&self, name: &str) -> Option<Box<io::Read>>;
|
||||||
|
@ -34,20 +38,50 @@ pub struct Manager {
|
||||||
version: usize,
|
version: usize,
|
||||||
|
|
||||||
vanilla_chan: Option<mpsc::Receiver<bool>>,
|
vanilla_chan: Option<mpsc::Receiver<bool>>,
|
||||||
|
vanilla_progress: Arc<Mutex<Progress>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ManagerUI {
|
||||||
|
progress_ui: Vec<ProgressUI>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sync for Manager {}
|
unsafe impl Sync for Manager {}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn new() -> Manager {
|
pub fn new() -> (Manager, ManagerUI) {
|
||||||
let mut m = Manager {
|
let mut m = Manager {
|
||||||
packs: Vec::new(),
|
packs: Vec::new(),
|
||||||
version: 0,
|
version: 0,
|
||||||
vanilla_chan: None,
|
vanilla_chan: None,
|
||||||
|
vanilla_progress: Arc::new(Mutex::new(Progress {
|
||||||
|
tasks: vec![],
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
m.add_pack(Box::new(InternalPack));
|
m.add_pack(Box::new(InternalPack));
|
||||||
m.download_vanilla();
|
m.download_vanilla();
|
||||||
m
|
(m, ManagerUI { progress_ui: vec!{} })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the 'version' of the manager. The version is
|
/// Returns the 'version' of the manager. The version is
|
||||||
|
@ -77,7 +111,7 @@ impl Manager {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self, mui: &mut ManagerUI, ui_container: &mut ui::Container, delta: f64) {
|
||||||
// Check to see if the download of vanilla has completed
|
// Check to see if the download of vanilla has completed
|
||||||
// (if it was started)
|
// (if it was started)
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
|
@ -90,6 +124,109 @@ impl Manager {
|
||||||
self.vanilla_chan = None;
|
self.vanilla_chan = None;
|
||||||
self.load_vanilla();
|
self.load_vanilla();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// Add a ui element for it
|
||||||
|
let background = ui::ImageBuilder::new()
|
||||||
|
.texture("steven:solid")
|
||||||
|
.position(0.0, -UI_HEIGHT)
|
||||||
|
.size(300.0, UI_HEIGHT)
|
||||||
|
.colour((0, 0, 0, 100))
|
||||||
|
.draw_index(100)
|
||||||
|
.alignment(ui::VAttach::Bottom, ui::HAttach::Left)
|
||||||
|
.create(ui_container);
|
||||||
|
|
||||||
|
ui::ImageBuilder::new()
|
||||||
|
.texture("steven:solid")
|
||||||
|
.position(0.0, 0.0)
|
||||||
|
.size(300.0, 10.0)
|
||||||
|
.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))
|
||||||
|
.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,
|
||||||
|
background: background,
|
||||||
|
progress_bar: progress_bar,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ui in &mut mui.progress_ui {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
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
|
||||||
|
let delta = delta.min(5.0);
|
||||||
|
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();
|
||||||
|
let target_size = (300.0 * ui.progress).min(300.0);
|
||||||
|
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
|
||||||
|
mui.progress_ui.retain(|v| v.position >= -UI_HEIGHT && !v.closing);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_pack(&mut self, pck: Box<Pack>) {
|
fn add_pack(&mut self, pck: Box<Pack>) {
|
||||||
|
@ -113,32 +250,39 @@ impl Manager {
|
||||||
let (send, recv) = mpsc::channel();
|
let (send, recv) = mpsc::channel();
|
||||||
self.vanilla_chan = Some(recv);
|
self.vanilla_chan = Some(recv);
|
||||||
|
|
||||||
info!("Vanilla assets missing, obtaining");
|
let progress_info = self.vanilla_progress.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let client = hyper::Client::new();
|
let client = hyper::Client::new();
|
||||||
let url = format!("https://s3.amazonaws.com/Minecraft.Download/versions/{0}/{0}.jar",
|
let res = client.get(VANILLA_CLIENT_URL)
|
||||||
RESOURCES_VERSION);
|
|
||||||
let res = client.get(&url)
|
|
||||||
.send()
|
.send()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut file = fs::File::create(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
let mut file = fs::File::create(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
||||||
|
|
||||||
let length = *res.headers.get::<hyper::header::ContentLength>().unwrap();
|
let length = *res.headers.get::<hyper::header::ContentLength>().unwrap();
|
||||||
let mut progress = ProgressRead {
|
let task_file = format!("./resources-{}", RESOURCES_VERSION);
|
||||||
read: res,
|
Self::add_task(&progress_info, "Downloading Core Assets", &task_file, *length);
|
||||||
progress: 0,
|
{
|
||||||
total: *length,
|
let mut progress = ProgressRead {
|
||||||
};
|
read: res,
|
||||||
io::copy(&mut progress, &mut file).unwrap();
|
progress: &progress_info,
|
||||||
|
task_name: "Downloading Core Assets".into(),
|
||||||
|
task_file: task_file,
|
||||||
|
};
|
||||||
|
io::copy(&mut progress, &mut file).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Copy the resources from the zip
|
// Copy the resources from the zip
|
||||||
let file = fs::File::open(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
let file = fs::File::open(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
||||||
let mut zip = zip::ZipArchive::new(file).unwrap();
|
let mut zip = zip::ZipArchive::new(file).unwrap();
|
||||||
|
|
||||||
|
let task_file = format!("./resources-{}", RESOURCES_VERSION);
|
||||||
|
Self::add_task(&progress_info, "Unpacking Core Assets", &task_file, zip.len() as u64);
|
||||||
|
|
||||||
let loc = format!("./resources-{}", RESOURCES_VERSION);
|
let loc = format!("./resources-{}", RESOURCES_VERSION);
|
||||||
let location = path::Path::new(&loc);
|
let location = path::Path::new(&loc);
|
||||||
let count = zip.len();
|
let count = zip.len();
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
|
Self::add_task_progress(&progress_info, "Unpacking Core Assets", &task_file, 1);
|
||||||
let mut file = zip.by_index(i).unwrap();
|
let mut file = zip.by_index(i).unwrap();
|
||||||
if !file.name().starts_with("assets/") {
|
if !file.name().starts_with("assets/") {
|
||||||
continue;
|
continue;
|
||||||
|
@ -150,12 +294,30 @@ impl Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::File::create(location.join("steven.assets")).unwrap(); // Marker file
|
fs::File::create(location.join("steven.assets")).unwrap(); // Marker file
|
||||||
info!("Done");
|
|
||||||
send.send(true).unwrap();
|
send.send(true).unwrap();
|
||||||
|
|
||||||
fs::remove_file(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
fs::remove_file(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DirPack {
|
struct DirPack {
|
||||||
|
@ -182,18 +344,17 @@ impl Pack for InternalPack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProgressRead<T> {
|
struct ProgressRead<'a, T> {
|
||||||
read: T,
|
read: T,
|
||||||
total: u64,
|
progress: &'a Arc<Mutex<Progress>>,
|
||||||
progress: u64,
|
task_name: String,
|
||||||
|
task_file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <T: io::Read> io::Read for ProgressRead<T> {
|
impl <'a, T: io::Read> io::Read for ProgressRead<'a, T> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
let size = try!(self.read.read(buf));
|
let size = try!(self.read.read(buf));
|
||||||
self.progress += size as u64;
|
Manager::add_task_progress(self.progress, &self.task_name, &self.task_file, size as u64);
|
||||||
trace!("Progress: {:.2}",
|
|
||||||
(self.progress as f64) / (self.total as f64));
|
|
||||||
Ok(size)
|
Ok(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,11 @@ pub use self::settings_menu::{SettingsMenu, VideoSettingsMenu, AudioSettingsMenu
|
||||||
use render;
|
use render;
|
||||||
use ui;
|
use ui;
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
pub trait Screen {
|
pub trait Screen {
|
||||||
// Called once
|
// Called once
|
||||||
fn init(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
|
fn init(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
|
||||||
}
|
}
|
||||||
fn deinit(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
|
fn deinit(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// May be called multiple times
|
// May be called multiple times
|
||||||
|
@ -45,7 +44,7 @@ pub trait Screen {
|
||||||
ui_container: &mut ui::Container) -> Option<Box<Screen>>;
|
ui_container: &mut ui::Container) -> Option<Box<Screen>>;
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
fn on_scroll(&mut self, x: f64, y: f64) {
|
fn on_scroll(&mut self, _x: f64, _y: f64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_closable(&self) -> bool {
|
fn is_closable(&self) -> bool {
|
||||||
|
|
Loading…
Reference in New Issue