bundles: Vec<Option<Note>>,
}
+pub type FileInBundleId = (Id, ZipIndex);
+pub type SpecsInBundles = HashMap<UniCase<String>, FileInBundleId>;
+
#[derive(Debug,Clone,Serialize,Deserialize)]
pub enum State {
Uploading,
struct Parsed {
meta: BundleMeta,
libs: IndexVec<LibInBundleI, shapelib::Contents>,
+ specs: SpecsInBundles,
}
#[derive(Debug)]
}
let mut libs = Vec::new();
+ let mut specs = HashMap::new();
for (name,i) in &za {
eh.besteffort(|| Ok::<_,LE>(if_chain!{
let mut split = name.as_ref().split('/');
inzip: i,
});
}
+ }} else if unicase::eq(dir, "specs") { if_chain!{
+ let mut split = file.rsplitn(3,'.');
+ if let Some(ext) = split.next(); if unicase::eq(ext, "toml");
+ if let Some(ext) = split.next(); if unicase::eq(ext, "game");
+ if let Some(base) = split.next();
+ then {
+ use hash_map::Entry::*;
+ match specs.entry(base.to_owned().into()) {
+ Occupied(oe) => throw!(LE::BadBundle(format!(
+ "duplicate spec {:?} vs {:?} - files varying only in case!",
+ file, oe.key()))),
+ Vacant(ve) => { ve.insert((id,i)); }
+ }
+ }
}}
}
}), ||())?;
let (libs, newlibs) = newlibs.into_iter().unzip();
(ForProcess { za, newlibs },
- Parsed { meta, libs })
+ Parsed { meta, libs, specs })
}
#[throws(LE)]
let spec_leaf = format!("{}.game.toml", spec_name);
- // todo: game specs from bundles
+ if let Some((id, index)) = ig.bundle_specs.get(&UniCase::from(spec_name)) {
+ let fpath = id.path_(&ig.name);
+ let f = File::open(&fpath)
+ .with_context(|| fpath.clone()).context("reopen bundle")
+ .map_err(IE::from)?;
+ match id.kind {
+ Kind::Zip => {
+ let mut za = ZipArchive::new(BufReader::new(f)).map_err(
+ |e| LE::BadBundle(format!("re-examine zipfile: {}", e)))?;
+ let mut f = za.i(*index).map_err(
+ |e| LE::BadBundle(format!("re-find zipfile member: {}", e)))?;
+ return read_from_read(&mut f, &mut |e|{
+ LE::BadBundle(format!("read zipfile member: {}", e))}.into())?;
+ }
+ }
+ }
if spec_name.chars().all(
|c| c.is_ascii_alphanumeric() || c=='-' || c =='_'
#[throws(InternalError)]
fn incorporate_bundle(ib: &mut InstanceBundles, ig: &mut Instance,
id: Id, parsed: Parsed) {
- let Parsed { meta, libs } = parsed;
+ let Parsed { meta, libs, specs } = parsed;
let iu: usize = id.index.into();
let slot = &mut ib.bundles[iu];
for lib in libs {
ig.local_libs.add(lib);
}
+ ig.bundle_specs.extend(specs);
let state = State::Loaded(Loaded { meta });
*slot = Some(Note { kind: id.kind, state });
}
impl InstanceBundles {
- pub fn new() -> Self { InstanceBundles{ bundles: default() } }
+ pub fn new() -> Self {
+ InstanceBundles{
+ bundles: default(),
+ }
+ }
fn iter(&self) -> impl Iterator<Item=(Id, &State)> {
self.bundles.iter().enumerate().filter_map(|(index, slot)| {
// Right, everything is at most NEARLY-ASENT, make them ABSENT
self.bundles.clear();
+ ig.bundle_specs.clear();
// Prevent old, removed, players from accessing any new bundles.
ig.asset_url_key = new_asset_key;
pub acl: LoadedAcl<TablePermission>,
pub links: Arc<LinksTable>,
pub bundle_list: MgmtBundleList, // copy for easy access
+ pub bundle_specs: bundles::SpecsInBundles,
pub asset_url_key: AssetUrlKey,
pub local_libs: shapelib::Registry,
}
tokens_clients: default(),
links: default(),
bundle_list: default(),
+ bundle_specs: default(),
asset_url_key: AssetUrlKey::new_random()?,
local_libs: default(),
};
acl: default(),
links: default(),
bundle_list: default(),
+ bundle_specs: default(),
asset_url_key: AssetUrlKey::Dummy,
local_libs: default(),
iplayers: default(),
tokens_players: default(),
bundle_list: default(), // set by load_game_bundles
local_libs: default(), // set by load_game_bundles
+ bundle_specs: default(), // set by load_game_bundles
asset_url_key,
};