stevenarella/src/resources.rs

171 lines
4.9 KiB
Rust

extern crate hyper;
extern crate zip;
use std::thread;
use std::path;
use std::io;
use std::fs;
use std::sync::mpsc;
const RESOURCES_VERSION: &'static str = "15w37a";
pub trait Pack {
fn open(&self, name: &str) -> Option<Box<io::Read>>;
}
pub struct Manager {
packs: Vec<Box<Pack>>,
version: usize,
vanilla_chan: Option<mpsc::Receiver<bool>>,
}
impl Manager {
pub fn new() -> Manager {
let mut m = Manager {
packs: Vec::new(),
version: 0,
vanilla_chan: None,
};
m.download_vanilla();
m
}
/// Returns the 'version' of the manager. The version is
/// 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>> {
for pack in self.packs.iter().rev() {
let path = format!("assets/{}/{}", plugin, name);
match pack.open(&path) {
Some(val) => return Some(val),
None => {},
}
}
None
}
pub fn open_all(&self, plugin: &str, name: &str) -> Vec<Box<io::Read>> {
let mut ret = Vec::new();
for pack in self.packs.iter().rev() {
let path = format!("assets/{}/{}", plugin, name);
match pack.open(&path) {
Some(val) => ret.push(val),
None => {},
}
}
ret
}
pub fn tick(&mut self) {
// Check to see if the download of vanilla has completed
// (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();
}
}
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);
self.add_pack(Box::new(DirPack{
root: location.to_path_buf(),
}))
}
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);
println!("Vanilla assets missing, obtaining");
thread::spawn(move || {
let client = hyper::Client::new();
let url = format!("https://s3.amazonaws.com/Minecraft.Download/versions/{0}/{0}.jar", RESOURCES_VERSION);
let res = client.get(&url)
.send().unwrap();
let mut file = fs::File::create(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
let length = *res.headers.get::<hyper::header::ContentLength>().unwrap();
let mut progress = ProgressRead {
read: res,
progress: 0,
total: *length,
};
io::copy(&mut progress, &mut file).unwrap();
// Copy the resources from the zip
let file = fs::File::open(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
let mut zip = zip::ZipArchive::new(file).unwrap();
let loc = format!("./resources-{}", RESOURCES_VERSION);
let location = path::Path::new(&loc);
let count = zip.len();
for i in 0 .. count {
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
println!("Done");
send.send(true).unwrap();
fs::remove_file(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
});
}
}
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,
}
}
}
struct ProgressRead<T> {
read: T,
total: u64,
progress: u64,
}
impl <T: io::Read> io::Read for ProgressRead<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let size = try!(self.read.read(buf));
self.progress += size as u64;
println!("Progress: {:.2}", (self.progress as f64) / (self.total as f64));
Ok(size)
}
}