ProjectDDD/Assets/0.Datas/02.Scripts/GoogldSheetManager.cs

323 lines
10 KiB (Stored with Git LFS)
C#

using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
using Newtonsoft.Json.Linq;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine.Serialization;
public class GoogleSheetManager : MonoBehaviour
{
[FormerlySerializedAs("isAccessGoogleSheet")]
[Tooltip("true: google sheet, false: local json")]
[SerializeField] private bool _isAccessGoogleSheet = true;
[FormerlySerializedAs("googleSheetUrl")]
[Tooltip("Google sheet appsscript webapp url")]
[SerializeField] private string _googleSheetUrl;
[FormerlySerializedAs("availSheets")]
[Tooltip("Google sheet avail sheet tabs. seperate `/`. For example `Sheet1/Sheet2`")]
[SerializeField] private string _availSheets = "Sheet1/Sheet2";
[FormerlySerializedAs("generateFolderPath")]
[Tooltip("For example `/GenerateGoogleSheet`")]
[SerializeField] private string _generateFolderPath = "/GenerateGoogleSheet";
[FormerlySerializedAs("googleSheetSO")] [Tooltip("You must approach through `GoogleSheetManager.SO<GoogleSheetSO>()`"), ReadOnly]
public ScriptableObject GoogleSheetSo;
[SerializeField, ReadOnly]
private string _lastUpdated;
private string JsonPath => $"{Application.dataPath}{_generateFolderPath}/GoogleSheetJson.json";
private string ClassPath => $"{Application.dataPath}{_generateFolderPath}/GoogleSheetClass.cs";
private string SoPath => $"Assets{_generateFolderPath}/GoogleSheetSO.asset";
private string[] _availSheetArray;
private string _json;
private bool _refeshTrigger;
private static GoogleSheetManager _instance;
public static T So<T>() where T : ScriptableObject
{
if (GetInstance().GoogleSheetSo == null)
{
Debug.Log($"googleSheetSO is null");
return null;
}
return GetInstance().GoogleSheetSo as T;
}
#if UNITY_EDITOR
[ContextMenu("FetchGoogleSheet")]
private async void FetchGoogleSheet()
{
//Init
_availSheetArray = _availSheets.Split('/');
if (_isAccessGoogleSheet)
{
Debug.Log($"Loading from google sheet..");
_json = await LoadDataGoogleSheet(_googleSheetUrl);
}
else
{
Debug.Log($"Loading from local json..");
_json = LoadDataLocalJson();
}
if (_json == null)
{
Debug.Log("Json is null.");
return;
}
bool isJsonSaved = SaveFileOrSkip(JsonPath, _json);
string allClassCode = GenerateCSharpClass(_json);
bool isClassSaved = SaveFileOrSkip(ClassPath, allClassCode);
if (isJsonSaved || isClassSaved)
{
_refeshTrigger = true;
UnityEditor.AssetDatabase.Refresh();
}
else
{
CreateGoogleSheetSo();
Debug.Log($"Fetch done.");
}
_lastUpdated = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
private async Task<string> LoadDataGoogleSheet(string url)
{
using (HttpClient client = new HttpClient())
{
try
{
byte[] dataBytes = await client.GetByteArrayAsync(url);
return Encoding.UTF8.GetString(dataBytes);
}
catch (HttpRequestException e)
{
Debug.LogError($"Request error: {e.Message}");
return null;
}
}
}
private string LoadDataLocalJson()
{
if (File.Exists(JsonPath))
{
return File.ReadAllText(JsonPath);
}
Debug.Log($"File not exist.\n{JsonPath}");
return null;
}
private bool SaveFileOrSkip(string path, string contents)
{
string directoryPath = Path.GetDirectoryName(path);
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
if (File.Exists(path) && File.ReadAllText(path).Equals(contents))
return false;
File.WriteAllText(path, contents);
return true;
}
private bool IsExistAvailSheets(string sheetName)
{
return Array.Exists(_availSheetArray, x => x == sheetName);
}
private string GenerateCSharpClass(string jsonInput)
{
JObject jsonObject = JObject.Parse(jsonInput);
StringBuilder classCode = new();
// Scriptable Object
classCode.AppendLine("using System;\nusing System.Collections.Generic;\nusing UnityEngine;\n");
classCode.AppendLine("/// <summary>You must approach through `GoogleSheetManager.SO<GoogleSheetSO>()`</summary>");
classCode.AppendLine("public class GoogleSheetSO : ScriptableObject\n{");
foreach (var sheet in jsonObject)
{
string className = sheet.Key;
if (!IsExistAvailSheets(className))
continue;
classCode.AppendLine($"\tpublic List<{className}> {className}List;");
}
classCode.AppendLine("}\n");
// Class
foreach (var jObject in jsonObject)
{
string className = jObject.Key;
if (!IsExistAvailSheets(className))
continue;
var items = (JArray)jObject.Value;
var firstItem = (JObject)items[0];
classCode.AppendLine($"[Serializable]\npublic class {className}\n{{");
// int > float > bool > string
int itemIndex = 0;
int propertyCount = ((JObject)items[0]).Properties().Count();
string[] propertyTypes = new string[propertyCount];
foreach (JToken item in items)
{
itemIndex = 0;
foreach (var property in ((JObject)item).Properties())
{
string propertyType = GetCSharpType(property.Value.Type);
string oldPropertyType = propertyTypes[itemIndex];
if (oldPropertyType == null)
{
propertyTypes[itemIndex] = propertyType;
}
else if (oldPropertyType == "int")
{
if (propertyType == "int") propertyTypes[itemIndex] = "int";
else if (propertyType == "float") propertyTypes[itemIndex] = "float";
else if (propertyType == "bool") propertyTypes[itemIndex] = "string";
else if (propertyType == "string") propertyTypes[itemIndex] = "string";
}
else if (oldPropertyType == "float")
{
if (propertyType == "int") propertyTypes[itemIndex] = "float";
else if (propertyType == "float") propertyTypes[itemIndex] = "float";
else if (propertyType == "bool") propertyTypes[itemIndex] = "string";
else if (propertyType == "string") propertyTypes[itemIndex] = "string";
}
else if (oldPropertyType == "bool")
{
if (propertyType == "int") propertyTypes[itemIndex] = "string";
else if (propertyType == "float") propertyTypes[itemIndex] = "string";
else if (propertyType == "bool") propertyTypes[itemIndex] = "bool";
else if (propertyType == "string") propertyTypes[itemIndex] = "string";
}
itemIndex++;
}
}
itemIndex = 0;
foreach (var property in firstItem.Properties())
{
string propertyName = property.Name;
string propertyType = propertyTypes[itemIndex];
classCode.AppendLine($"\tpublic {propertyType} {propertyName};");
itemIndex++;
}
classCode.AppendLine("}\n");
}
return classCode.ToString();
}
private string GetCSharpType(JTokenType jsonType)
{
switch (jsonType)
{
case JTokenType.Integer:
return "int";
case JTokenType.Float:
return "float";
case JTokenType.Boolean:
return "bool";
default:
return "string";
}
}
private bool CreateGoogleSheetSo()
{
if (Type.GetType("GoogleSheetSO") == null)
return false;
GoogleSheetSo = ScriptableObject.CreateInstance("GoogleSheetSO");
JObject jsonObject = JObject.Parse(_json);
try
{
foreach (var jObject in jsonObject)
{
string className = jObject.Key;
if (!IsExistAvailSheets(className))
continue;
Type classType = Type.GetType(className);
Type listType = typeof(List<>).MakeGenericType(classType);
IList listInst = (IList)Activator.CreateInstance(listType);
var items = (JArray)jObject.Value;
foreach (var item in items)
{
object classInst = Activator.CreateInstance(classType);
foreach (var property in ((JObject)item).Properties())
{
FieldInfo fieldInfo = classType.GetField(property.Name);
object value = Convert.ChangeType(property.Value.ToString(), fieldInfo.FieldType);
fieldInfo.SetValue(classInst, value);
}
listInst.Add(classInst);
}
GoogleSheetSo.GetType().GetField($"{className}List").SetValue(GoogleSheetSo, listInst);
}
}
catch (Exception e)
{
Debug.LogError($"CreateGoogleSheetSO error: {e.Message}");
}
print("CreateGoogleSheetSO");
UnityEditor.AssetDatabase.CreateAsset(GoogleSheetSo, SoPath);
UnityEditor.AssetDatabase.SaveAssets();
return true;
}
private void OnValidate()
{
if (_refeshTrigger)
{
bool isCompleted = CreateGoogleSheetSo();
if (isCompleted)
{
_refeshTrigger = false;
Debug.Log($"Fetch done.");
}
}
}
#endif
private static GoogleSheetManager GetInstance()
{
if (_instance == null)
{
_instance = FindFirstObjectByType<GoogleSheetManager>();
}
return _instance;
}
}