Skip to content

Instantly share code, notes, and snippets.

@codorizzi
Created September 20, 2022 12:43
Show Gist options
  • Save codorizzi/44b4544f4f34c80ec9295e454cea9d9e to your computer and use it in GitHub Desktop.
Save codorizzi/44b4544f4f34c80ec9295e454cea9d9e to your computer and use it in GitHub Desktop.
using Newtonsoft.Json;
using Utilities.GameSettings;
namespace Framework.Map {
public class BuildingTypes : GameSetting<BuildingTypes, BuildingTypes.DataModel> {
#region Sub Classes / Enums
// requires static enum to be defined
public enum IdTypes {
Market = 0,
Hovel = 1,
}
// requires a data model to be defined, default values can be initialized here
public class DataModel : SettingModel {
[JsonProperty] public int Capacity = 2;
}
#endregion
#region Construction
protected override void Initialize() {
base.Initialize(typeof(IdTypes));
}
#endregion
#region Static Methods
// these are optional
public static DataModel GetByType(IdTypes idTypes) {
return GetById(idTypes.ToString(), true);
}
public static DataModel GetByType(string name) {
return GetByName(name, true);
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using UnityEngine;
namespace Utilities.GameSettings {
public abstract class GameSetting<TGameSetting, TDataModel>
where TGameSetting : GameSetting<TGameSetting, TDataModel>, new()
where TDataModel : SettingModel {
#region Exceptions
[Serializable]
class AmbiguousMatch : Exception {
public AmbiguousMatch() { }
public AmbiguousMatch(string message) : base(message) { }
}
#endregion
#region Static Properties
private static TGameSetting _instance;
// ReSharper disable once StaticMemberInGenericType
private static string _path = string.Empty;
public static TGameSetting Instance {
get {
_instance ??= DeserializeSettings();
return _instance;
}
}
#endregion
#region Public Properties
[JsonProperty] public Dictionary<string, SettingModel> Data { get; protected set; }
#endregion
#region Abstract Methods
protected abstract void Initialize();
/// <summary>
/// Initializes the settings.
/// </summary>
/// <param name="enumType">Type Enum</param>
/// <typeparam name="TDataModel">Setting Model</typeparam>
protected virtual void Initialize(Type enumType) {
Data ??= new Dictionary<string, SettingModel>();
// for each enum value, create a new setting model
foreach (var value in Enum.GetValues(enumType)) {
int id = (int) value;
string name = value.ToString();
SettingModel model = Activator.CreateInstance<TDataModel>();
model.Id = id.ToString();
model.Name = name;
Data.Add(name, model);
}
}
#endregion
#region Serialization
protected static TGameSetting DeserializeSettings() {
string path = GetPath();
string json = File.ReadAllText(path);
TGameSetting settings = null;
try {
settings = JsonConvert.DeserializeObject<TGameSetting>(json, new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.Auto
});
} catch (JsonSerializationException) { }
if (settings != null) return settings;
// create new TGameSetting
settings = new TGameSetting();
settings.Initialize();
settings.SerializeSettings();
return settings;
}
public void SerializeSettings() {
string path = GetPath();
string json = JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Auto
});
File.WriteAllText(path, json);
}
public static void Serialize() {
Instance.SerializeSettings();
}
#endregion
#region Access Methods
public static TDataModel GetById(string id, bool exact=false) {
if (exact) {
if(Instance.Data.ContainsKey(id)) return (TDataModel)Instance.Data[id];
} else {
List<string> matches = Instance.Data.Keys.ToList().FindAll(key => key == id).ToList();
switch (matches.Count) {
case > 1:
throw new AmbiguousMatch($"Multiple matches found for id: {id}");
case 1:
return (TDataModel)Instance.Data[matches[0]];
}
}
throw new Exception($"No matching id found for: {id}");
}
public static TDataModel GetByName(string name, bool exact=false) {
if (exact) {
return (TDataModel)Instance.Data.Values.First(x => x.Name == name);
}
List<SettingModel> matches = Instance.Data.Values.ToList().FindAll(key => key.Name.Contains(name)).ToList();
switch (matches.Count) {
case > 1:
throw new AmbiguousMatch($"Multiple matches found for name: {name}");
case 1:
return (TDataModel)matches[0];
}
throw new Exception($"No matching name found for: {name}");
}
#endregion
#region Private Methods
private static string GetPath() {
if(_path != string.Empty) return _path;
string path = $"{Application.streamingAssetsPath}/json/{typeof(TGameSetting).Name}.json";
if (!Directory.Exists(Path.GetDirectoryName(path))) {
string directory = Path.GetDirectoryName(path);
if(directory != null)
Directory.CreateDirectory(directory);
}
if (!File.Exists(path)) {
using (FileStream fs = File.Create(path)) {
fs.Close();
}
}
return path;
}
#endregion
}
}
using Newtonsoft.Json;
namespace Utilities.GameSettings {
public abstract class SettingModel {
[JsonProperty] public string Id;
[JsonProperty] public string Name;
}
}
@codorizzi
Copy link
Author

Some test code that shows accessing the data.

    private void AccessBuilding() {
        BuildingTypes.DataModel path = BuildingTypes.GetByType(Buildings.Market);
        Debug.Log(path.Id);
        Debug.Log(path.Capacity);
        
        BuildingTypes.DataModel path2 = BuildingTypes.GetByType(Buildings.Hovel);
        Debug.Log(path2.Id);
        Debug.Log(path2.Capacity);
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment