Skip to content

Commit 0de3e81

Browse files
committed
Added save slots feature and several other breaking changes. Incrementing minor version to 0.9.0
1 parent e2e6074 commit 0de3e81

File tree

13 files changed

+164
-105
lines changed

13 files changed

+164
-105
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [0.9.0] - 2025-08-06
4+
- **Breaking Change**: Added support for save slots! The SaveManager now requires a save slot parameter for all methods that interact with save data. This allows for multiple save files to be managed simultaneously, such as for different runs or user profiles.
5+
- **Breaking Change**: LoadDefaults() is a new method that replaces the previous Load() method signature that would allow for a boolean to ignore save data.
6+
- Erase() and Delete() methods no longer have a restoreDefaultSaveState parameter. Instead, it is recommended to use LoadDefaults() to restore the default state of ISaveables after calling Erase() or Delete().
7+
- Updated license dates in all files to reflect the current year.
8+
39
## [0.8.3] - 2025-07-30
410
- Added .meta files back for GitHub files since their absence was causing errors in Unity due to package folders being immutable.
511

Editor/SaveAsyncMenu.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// MIT License - Copyright (c) 2024 BUCK Design LLC - https://github.com/buck-co
1+
// MIT License - Copyright (c) 2025 BUCK Design LLC - https://github.com/buck-co
22

33
using UnityEngine;
44
using UnityEditor;

Runtime/Encryption.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// MIT License - Copyright (c) 2024 BUCK Design LLC - https://github.com/buck-co
1+
// MIT License - Copyright (c) 2025 BUCK Design LLC - https://github.com/buck-co
22

33
using System;
44

Runtime/FileHandler.cs

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// MIT License - Copyright (c) 2024 BUCK Design LLC - https://github.com/buck-co
1+
// MIT License - Copyright (c) 2025 BUCK Design LLC - https://github.com/buck-co
22

33
using System;
44
using UnityEngine;
@@ -63,10 +63,18 @@ protected virtual void ValidatePath(string pathOrFilename)
6363
/// </code>
6464
/// </summary>
6565
/// <param name="pathOrFilename">The path or filename of the file that will be combined with the persistent data path.</param>
66-
protected string GetPartialPath(string pathOrFilename)
66+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
67+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
68+
protected string GetPartialPath(string pathOrFilename, int saveSlotIndex = -1)
6769
{
6870
ValidatePath(pathOrFilename);
69-
return $"{pathOrFilename}{FilenameSuffix}{FileExtension}";
71+
72+
string path = $"{pathOrFilename}{FilenameSuffix}{FileExtension}";
73+
74+
if (saveSlotIndex > -1)
75+
return Path.Combine($"slot{saveSlotIndex}", path);
76+
77+
return path;
7078
}
7179

7280
/// <summary>
@@ -77,8 +85,10 @@ protected string GetPartialPath(string pathOrFilename)
7785
/// </code>
7886
/// </summary>
7987
/// <param name="pathOrFilename">The path or filename of the file that will be combined with the persistent data path.</param>
80-
protected virtual string GetFullPath(string pathOrFilename)
81-
=> Path.Combine(m_persistentDataPath, GetPartialPath(pathOrFilename));
88+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
89+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
90+
protected virtual string GetFullPath(string pathOrFilename, int saveSlotIndex = -1)
91+
=> Path.Combine(m_persistentDataPath, GetPartialPath(pathOrFilename, saveSlotIndex));
8292

8393
/// <summary>
8494
/// Returns true if a file exists at the given path or filename.
@@ -88,9 +98,11 @@ protected virtual string GetFullPath(string pathOrFilename)
8898
/// </code>
8999
/// </summary>
90100
/// <param name="pathOrFilename">The path or filename of the file to check.</param>
101+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
102+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
91103
/// <returns>True if the file exists; otherwise, false.</returns>
92-
public virtual bool Exists(string pathOrFilename)
93-
=> File.Exists(GetFullPath(pathOrFilename));
104+
public virtual bool Exists(string pathOrFilename, int saveSlotIndex = -1)
105+
=> File.Exists(GetFullPath(pathOrFilename, saveSlotIndex));
94106

95107
/// <summary>
96108
/// Writes the given content to a file at the given path or filename.
@@ -102,18 +114,18 @@ public virtual bool Exists(string pathOrFilename)
102114
/// <param name="pathOrFilename">The path or filename of the file to write.</param>
103115
/// <param name="content">The string to write to the file.</param>
104116
/// <param name="cancellationToken">The cancellation token should be the same one from the calling MonoBehaviour.</param>
105-
public virtual async Task WriteFile(string pathOrFilename, string content, CancellationToken cancellationToken)
117+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
118+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
119+
public virtual async Task WriteFile(string pathOrFilename, string content, CancellationToken cancellationToken, int saveSlotIndex = -1)
106120
{
107-
string fullPath = GetFullPath(pathOrFilename);
121+
string fullPath = GetFullPath(pathOrFilename, saveSlotIndex);
108122

109123
// Get the directory path from the full file path
110124
string directoryPath = Path.GetDirectoryName(fullPath);
111125

112126
// Create the directory structure if it doesn't exist
113127
if (!string.IsNullOrEmpty(directoryPath))
114-
{
115128
Directory.CreateDirectory(directoryPath);
116-
}
117129

118130
await File.WriteAllTextAsync(fullPath, content, cancellationToken).ConfigureAwait(false);
119131
}
@@ -127,8 +139,10 @@ public virtual async Task WriteFile(string pathOrFilename, string content, Cance
127139
/// </summary>
128140
/// <param name="pathOrFilename">The path or filename of the file to write.</param>
129141
/// <param name="content">The string to write to the file.</param>
130-
public virtual async Task WriteFile(string pathOrFilename, string content)
131-
=> await WriteFile(pathOrFilename, content, CancellationToken.None);
142+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
143+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
144+
public virtual async Task WriteFile(string pathOrFilename, string content, int saveSlotIndex = -1)
145+
=> await WriteFile(pathOrFilename, content, CancellationToken.None, saveSlotIndex);
132146

133147
/// <summary>
134148
/// Returns the contents of a file at the given path or filename.
@@ -139,23 +153,31 @@ public virtual async Task WriteFile(string pathOrFilename, string content)
139153
/// </summary>
140154
/// <param name="pathOrFilename">The path or filename of the file to read.</param>
141155
/// <param name="cancellationToken">The cancellation token should be the same one from the calling MonoBehaviour.</param>
142-
public virtual async Task<string> ReadFile(string pathOrFilename, CancellationToken cancellationToken)
156+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
157+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
158+
public virtual async Task<string> ReadFile(string pathOrFilename, CancellationToken cancellationToken, int saveSlotIndex = -1)
143159
{
144160
try
145161
{
162+
string fullPath = GetFullPath(pathOrFilename, saveSlotIndex);
163+
146164
// If the file does not exist, return an empty string and log a warning.
147-
if (!Exists(pathOrFilename))
165+
if (!Exists(pathOrFilename, saveSlotIndex))
148166
{
149-
Debug.LogWarning($"FileHandler: File does not exist at path or filename \"{pathOrFilename}\". This may be expected if the file has not been created yet.");
167+
Debug.LogWarning($"FileHandler: File does not exist at path \"{fullPath}\". This may be expected if the file has not been created yet.");
150168
return string.Empty;
151169
}
152170

153-
string fileContent = await File.ReadAllTextAsync(GetFullPath(pathOrFilename), cancellationToken).ConfigureAwait(false);
171+
string fileContent = await File.ReadAllTextAsync(GetFullPath(pathOrFilename, saveSlotIndex), cancellationToken).ConfigureAwait(false);
154172

155173
// If the file is empty, return an empty string and log a warning.
156174
if (string.IsNullOrEmpty(fileContent))
157175
{
158-
Debug.LogWarning($"FileHandler: The file \"{pathOrFilename}\" was empty. This may be expected if the file has been erased.");
176+
if (saveSlotIndex > -1)
177+
Debug.LogWarning($"FileHandler: The file \"{pathOrFilename}\" in slot index {saveSlotIndex} was empty. This may be expected if the file has been erased.");
178+
else
179+
Debug.LogWarning($"FileHandler: The file \"{pathOrFilename}\" was empty. This may be expected if the file has been erased.");
180+
159181
return string.Empty;
160182
}
161183

@@ -181,8 +203,10 @@ public virtual async Task<string> ReadFile(string pathOrFilename, CancellationTo
181203
/// </code>
182204
/// </summary>
183205
/// <param name="pathOrFilename">The path or filename of the file to read.</param>
184-
public virtual async Task<string> ReadFile(string pathOrFilename)
185-
=> await ReadFile(pathOrFilename, CancellationToken.None);
206+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
207+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
208+
public virtual async Task<string> ReadFile(string pathOrFilename, int saveSlotIndex = -1)
209+
=> await ReadFile(pathOrFilename, CancellationToken.None, saveSlotIndex);
186210

187211
/// <summary>
188212
/// Erases a file at the given path or filename. The file will still exist on disk, but it will be empty.
@@ -194,8 +218,10 @@ public virtual async Task<string> ReadFile(string pathOrFilename)
194218
/// </summary>
195219
/// <param name="pathOrFilename">The path or filename of the file to erase.</param>
196220
/// <param name="cancellationToken">The cancellation token should be the same one from the calling MonoBehaviour.</param>
197-
public virtual async Task Erase(string pathOrFilename, CancellationToken cancellationToken)
198-
=> await WriteFile(pathOrFilename, string.Empty, cancellationToken);
221+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
222+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
223+
public virtual async Task Erase(string pathOrFilename, CancellationToken cancellationToken, int saveSlotIndex = -1)
224+
=> await WriteFile(pathOrFilename, string.Empty, cancellationToken, saveSlotIndex);
199225

200226
/// <summary>
201227
/// Erases a file at the given path or filename. The file will still exist on disk, but it will be empty.
@@ -206,8 +232,10 @@ public virtual async Task Erase(string pathOrFilename, CancellationToken cancell
206232
/// </code>
207233
/// </summary>
208234
/// <param name="pathOrFilename">The path or filename of the file to erase.</param>
209-
public virtual async Task Erase(string pathOrFilename)
210-
=> await Erase(pathOrFilename, CancellationToken.None);
235+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
236+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
237+
public virtual async Task Erase(string pathOrFilename, int saveSlotIndex = -1)
238+
=> await Erase(pathOrFilename, CancellationToken.None, saveSlotIndex);
211239

212240
/// <summary>
213241
/// Deletes a file at the given path or filename. This will remove the file from disk.
@@ -219,9 +247,11 @@ public virtual async Task Erase(string pathOrFilename)
219247
/// </summary>
220248
/// <param name="pathOrFilename">The path or filename of the file to delete.</param>
221249
/// <param name="cancellationToken">The cancellation token should be the same one from the calling MonoBehaviour.</param>
222-
public virtual async Task Delete(string pathOrFilename, CancellationToken cancellationToken)
250+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
251+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
252+
public virtual async Task Delete(string pathOrFilename, CancellationToken cancellationToken, int saveSlotIndex = -1)
223253
{
224-
string fullPath = GetFullPath(pathOrFilename);
254+
string fullPath = GetFullPath(pathOrFilename, saveSlotIndex);
225255
if (File.Exists(fullPath))
226256
await Task.Run(() => File.Delete(fullPath), cancellationToken).ConfigureAwait(false);
227257
}
@@ -235,7 +265,9 @@ public virtual async Task Delete(string pathOrFilename, CancellationToken cancel
235265
/// </code>
236266
/// </summary>
237267
/// <param name="pathOrFilename">The path or filename of the file to delete.</param>
238-
public virtual async Task Delete(string pathOrFilename)
239-
=> await Delete(pathOrFilename, CancellationToken.None);
268+
/// <param name="saveSlotIndex">The save slot index to use. Passing in anything below 0 will not
269+
/// use a slot, which can be good for things like settings files or for projects that don't use save slots.</param>
270+
public virtual async Task Delete(string pathOrFilename, int saveSlotIndex = -1)
271+
=> await Delete(pathOrFilename, CancellationToken.None, saveSlotIndex);
240272
}
241273
}

Runtime/ISaveable.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// MIT License - Copyright (c) 2024 BUCK Design LLC - https://github.com/buck-co
1+
// MIT License - Copyright (c) 2025 BUCK Design LLC - https://github.com/buck-co
22

33
namespace Buck.SaveAsync
44
{

0 commit comments

Comments
 (0)