323 lines
10 KiB (Stored with Git LFS)
C#
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;
|
|
}
|
|
} |