Common plugin parts and implementations

Configuration

#region Configuration

private Configuration _config;

private class Configuration
{
    [JsonProperty("Example String")]
    public string Example = "example";

    [JsonProperty("Example List", ObjectCreationHandling = ObjectCreationHandling.Replace)]
    public List<string> ExampleList = new List<string>() { "example" };
}

protected override void LoadConfig()
{
    base.LoadConfig();
    try
    {
        _config = Config.ReadObject<Configuration>();
        if (_config == null) throw new Exception();
        SaveConfig();
    }
    catch
    {
        PrintError("Your configuration file contains an error. Using default configuration values.");
        LoadDefaultConfig();
    }
}

protected override void SaveConfig() => Config.WriteObject(_config);

protected override void LoadDefaultConfig() => _config = new Configuration();

#endregion

Single Datafile

#region Work with Data

private PluginData _data;

private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, _data);

private void LoadData()
{
    try
    {
        _data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(Name);
    }
    catch (Exception e)
    {
        PrintError(e.ToString());
    }

    if (_data == null) _data = new PluginData();
}

private class PluginData
{
    public string ExampleString = string.Empty;
    public List<string> ExampleList = new List<string>();
}

#endregion

Multiple Datafiles

You may implement datafiles per player, building etc. You would need to copy the CustomData class to implement new files. Remember to change the path under CustomData.BaseFolder and rename CustomData to something more meaningful, such as PlayerPreferences. You might consider running CustomData.LoadedData.Clear() on Unload.

#region Work with Data

private abstract class SplitDatafile<T> where T : SplitDatafile<T>, new()
{
    public static Dictionary<string, T> LoadedData = new Dictionary<string, T>();

    protected static string[] GetFiles(string baseFolder)
    {
        try
        {
            var json = ".json".Length;
            var paths = Interface.Oxide.DataFileSystem.GetFiles(baseFolder);
            for (var i = 0; i < paths.Length; i++)
            {
                var path = paths[i];
                var separatorIndex = path.LastIndexOf(Path.DirectorySeparatorChar);
                
                // We have to do this since GetFiles returns paths instead of filenames
                // And other methods require filenames
                paths[i] = path.Substring(separatorIndex + 1, path.Length - separatorIndex - 1 - json);
            }
            
            return paths;
        }
        catch
        {
            return Array.Empty<string>();
        }
    }

    protected static T Save(string baseFolder, string filename)
    {
        T data;
        if (!LoadedData.TryGetValue(filename, out data))
            return null;

        Interface.Oxide.DataFileSystem.WriteObject(baseFolder + filename, data);
        return data;
    }

    protected static T Get(string baseFolder, string filename)
    {
        T data;
        if (LoadedData.TryGetValue(filename, out data))
            return data;
        
        return null;
    }

    protected static T GetOrLoad(string baseFolder, string filename)
    {
        T data;
        if (LoadedData.TryGetValue(filename, out data))
            return data;

        try
        {
            data = Interface.Oxide.DataFileSystem.ReadObject<T>(baseFolder + filename);
        }
        catch (Exception e)
        {
            Interface.Oxide.LogError(e.ToString());
        }

        return LoadedData[filename] = data;
    }

    protected static T GetOrCreate(string baseFolder, string path)
    {
        return GetOrLoad(baseFolder, path) ?? (LoadedData[path] = new T());
    }
}

private class CustomData : SplitDatafile<CustomData>
{
    public string ExampleString = string.Empty;
    public List<string> ExampleList = new List<string>();
    
    public static readonly string BaseFolder = "PluginName" + Path.DirectorySeparatorChar + "Subfolder" + Path.DirectorySeparatorChar;
    public static string[] GetFiles() => GetFiles(BaseFolder);
    public static CustomData Save(string filename) => Save(BaseFolder, filename);
    public static CustomData Get(string filename) => Get(BaseFolder, filename);
    public static CustomData GetOrLoad(string filename) => GetOrLoad(BaseFolder, filename);
    public static CustomData GetOrCreate(string filename) => GetOrCreate(BaseFolder, filename);
}

#endregion