Resource saved in script contains no script?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By 9BitStrider

Working on a save system for my game’s config file. I recently learned about saving values as a resource for easy saving and loading of config data. Problem is, when I go to save the resource, it’s empty.

Resource Code:

using Godot;
using System;
using Array = Godot.Collections.Array;
using Dictionary = Godot.Collections.Dictionary;

public class MasterConfig : Resource
{
    // Constants.
    [Export] private string CONFIGPATH = "user://config.tres";

    // Editable values;
    [Export] public Dictionary inputMap = new Dictionary();
    [Export] public float soundVolume;
    [Export] public float musicVolume;    
    [Export] public string audioStyle;
    [Export] public bool chargeFade;
    [Export] public float currentScale;
    [Export] public int shaderID;
    [Export] public bool dotMatrix;
    [Export] public int colorblind;
    [Export] public int wallpaper;
    [Export] public bool skipReady;
    [Export] public bool dashDownJump;
    [Export] public bool dashDoubleTap;
    [Export] public bool autoCharge;
    [Export] public bool autoFire;
    [Export] public bool rapidFire;
    [Export] public bool loseCharge;
    [Export] public bool chargeKickBack;

    // Default values;
    [Export] public Dictionary defaultIM = new Dictionary();
    [Export] public float defaultSV;
    [Export] public float defaultMV;
    [Export] public string defaultAS;
    [Export] public bool defaultCF;
    [Export] public float defaultCS;
    [Export] public int defaultSI;
    [Export] public bool defaultDM;
    [Export] public int defaultCB;
    [Export] public int defaultWP;
    [Export] public bool defaultSR;
    [Export] public bool defaultDDJ;
    [Export] public bool defaultDDT;
    [Export] public bool defaultAC;
    [Export] public bool defaultAF;
    [Export] public bool defaultRF;
    [Export] public bool defaultLC;
    [Export] public bool defaultCKB;

    public void load() {
        ResourceLoader.Load(CONFIGPATH);
    }

    public void save() {
        ResourceSaver.Save(CONFIGPATH, this);
    }
}

Save and loading code in Master:

private void loadConfig() {  
        bool err = ResourceLoader.Exists(CONFIGPATH);

        switch(err) {
            case true:
                mc = (MasterConfig)ResourceLoader.Load(CONFIGPATH);
                mc.load();

                // Load the values from config.
                for(int i = 0; i < InputMap.GetActions().Count; i++) {
                    string action = (string)InputMap.GetActions()[i];

                    InputMap.ActionEraseEvents(action);
                    GD.Print("InputEvents for action ",action," erased.");

                    for(int j = 0; j < InputMap.GetActionList(action).Count; j++) {
                        InputEvent input = (InputEvent)InputMap.GetActionList(action)[j];

                        InputMap.ActionAddEvent(action, input);
                        GD.Print("InputEvent ",input," added to ",action);
                    }
                }

                _soundVolume = mc.soundVolume;
                _musicVolume = mc.musicVolume;
                audioStyle = mc.audioStyle;
                chargeFade = mc.chargeFade;
                _currentScale = mc.currentScale;
                _shaderID = mc.shaderID;
                _dotMatrix = mc.dotMatrix;
                _cb = mc.colorblind;
                _wallpaper = mc.wallpaper;
                skipReady = mc.skipReady;
                dashDownJump = mc.dashDownJump;
                dashDoubleTap = mc.dashDoubleTap;
                autoCharge = mc.autoCharge;
                autoFire = mc.autoFire;
                rapidFire = mc.rapidFire;
                loseCharge = mc.loseCharge;
                chargeKickBack = mc.chargeKickBack;

                // Refresh controller directory after every load.
                GDScript getScript = (GDScript)GD.Load("res://addons/controller_icons/ControllerIcons.gd");
                Godot.Object controllerIcons = (Godot.Object)getScript.New();
                controllerIcons.Call("refresh");
                break;
        case false:
            mc = new MasterConfig();
            saveDefaults();
            break;
        }
    }

    private void saveDefaults() {
        for(int i = 0; i < InputMap.GetActions().Count; i++) {
            string action = (string)InputMap.GetActions()[i];
            mc.inputMap.Add(action, InputMap.GetActionList(action));
            mc.defaultIM.Add(action, InputMap.GetActionList(action));
        }

        mc.defaultSV = soundVolume;
        mc.defaultMV = musicVolume;
        mc.defaultAS = audioStyle;
        mc.defaultCF = chargeFade;
        mc.defaultCS = currentScale;
        mc.defaultSI = shaderID;
        mc.defaultDM = dotMatrix;
        mc.defaultCB = cb;
        mc.defaultWP = wallpaper;
        mc.defaultSR = skipReady;
        mc.defaultDDJ = dashDownJump;
        mc.defaultDDT = dashDoubleTap;
        mc.defaultAC = autoCharge;
        mc.defaultAF = autoFire;
        mc.defaultRF = rapidFire;
        mc.defaultLC = loseCharge;
        mc.defaultCKB = chargeKickBack;

        saveConfig();
    }

    public void saveConfig() {
        for(int i = 0; i < InputMap.GetActions().Count; i++) {
            string action = (string)InputMap.GetActions()[i];
            mc.inputMap[action] = InputMap.GetActionList(action);
        }

        mc.soundVolume = soundVolume;
        mc.musicVolume = musicVolume;
        mc.audioStyle = audioStyle;
        mc.chargeFade = chargeFade;
        mc.currentScale = currentScale;
        mc.shaderID = shaderID;
        mc.dotMatrix = dotMatrix;
        mc.colorblind = cb;
        mc.wallpaper = wallpaper;
        mc.skipReady = skipReady;
        mc.dashDownJump = dashDownJump;
        mc.dashDoubleTap = dashDoubleTap;
        mc.autoCharge = autoCharge;
        mc.autoFire = autoFire;
        mc.rapidFire = rapidFire;
        mc.loseCharge = loseCharge;
        mc.chargeKickBack = chargeKickBack;

        mc.save();
    }

This indeed creates config.tres, but the file only contains the following:

[gd_resource type="Resource" load_steps=2 format=2]

[sub_resource type="CSharpScript" id=1]

[resource]
script = SubResource( 1 )

Not sure where I messed up. Any and all help is deeply appreciated.

:bust_in_silhouette: Reply From: 9BitStrider

I was able to get the function working with a bit of trickery. Had to make a new resource and set the script I wanted to it. Then used the following code to save it:

Resource mc = (Resource)GD.Load("res://scripts/resources/config.tres");

        Dictionary temp = new Dictionary();

        for(int i = 0; i < InputMap.GetActions().Count; i++) {
            string action = (string)InputMap.GetActions()[i];
            temp.Add(action, InputMap.GetActionList(action));
        }

        mc.Set("colorblind", 3);
        mc.Set("inputMap", temp);

        ResourceSaver.Save(CONFIGPATH, mc);

Here’s hoping this is fixed in Godot 3.6 or 4.1.