-
-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System; | ||
using System.Globalization; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace BoundfoxStudios.FairyTaleDefender.Extensions | ||
{ | ||
public static class StringExtensions | ||
{ | ||
// From: https://stackoverflow.com/a/19937132/959687 | ||
// white space, em-dash, en-dash, underscore | ||
static readonly Regex WordDelimiters = new(@"[\s—–_]", RegexOptions.Compiled); | ||
|
||
// characters that are not valid | ||
static readonly Regex InvalidChars = new(@"[^a-z0-9\-]", RegexOptions.Compiled); | ||
|
||
// multiple hyphens | ||
static readonly Regex MultipleHyphens = new(@"-{2,}", RegexOptions.Compiled); | ||
|
||
private static string ToSlug(string value) | ||
{ | ||
// trim whitespace from start and end | ||
value = value.Trim(); | ||
|
||
// convert to lower case | ||
value = value.ToLowerInvariant(); | ||
|
||
// remove diacritics (accents) | ||
value = RemoveDiacritics(value); | ||
|
||
// ensure all word delimiters are hyphens | ||
value = WordDelimiters.Replace(value, "-"); | ||
|
||
// strip out invalid characters | ||
value = InvalidChars.Replace(value, ""); | ||
|
||
// replace multiple hyphens (-) with a single hyphen | ||
value = MultipleHyphens.Replace(value, "-"); | ||
|
||
// trim hyphens (-) from ends | ||
return value.Trim('-'); | ||
} | ||
|
||
/// See: http://www.siao2.com/2007/05/14/2629747.aspx | ||
private static string RemoveDiacritics(string stIn) | ||
{ | ||
var stFormD = stIn.Normalize(NormalizationForm.FormD); | ||
var sb = new StringBuilder(); | ||
|
||
foreach (var t in stFormD) | ||
{ | ||
var uc = CharUnicodeInfo.GetUnicodeCategory(t); | ||
if (uc != UnicodeCategory.NonSpacingMark) | ||
{ | ||
sb.Append(t); | ||
} | ||
} | ||
|
||
return (sb.ToString().Normalize(NormalizationForm.FormC)); | ||
} | ||
|
||
public static string Slugify(this string input) => ToSlug(input); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System.IO; | ||
using System.Linq; | ||
using Cysharp.Threading.Tasks; | ||
using UnityEngine; | ||
|
||
namespace BoundfoxStudios.FairyTaleDefender.Infrastructure.FileManagement | ||
{ | ||
public class DirectoryManager | ||
{ | ||
private readonly string _rootPath = Application.persistentDataPath; | ||
|
||
/// <summary> | ||
/// Checks if the specified directory exists. | ||
/// </summary> | ||
/// <param name="directory">The directory to check.</param> | ||
public UniTask<bool> ExistsAsync(string directory) | ||
{ | ||
var result = Directory.Exists(CreateDirectoryPath(directory)); | ||
|
||
return UniTask.FromResult(result); | ||
} | ||
|
||
|
||
/// <summary> | ||
/// Lists all files in the given <paramref name="directory"/>. | ||
/// </summary> | ||
/// <param name="directory">The directory to list files.</param> | ||
/// <returns></returns> | ||
public UniTask<string[]> ListFilesAsync(string directory) | ||
{ | ||
var result = Directory.EnumerateFiles(CreateDirectoryPath(directory)); | ||
|
||
return UniTask.FromResult(result.ToArray()); | ||
} | ||
|
||
/// <summary> | ||
/// Lists all directories within the given <paramref name="directory"/>. | ||
/// </summary> | ||
/// <param name="directory">The directory to list directories.</param> | ||
/// <returns></returns> | ||
public UniTask<string[]> ListDirectoriesAsync(string directory) | ||
{ | ||
var result = Directory.EnumerateDirectories(CreateDirectoryPath(directory)); | ||
|
||
return UniTask.FromResult(result.ToArray()); | ||
} | ||
|
||
/// <summary> | ||
/// Deletes a directory recursively. | ||
/// </summary> | ||
/// <param name="directory">The directory to delete.</param> | ||
/// <returns></returns> | ||
public UniTask DeleteAsync(string directory) | ||
{ | ||
Directory.Delete(CreateDirectoryPath(directory), true); | ||
|
||
return UniTask.CompletedTask; | ||
} | ||
|
||
private string CreateDirectoryPath(string directory) => Path.Combine(_rootPath, directory); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
This file was deleted.
This file was deleted.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace BoundfoxStudios.FairyTaleDefender.Systems.SaveGameSystem | ||
{ | ||
public class SaveGame | ||
{ | ||
public SaveGameMeta Meta { get; } | ||
public SaveGameData Data { get; } | ||
|
||
public SaveGame(SaveGameMeta meta, SaveGameData data) | ||
{ | ||
Meta = meta; | ||
Data = data; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System; | ||
using BoundfoxStudios.FairyTaleDefender.Infrastructure.ScriptableObjects; | ||
|
||
namespace BoundfoxStudios.FairyTaleDefender.Systems.SaveGameSystem | ||
{ | ||
/// <summary> | ||
/// This is the actual save game that will be written to a file. | ||
/// </summary> | ||
[Serializable] | ||
public class SaveGameData | ||
{ | ||
/// <summary> | ||
/// Identifies the last played level. | ||
/// </summary> | ||
public IdentifiableSO? LastLevel; | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System; | ||
|
||
namespace BoundfoxStudios.FairyTaleDefender.Systems.SaveGameSystem | ||
{ | ||
/// <summary> | ||
/// Meta Information about a save game. | ||
/// </summary> | ||
[Serializable] | ||
public class SaveGameMeta | ||
{ | ||
public string Name = string.Empty; | ||
public DateTime LastPlayedDate; | ||
|
||
public string Directory { get; set; } | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using BoundfoxStudios.FairyTaleDefender.Common; | ||
using BoundfoxStudios.FairyTaleDefender.Extensions; | ||
using BoundfoxStudios.FairyTaleDefender.Infrastructure.FileManagement; | ||
using Cysharp.Threading.Tasks; | ||
using UnityEngine; | ||
|
||
namespace BoundfoxStudios.FairyTaleDefender.Systems.SaveGameSystem.ScriptableObjects | ||
{ | ||
[CreateAssetMenu(menuName = Constants.MenuNames.SaveGame + "/Save Game Manager")] | ||
public class SaveGameManagerSO : ScriptableObject | ||
{ | ||
private DirectoryManager _directoryManager = default!; | ||
private JsonFileManager _fileManager = default!; | ||
|
||
private void OnEnable() | ||
{ | ||
_directoryManager = new(); | ||
_fileManager = new(); | ||
} | ||
|
||
public async UniTask<List<SaveGameMeta>> ListAvailableSaveGamesAsync() | ||
{ | ||
var directories = await _directoryManager.ListDirectoriesAsync(Constants.SaveGames.DirectoryName); | ||
|
||
var candidates = new List<string>(); | ||
|
||
foreach (var directory in directories) | ||
{ | ||
if (await IsValidSaveGameAsync(directory)) | ||
{ | ||
candidates.Add(directory); | ||
} | ||
} | ||
|
||
var metas = new List<SaveGameMeta>(); | ||
|
||
foreach (var candidate in candidates) | ||
{ | ||
var meta = await _fileManager.ReadAsync<SaveGameMeta>(CreatePath(candidate, Constants.SaveGames.MetaFileName)); | ||
meta.Directory = candidate; | ||
|
||
metas.Add(meta); | ||
} | ||
|
||
return metas; | ||
} | ||
|
||
public async UniTask<SaveGame?> LoadSaveGameAsync(SaveGameMeta meta) | ||
{ | ||
if (!await IsValidSaveGameAsync(meta.Directory)) | ||
{ | ||
return null; | ||
} | ||
|
||
var saveGameData = | ||
await _fileManager.ReadAsync<SaveGameData>(CreatePath(meta.Directory, Constants.SaveGames.SaveGameFileName)); | ||
|
||
var saveGame = new SaveGame(meta, saveGameData); | ||
|
||
return saveGame; | ||
} | ||
|
||
public async UniTask<SaveGameMeta?> CreateSaveGameAsync(string saveName, SaveGameData data) | ||
{ | ||
var slugifiedName = saveName.Slugify(); | ||
|
||
var meta = new SaveGameMeta() | ||
{ | ||
Name = saveName, | ||
LastPlayedDate = DateTime.Now, | ||
Directory = CreatePath(slugifiedName), | ||
}; | ||
|
||
try | ||
{ | ||
await _fileManager.WriteAsync(Path.Combine(meta.Directory, Constants.SaveGames.MetaFileName), meta); | ||
await _fileManager.WriteAsync(Path.Combine(meta.Directory, Constants.SaveGames.SaveGameFileName), data); | ||
} | ||
|
||
catch | ||
{ | ||
await _directoryManager.DeleteAsync(meta.Directory); | ||
return null; | ||
} | ||
|
||
return meta; | ||
} | ||
|
||
public async UniTask<bool> SaveGameExistsAsync(string saveName) | ||
{ | ||
var slugifiedName = saveName.Slugify(); | ||
|
||
return await IsValidSaveGameAsync(slugifiedName); | ||
} | ||
|
||
private async UniTask<bool> IsValidSaveGameAsync(string directory) | ||
{ | ||
var metaFileExists = await _fileManager.ExistsAsync(CreatePath(directory, Constants.SaveGames.MetaFileName)); | ||
var saveGameFileExists = | ||
await _fileManager.ExistsAsync(CreatePath(directory, Constants.SaveGames.SaveGameFileName)); | ||
|
||
return metaFileExists && saveGameFileExists; | ||
} | ||
|
||
private string CreatePath(string directory, string fileName) => Path.Combine(CreatePath(directory), fileName); | ||
|
||
private string CreatePath(string directory) => Path.Combine(Constants.SaveGames.DirectoryName, directory); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.