rs-tiled
rs-tiled copied to clipboard
Options for more easily opening a map with external files from a special source
The problem I'm currently running into is that in ggez, opening files from resources is done through a special Filesystem type, which doesn't give the full path of the map file. I want to provide a way for rs-tiled to find the tilesets using this filesystem type, rather than manually finding the full path of the map file and passing that.
Hello, I'm giving a try at making a game in Rust and I also faced this kind of issue. I'm think about using a version of parse
that'd also require a function (or any kind of structure) that'd load itself the file for the library to work. Making the glue code would require a bit of elbow ooil but it'd greatly improve this library's compatibility with other asset management.
Have a nice day.
Being able to customize this would also enable you to protect against malicious .tmx files opening arbitrary files on disk, which could be a problem if you want to allow any User Generated Content. I'd even argue for some default sanity checks / sandboxing...
This is probably as straightforward as replacing the map_path: Option<&Path>
with map_path: Option<&dyn BufReadFactory>
(where BufReadFactory is some new trait) in various calls that eventually calls down to:
https://github.com/mattyhall/rs-tiled/blob/06e4ecf4257e4dd0813087e1fcd642d7f0ce7713/src/lib.rs#L432
For bonus points, making this async/future based would make it easier to integrate into the browser I believe.
Trying to address this by supplying a closure for loading external files: https://github.com/mattyhall/rs-tiled/pull/100
To deploy on the web I put all the files in the binary and then I implement the loader like this:
pub struct MyTiledResourceCache(HashMap<tiled::ResourcePathBuf, Arc<tiled::Tileset>>);
impl tiled::ResourceCache for MyTiledResourceCache {
fn get_tileset(&self, path: impl AsRef<tiled::ResourcePath>) -> Option<Arc<tiled::Tileset>> {
self.0.get(&path.as_ref().to_path_buf()).cloned()
}
fn get_or_try_insert_tileset_with<F, E>(
&mut self,
path: tiled::ResourcePathBuf,
f: F
) -> Result<Arc<tiled::Tileset>, E>
where
F: FnOnce() -> Result<tiled::Tileset, E>
{
match self.0.entry(path.clone()) {
Entry::Occupied(o) => Ok(o.get().clone()),
Entry::Vacant(v) => {
#[cfg(not(target_arch = "wasm32"))]
{
let tileset = f()?;
Ok(v.insert(Arc::new(tileset)).clone())
}
#[cfg(target_arch = "wasm32")]
{
let _ = f;
let loader = tiled::Loader::new();
// in_memory_tiled_files contains all the map and tileset inside the memory
let tileset = loader.load_tsx_tileset_from(in_memory_tiled_files::InMemoryFetcher::fetch(path).unwrap(), &"assets/").unwrap();
Ok(v.insert(Arc::new(tileset)).clone())
}
},
}
}
}
But I have an issue that even when using this cache rs-tiled tries to open the file before calling get_or_try_insert_tileset
.
Thus I modified rs-tiled like this:
[thiolliere@fedora rs-tiled]$ git diff
diff --git a/src/map.rs b/src/map.rs
index 8871985..807c9e7 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -188,8 +188,15 @@ impl Map {
let res = Tileset::parse_xml_in_map(parser, attrs, map_path)?;
match res.result_type {
EmbeddedParseResultType::ExternalReference { tileset_path } => {
- let file = File::open(&tileset_path).map_err(|err| Error::CouldNotOpenFile{path: tileset_path.clone(), err })?;
- let tileset = cache.get_or_try_insert_tileset_with(tileset_path.clone(), || crate::parse::xml::parse_tileset(file, &tileset_path))?;
+ let callback = || {
+ let file = File::open(&tileset_path)
+ .map_err(|err| Error::CouldNotOpenFile{path: tileset_path.clone(), err })?;
+ crate::parse::xml::parse_tileset(file, &tileset_path)
+ };
+ let tileset = cache.get_or_try_insert_tileset_with(
+ tileset_path.clone(),
+ callback,
+ )?;
tilesets.push(MapTilesetGid{first_gid: res.first_gid, tileset});
}
EmbeddedParseResultType::Embedded { tileset } => {
This way my cache always contains all the external tileset.
I am working on a PR to have this File::open inside the callback on master
EDIT: I see that the branch next already contains a ResourceReader
to implement what I have above.
Thanks for the good work ResourceReader
looks good
Pretty much solved in 0.11.0. Closing.