Do you want to work on this issue?
You can request for a bounty in order to promote it!
Add scripting API for the project #2902
bjorn posted onGitHub
Scripts should be able to access the files in the project and the project properties.
Split off from #1665.
After #3622 we have access to the basic properties of the project, including the list of folders, which is usually more relevant than the project file path. However, I don't think we can close this issue, since there are at least two shortcomings:
- There is still no convenient way to access the files in a project, due to:
- Recursive search for files needs to be implemented manually.
- We can't easily get all files of a certain type, for example all files that can likely be read as map files.
- The project also stores the custom types, which needs to be accessible as well (see also #3419, which maybe could be considered to cover this aspect).
Regarding file search, the just added tiled.project
could be entry point of convenience functions like project.maps
, project.tilesets
and project.worlds
(or maybe project.assets(Asset.TileMap)
would be better?). I also wonder whether these functions should return file names, or actually loaded assets (which could be convenient, but would in some cases result in performance issues). A more generic file search like, project.files("*.tmx")
could also be useful.
While testing #3622, I ended up writing that big recursive search for map files:
function collectMaps(folder) {
//First, get all the files in this folder
let files = File.directoryEntries(folder, File.Files | File.Readable | File.NoDotAndDotDot);
for(file of files) {
let path = folder+"/"+file;
let format = tiled.mapFormatForFile(path);
if(format) {
let map = getOpenMap(path);
if(map) //If it's already open, use that instance
maps.push(map);
else //save the path to open later
maps.push(path);
} //else there's no map format that can read this file, it's not a Tiled map, skip it.
}
//Then, look at any subfolders:
files = File.directoryEntries(folder, File.Dirs | File.Readable | File.NoDotAndDotDot);
for(file of files) {
collectMaps(folder+"/"+file);
}
}
//Find all the maps in each project directory:
if(tiled.project) {
let folders = tiled.project.folders;
for(folder of folders)
collectMaps(folder);
}
As you can see, I ended up saving a list of paths, because opening all those maps at once was terrible for performance xP Opening them one by one made for a much more pleasant experience. They need to be the actual map documents in my case (i.e. tiled.open, not MapFormat.read), so there was no avoiding the overhead of that. I think returning a list of paths gives scripts the most options - they can WhateverFormat.read or tiled.open all of them, they can read them one at a time, read them in pairs for comparisons, whatever, all with the minimal memory impact possible for their given task.
It might also be nice if projects supported getting a list of images within the project, as that's another file type commonly used in Tiled, even if Tiled doesn't usually create them. Tiled knows best what it can open as an image xP However, I do also quite like the idea of project.assets(Asset.TileMap)
, and that wouldn't make sense for images.
Whatever the method for getting files of a particular type, I think it would be improved by the option to specify a path to look within, e.g. a script might only want the maps in /automap or in /jungle, e.g. project.assets(Asset.TileMap, '/jungle')
. If possible, both absolute paths (e.g. from project.folders
) and relative paths (treated as relative to the project root) should be allowed, and any attempts to search outside the project should return nothing.
A generic file search with filename wildcards would be pretty nice to have too. Perhaps this could be combined with all of the above, e.g. "find all the Maps in /automap/ with the name filter jungle*": project.files(Asset.TileMap, '/automap', 'jungle*')
. All those parameters would get in the way when you just want the filter OR the path though.
I would agree that the map/tileset/etc. list functions should just return the paths, especially because if you did want to load them all, you could just loop through the paths yourself without too much code.
All those parameters would get in the way when you just want the filter OR the path though.
Maybe passing ''
, null, or undefined as the search directory would default it to the project root
All those parameters would get in the way when you just want the filter OR the path though.
Maybe passing
''
, null, or undefined as the search directory would default it to the project root
Yeah, it'd have to be done that way if we have a single three-parameter files
method xP It's just not the prettiest, is all.
Hello,
Would it be a possibility to leverage this feature also for the CLI? Having full project access in the scripting API is great, but having the possibility to use it in a CI pipeline by using the tiled CLI would be even greater.
Or scripts can already be executed via CLI, but since there is no project loaded at all at that point in time, this feature here would not be usable.
So, tldr: could this be enhanced with the option to specify a project file when using the CLI?
Or scripts can already be executed via CLI, but since there is no project loaded at all at that point in time, this feature here would not be usable.
In the newest builds, there's --project
for the CLI that loads a given project, so hopefully you should be able to use scripts that use the Projects API via the CLI. The first official release with this feature will be 1.11, which should be coming out in a week or two AFAIK, but all the recent autobuilds already include it, if you want to try it sooner.
Or scripts can already be executed via CLI, but since there is no project loaded at all at that point in time, this feature here would not be usable.
In the newest builds, there's
--project
for the CLI that loads a given project, so hopefully you should be able to use scripts that use the Projects API via the CLI. The first official release with this feature will be 1.11, which should be coming out in a week or two AFAIK, but all the recent autobuilds already include it, if you want to try it sooner.
Incredible 😍 Thank you very much for pointing this out!