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

Add scripting API for the project #2902

Open
bjorn opened this issue Sep 29, 2020 · 11 comments
Open

Add scripting API for the project #2902

bjorn opened this issue Sep 29, 2020 · 11 comments
Assignees
Labels
missing feature It's not just a feature, it's a feature that really should be there!
Milestone

Comments

@bjorn
Copy link
Member

bjorn commented Sep 29, 2020

Scripts should be able to access the files in the project and the project properties.

Split off from #1665.

@bjorn bjorn added the missing feature It's not just a feature, it's a feature that really should be there! label Sep 29, 2020
@bjorn bjorn added this to the Tiled 1.5 milestone Sep 29, 2020
@bjorn bjorn modified the milestones: Tiled 1.5, Tiled 1.6 Feb 4, 2021
@jonny64bit
Copy link
Contributor

So i could do with this feature for my use case.

I want to be able to open every map in a project and run a set of Actions which automate a whole bunch of stuff. Mostly auto mapping.

For the current work around im having to hardcode the map paths into the triggering action.

/// <reference types="@mapeditor/tiled-api" />

const automapAllMaps = tiled.registerAction("AutomapAllMaps", function(action) {
    tiled.trigger("CloseAll");

    processMap("X:\\Source\\MangoDungeon\\Mango\\Assets\\Tiled\\Maps\\Test.tmx");
});

function processMap(path:string) {
    tiled.open(path);
    tiled.trigger("AutomapExtra");
    tiled.trigger("Close");
}

automapAllMaps.text = "Automap All Maps";
automapAllMaps.checkable = false;

tiled.extendMenu("Edit", [
    { action: "AutomapAllMaps" }
]);`

@bjorn
Copy link
Member Author

bjorn commented May 2, 2022

For the current work around im having to hardcode the map paths into the triggering action.

Just one thing to note, there is API available to access a list of files in a folder, so you may only need to hardcode the project path instead of the paths to each map.

Since you're applying AutoMapping to all files in your project, I hope you noticed the Tiled 1.9 Alpha release that greatly improves the speed of AutoMapping. Any feedback regarding AutoMapping is very welcome.

bjorn added a commit that referenced this issue Nov 11, 2022
There is still no scripting API for the project, but at least now you
can get the path of the current project.

Related to issue #2902.
@solerante
Copy link

from #3527 - I also have a use case for this. I would like to get the path to the current project. I'm working on an extension to edit maps for Cataclysm: Dark Days Ahead. It involves importing tilesets and mapping the game's IDs to each sprite and preparing maps with data from the game files.

I generate tilesets, maps, and supplemental files to the project folder which I prompt from the user and then store in a config file to autofill next time the command is run for slight convenience.

@eishiya
Copy link
Contributor

eishiya commented Dec 10, 2022

I think I've asked for this elsewhere, but I think it makes sense being included here: the Project would be a very convenient place for scripts to store script configuration, which at present has to be either set every time the user restarts Tiled, hard-coded by the user, or stored in config files that clutter the user's directories. To this end, I'd like to be able to set Custom Properties or something like them on the project, both via scripts and via the GUI.

Edit: Oh, looks like there's an issue for that already: #2903

@bjorn
Copy link
Member Author

bjorn commented Mar 21, 2023

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 Scripting: the API for getting PropertyTypes #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.

@eishiya
Copy link
Contributor

eishiya commented Mar 21, 2023

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.

@dogboydog
Copy link
Contributor

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

@eishiya
Copy link
Contributor

eishiya commented Mar 21, 2023

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.

@Laskoran
Copy link
Contributor

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?

@eishiya
Copy link
Contributor

eishiya commented Jun 16, 2024

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.

@Laskoran
Copy link
Contributor

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
missing feature It's not just a feature, it's a feature that really should be there!
Projects
Status: In Progress
Development

No branches or pull requests

6 participants