Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of plugin in a headless environment (node server) #478

Closed
jfelsinger opened this issue Dec 17, 2023 · 2 comments
Closed

Allow use of plugin in a headless environment (node server) #478

jfelsinger opened this issue Dec 17, 2023 · 2 comments

Comments

@jfelsinger
Copy link
Contributor

jfelsinger commented Dec 17, 2023

Context

Tiled maps can contain a lot of useful information for game state, actors, colliders, etc. For a game system that communicates back to a server for game state (see: excaliburjs/Excalibur#2759) it seems that it could be useful to have all the of the map data also loadable on the server.

This plugin already does the heavy lifting of parsing, creating actors, and colliders for tiled map data, but it also does a lot of lifting in loading graphics and animations, which is a lot of overhead that a server or headless environment probably doesn't need or care about and often doesn't support.

Proposal

To make this plugin work in a server or headless environment. To add options that can be set to skip the loading of the graphics/sprite/animation components for this plugin; also, to provide a method of changing how the tiled plugin loads resources to not always be reliant on an Excalibur Resource specifically.


I needed this functionality for my own personal project, and have already implemented on a fork, but it's pretty rudimentary, and I'm not sure if it jives as a proper way to implement this to a wider-audience, but...

Interface-wise, I went with something like this:

// Options to skip the loading of graphical resources
const tiledMap = new TiledMapResource(pathToMyResource, { skipCamera: true, skipGraphics: true });

My initial implementation for a way to replace to ingrained Resource dependency could be a lot less messy with better typing, but is working to allow using bun/node to fetch resources. ie:

// options.resourceBuilder - a function that takes a path+responseType and returns a Loadable to be used for fetching resources.

const tiledMap = new TiledMapResource('./maps/map.tmx', {
    // By default this just returns a `new Resource` and isn't any different than the current implementation
    resourceBuilder: <T>(path: string, responseType: 'text' | 'json'): Loadable<T> => {
        return new FileLoadable<T>(path, responseType);
    }
});

// A part of a sample of a loadable that gets data from a file with Bun.
class FileLoadable<T> implements Loadable<T> {
    async load() {
        const file = Bun.file(this.path);
        switch(this.responseType) {
          case 'text': return this.data = await file.text() as any;
          case 'json': return this.data = await file.json();
        }
    }
    
    // ...
}
@eonarheim
Copy link
Member

@jfelsinger 100% agree with a headless mode!

I've been working on an overhaul of the plugin here, and I think it'd make sense to roll this feature in #477

In this PR I'm making a new resource TiledResource to encapsulate all the new parsing/behavior. The older TiledMapResource will be marked deprecated.

What if I add a parameter here?
image

const tiledResource = new TiledResource('path/to/my.tmx', { headless: true });

My initial implementation for a way to replace to ingrained Resource dependency could be a lot less messy with better typing, but is working to allow using bun/node to fetch resources. ie:

Say more about this, is it just that Bun/Node other server environments will want to load files differently than ajax?

@jfelsinger
Copy link
Contributor Author

What if I add a parameter here?

I think that's perfect. Cleaner than the split-out options that I did.

Say more about this, is it just that Bun/Node other server environments will want to load files differently than ajax?

I'll link my implementation here for reference, even though it's a little ugly. jfelsinger/excalibur-tiled@main...headless-node-tiles#diff-c5a009df77af4b093f977e8a8e32e289c702a8930b262c70950b71e001c2fb6dR58

But, for what you said, mainly, yes. If someone is running in Bun/Node the files are likely going to be right there on the machine, so being able to use a Loadable that loads them from the file system is going to be cleaner/faster than ajax.

The other problem though is that ResourceLoader specifically uses XMLHttpRequest, and XMLHttpRequest is not a built-in global in Bun or Node like in the browser, so even if you want to use ajax on the server-side it's currently broken. It might be a good solution to either change ResourceLoader to use fetch (which has support in Node and Web, but sucks for detecting request progress), or detect and change it's implementation based on the platform it's running on, or maybe something else? I think that's probably a feature request for the main Excalibur repo tho. 🤔

The solution I implemented on my fork was just to replace the instances of ResourceLoader with a options-defined function that returns a generic Loadable instead (defaulted to that Loadable being the ResourceLoader), which "works." It's just ugly the way I did it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants