viniciusgerevini / godot-aseprite-wizard

Godot Editor plugin to help import Aseprite animations to AnimationPlayers, AnimatedSprites and SpriteFrames.
MIT License
893 stars 43 forks source link

Store config in metadata instead of editor_description property #61

Closed dfkeenan closed 1 year ago

dfkeenan commented 2 years ago

Hi,

One of the few things I dislike about this great plugin is that it is currently storing config as a base64 encoded string in the editor_description property. There is a better option. you can store the config dictionary in the object metadata using methods set_meta, get_meta, has_meta. i.e.

func _ready():
    var cfg = null

    if sprite.has_meta(wizard_config.META_NAME):
        cfg = sprite.get_meta(wizard_config.META_NAME)

and

func _save_config():
    var cfg = {
        "player": _animation_player_path,
        "source": _source,
        "layer": _layer,
        "op_exp": _options_title.pressed,
        "o_folder": _output_folder,
        "o_name": _out_filename_field.text,
        "only_visible": _visible_layers_field.pressed,
        "o_ex_p": _ex_pattern_field.text
    }

    sprite.set_meta(wizard_config.META_NAME, cfg)

There was only a few minor things I had to change. Seems to work quite well. I might be able to do PR later. I haven't actually forked the repo, I am just playing in a project I installed the plugin into. Not sure how to test the plugin without adding things to the repo.

viniciusgerevini commented 2 years ago

Oh, that's really nice. I wasn't aware of the meta methods.

For the most part I prefer to leave anything related to this plugin out of the user's production code, however, the editor_description approach is kind of ugly. I think this is a good approach.

Just confirm the meta is indeed persisted with the scene when reloading it. I don´t think that's the case. I was reading the docs, and these set_meta and get_meta methods are supposed to be used in runtime. In other words, they do not modify the persisted scene. This means once the node is reloaded, or the editor is restarted, all meta will go away.

I might be able to do PR later

Yeah, give a stab at raising the PR. It's a great contribution. Thank you.

dfkeenan commented 2 years ago

I have been doing some investigations today. The metadata is saved in the scene.

Fragment from the Player.tscn file:

[node name="Sprite" type="Sprite" parent="."]
texture = SubResource( 7 )
hframes = 3
vframes = 2
frame = 5
__meta__ = {
"_aseprite_wizard_config_": {
"layer": "Player",
"o_ex_p": "",
"o_folder": "",
"o_name": "",
"only_visible": false,
"op_exp": false,
"player": "AnimationPlayer",
"source": "res://Player.aseprite"
},
"_editor_description_": "This is a description"
}

In fact in version 3.X the editor_description is stored in the meta dictionary with a key of _editor_description_. (This is being changed in 4.0)

This data is still present when you export the game. I don't know that there is anyway to make it so it doesn't. I haven't found any info about it at least.

dfkeenan commented 2 years ago

After a whole lot of messing about I implemented a EditorExportPlugin that strips out this metadata. No idea if what I have done is "correct", though. 😅

extends EditorExportPlugin

const wizard_config = preload("config/wizard_config.gd")

func _export_file(path: String, type: String, features: PoolStringArray) -> void:
    if type != "PackedScene": return

    var scene : PackedScene =  ResourceLoader.load(path, type, true)
    var scene_changed := false
    var root_node := scene.instance(PackedScene.GEN_EDIT_STATE_INSTANCE)
    var nodes := [root_node]

    while not nodes.empty():
        var node : Node = nodes.pop_front()

        for child in node.get_children():
            nodes.push_back(child)

        if _remove_meta(node):
            scene_changed = true

    #what am I supposed to do here
    if scene_changed:
        var filtered_scene := PackedScene.new()
        if filtered_scene.pack(root_node) != OK:
            print("Somthing went wrong")
            return

        var content := _get_scene_content(path, filtered_scene)

        add_file(path, content, true)

    root_node.free()

func _remove_meta(node:Node) -> bool:
    if node.has_meta(wizard_config.META_NAME):
        node.remove_meta(wizard_config.META_NAME)
        print("Removed metadata")
        return true

    return false

func _get_scene_content(path:String, scene:PackedScene) -> PoolByteArray:
    var tmp_path = OS.get_cache_dir()  + "tmp_scene." + path.get_extension()
    ResourceSaver.save(tmp_path, scene)

    var tmp_file = File.new()
    tmp_file.open(tmp_path,File.READ)
    var content : PoolByteArray = tmp_file.get_buffer(tmp_file.get_len())
    tmp_file.close()

    var dir = Directory.new()

    if dir.file_exists(tmp_path):
        dir.remove(tmp_path)

    return content
viniciusgerevini commented 2 years ago

@dfkeenan That's great. Sorry, I missed these notifications. I don´t think you need to bother with the EditorExportPlugin though. The size of the metadata is negligible.

I do think it would be nice to have a config for it, though, in case people want to keep the previous behaviour (for any reason), or maybe as a first step to move to meta.

It's looking good so far.