#Asset loading strategy: Toeing two lines

27 messages · Page 1 of 1 (latest)

timid scroll
#

Subject

Overview

I have two styles to go along with:

  • asset_tracking given in the bevy_new_2d template
  • ResourceReader from crate rs-tiled, this read_from.

What solution? Your code seems working

This does not work when read_from goes nested/recursive, it's a bit tone deaf for various file structures given for example a non-technical teammate using the editor only or being in such confinement.

Recursive on what? Background knowledge please

This map file (called "tmx") can either embed a tileset or reference another xml file called tileset (tsx, huh?).

Example of such tsx file (if i add, say, assets/tiled/tiles/dg_under_the_castle-10.tsx):

<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.2" name="dg_under_the_castle-10" tilewidth="16" tileheight="16" tilecount="560" columns="28">
 <image source="dg_under_the_castle-10.png" width="448" height="320"/> <!-- This path can vary --->
 <tile id="200">
   <!-- ... --->
 </tile>
 <tile id="201">
   <!-- ... --->
 </tile>
 <!-- ... --->
</tileset>

Wouldn't it be so much to work on?

  • I already yoinked rs-tiled: ready to tweak from both ends.

What I do NOT want to do:

  • Cutting out rs-tiled and using something like custom XmlAsset.

What I may want to do:

  • Using load_folderthrough from_world
scenic walrus
#

Though now that I know that method exists, I'm going to try to make it return an (async) Reader

timid scroll
scenic walrus
#

No haha, I just read enough asset code. But I guess I missed this one lol

timid scroll
#

In Tiled editor given a loaded map(tmx) file, if a tileset (image, not tsx) file is loaded, for example, without the embed option selected, the map file looks like the following. In this scenario(where, say, an artist uses that default setup in Tiled editor), the current code does not work:

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="5" nextobjectid="1">
 <tileset firstgid="1" source="tiles/dg_under_the_castle-10.tsx"/>
 <!-- ... --->
</map>

This could be (the current t2pw-templatoid "map1.tile-16x16.tmx") like this with the embed option. This works:

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="5" nextobjectid="1">
 <tileset firstgid="1" name="dg_under_the_castle-10" tilewidth="16" tileheight="16" tilecount="560" columns="28">
  <image source="tiles/dg_under_the_castle-10.png" width="448" height="320"/>
  <!-- ... --->
</map>
#

Before Debugging...

  1. This embedded and un-embedded configuration can coexist (through Tiled editor) this makes the debugging harder.
  2. In the web setting, the assets have to be refreshed thouroughly per change.

Solador

It's basically ts-tiled with, for debugging-purpose, publicly reexported private methods and types (and it has tracing)

The current template(t3pw) uses this fork

timid scroll
#

What various structure? or What can happen during collaboration

Desired (in the template) structure

/assets
└── /tiled
    ├── foo.tmx
    └── /tiles
        ├── bar.png
        └── bar.tsx

If it's not embedded, there's no tsx

/assets
└── /tiled
    ├── foo.tmx
    └── /tiles
        └── bar.png

All-pwd style (i personally don't like this style)

/assets
└── /tiled
    ├── foo.tmx
    ├── bar.png
    └── bar.tsx

Bevy source code style

/assets
├── /textures/tiles/bar.png
└── /tiled
    ├── foo.tmx
    └── /tiles/bar.tsx

It could be all caps. Something like this happened. The editor may behave differently on Windows

/assets
└── /tiled
    ├── Foo.tmx
    └── /tiles
        ├── Bar.png
        └── Bar.tsx

etc.

timid scroll
# scenic walrus Though now that I know that method exists, I'm going to try to make it return an...

Since the dep(rs-tiled) is yoinked, we can do something like

pub trait ResourceReader {
    /// The type of the resource that the reader provides. For example, for
    /// [`FilesystemResourceReader`], this is defined as [`File`].
    type Resource: Read;
    /// The type that is returned if [`read_from()`](Self::read_from()) fails. For example, for
    /// [`FilesystemResourceReader`], this is defined as [`std::io::Error`].
    type Error: std::error::Error + Send + Sync + 'static;

    /// Try to return a reader object from a path into the resources filesystem.
    fn read_from(&mut self, path: &Path) -> std::result::Result<Self::Resource, Self::Error>;

    /// However we like
    fn read_from_2(/* ... */) -> Foo;
}
timid scroll
timid scroll
#

ugh i don't want to learn all these part. it's time-consuming ;o i think the problem is somewher else

#

jeez lol

timid scroll
#

@scenic walrus how should i use LoadContext? Could it be in this scope?

impl tiled::ResourceReader for FooReader {
    type Resource = Cursor<Arc<[u8]>>;
    type Error = SomeError;

    fn read_from(&mut self, _path: &Path) -> std::result::Result<Self::Resource, Self::Error> {
        /* Here */
    }
}
scenic walrus
#

And then just access it through self

timid scroll
#

Is this in right direction? what am i doing?

It looks like that I need to deal with async flow between Result<Resource, Error> and Result<Asset, Error>

impl AssetLoader for FooReader {
    type Asset = BarAsset;
    type Settings = ();
    type Error = FooError;

    fn load(
        &self,
        reader: &mut dyn Reader,
        _settings: &Self::Settings,
        load_context: &mut bevy::asset::LoadContext<'_>,
    ) -> impl ConditionalSendFuture<Output = Result<Self::Asset, Self::Error>> {
        Box::pin(async move {
            // Can `reader` or `load_context` hold a callback?
            // `read_from` of `Result<Resource, Error>` does stuff and callback () -> `Result<Asset, Error>`
            // Ok(BarAsset(/* ... */))
        })        
    }
    // extension ...
}
#

can i use pseudo code by the way?

#

@scenic walrus Am i missing any helpful documentation about the high-level idea of Loader, Asset, and Reader (and their interactions)?

timid scroll
#

I should spend some time in Rust async too

scenic walrus
#

I don't know if we have a single doc that explains that? But each type has some docs for it

scenic walrus
timid scroll
# scenic walrus An `AssetReader` is an abstraction over a filesystem effectively: it lets you ge...
/// Gives you byte for a file
struct ByteGiver {    
    bytes: Arc<[u8]>,
}

impl ByteGiver {
    fn new(bytes: &[u8]) -> Self {
        Self {
            bytes: Arc::from(bytes),
        }
    }
}

impl tiled::ResourceReader for ByteGiver {
    type Resource = Cursor<Arc<[u8]>>;
    type Error = std::io::Error;

    fn read_from(&mut self, _path: &Path) -> std::result::Result<Self::Resource, Self::Error> {
        // Ok(Cursor of byte)
    }
}

// Takes a Reader and creates an Asset from it
#[derive(Default, TypePath)]
pub struct ReaderTaker;

impl AssetLoader for ReaderTaker {
    type Asset = BarAsset;
    type Settings = ();
    type Error = FooError;

    fn load(
        &self,
        reader: &mut dyn Reader,
        settings: &Self::Settings,
        load_context: &mut bevy::asset::LoadContext<'_>,
    ) -> impl ConditionalSendFuture<Output = Result<Self::Asset, Self::Error>> {
           // BarAsset something to do with tile data in this context
    }

    // extension ...
}
timid scroll
timid scroll
#

lol I'll get back to it probably next week. 3/10