This commit is contained in:
2026-07-09 21:33:33 +02:00
commit 84a2a365f3
2364 changed files with 950134 additions and 0 deletions
@@ -0,0 +1,28 @@
using System;
using UnityEditor;
using UnityEngine;
namespace VOID.Generic.SaveLoad
{
[Serializable]
public class PlayerProfile
{
public string guid;
public string name;
public string version;
public int essence;
public PlayerProfile(string profileName)
{
name = profileName;
guid = GUID.Generate().ToString();
version = Application.version;
}
public static bool operator ==(PlayerProfile obj1, PlayerProfile obj2) => obj1?.guid == obj2?.guid;
public static bool operator !=(PlayerProfile obj1, PlayerProfile obj2) => !(obj1 == obj2);
public override bool Equals(object obj) => obj is PlayerProfile other && guid == other.guid;
public override int GetHashCode() => guid.GetHashCode();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: befc689e45084965b9ede9a3b152b430
timeCreated: 1723556727
@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Sirenix.OdinInspector;
using UnityEngine;
namespace VOID.Generic.SaveLoad
{
/// <summary>
/// Manages profiles (save files) and caches currently active one.
/// </summary>
public class PlayerProfileManager : MonoBehaviour
{
public static PlayerProfileManager current { get; private set; }
// Paths
private static string directoryPath => Path.Combine(Application.persistentDataPath, "profiles");
private static string fileNameTemplate => "{0}_{1}.profile";
private static string fileNameMask => "*.profile";
private static string fileNameWithDefaultProfile => "default";
// Cache
public PlayerProfile activeProfile { get; private set; }
// Events
public event Action<PlayerProfile> OnProfileChange;
private void Awake()
{
if (current)
{
Debug.LogWarning("Instance of " + nameof(PlayerProfileManager) + " already exists!");
Destroy(this);
return;
}
// Only one instance for whole game, this should manage player profiles
// DontDestroyOnLoad works only on root game object so we need to unparent it
current = this;
current.transform.parent = null;
DontDestroyOnLoad(this);
}
public PlayerProfile GetDefaultProfile()
{
var defaultPath = Path.Combine(directoryPath, fileNameWithDefaultProfile);
// Default file not found
if (!File.Exists(defaultPath)) return null;
var guid = File.ReadAllText(defaultPath);
// Profile not found, by guid from default file
var file = Directory.GetFiles(directoryPath, string.Format(fileNameTemplate, "*", guid)).FirstOrDefault();
if (file == null) return null;
return JsonUtility.FromJson<PlayerProfile>(File.ReadAllText(file));
}
/// <summary>
/// Parse all profiles from files and returns them.
/// There is no caching, it will scan files everytime it is called.
/// </summary>
[Button]
public List<PlayerProfile> GetProfiles()
{
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
return Directory.GetFiles(directoryPath, fileNameMask)
.Select(File.ReadAllText)
.Select(JsonUtility.FromJson<PlayerProfile>)
.ToList();
}
/// <summary>
/// Creates new profile with given name and saves it as file.
/// </summary>
[Button]
public PlayerProfile CreateProfile(string name)
{
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
var newProfile = new PlayerProfile(name);
SaveProfile(newProfile);
return newProfile;
}
/// <summary>
/// Removes given profile from files and if its currently active unselects it.
/// </summary>
/// <param name="playerProfile"></param>
[Button]
public void DeleteProfile([NotNull] PlayerProfile playerProfile)
{
if (playerProfile == activeProfile) SelectProfile(null);
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
File.Delete(GetFullPathFor(playerProfile));
}
/// <summary>
/// Saves currently active profile as file.
/// </summary>
public void SaveProfile()
{
SaveProfile(activeProfile);
}
/// <summary>
/// Saves given profile as file.
/// </summary>
[Button]
private void SaveProfile([NotNull] PlayerProfile playerProfile)
{
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
File.WriteAllText(GetFullPathFor(playerProfile), JsonUtility.ToJson(playerProfile, true));
}
/// <summary>
/// Set active given profile. Will be selected automatically next time.
/// </summary>
[Button]
public void SelectProfile([CanBeNull] PlayerProfile playerProfile)
{
activeProfile = playerProfile;
OnProfileChange?.Invoke(activeProfile);
// No profile found - create it for user
activeProfile ??= CreateProfile("New Player Profile");
// Our selected profile will be default one when starting game again
File.WriteAllText(Path.Combine(directoryPath, fileNameWithDefaultProfile), activeProfile.guid);
}
private string GetFullPathFor([NotNull] PlayerProfile playerProfile)
{
var fileName = string.Format(fileNameTemplate, playerProfile.name, playerProfile.guid);
return Path.Combine(directoryPath, fileName);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 50391e30f30e148468b4fd1f4506ff92