using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using Google.Apis.Auth.OAuth2; using Google.Apis.Services; using Google.Apis.Sheets.v4; using Google.Apis.Util.Store; using UnityEngine; #region Editor #if UNITY_EDITOR using UnityEditor; public static partial class DialogueDatabase { public static string EDITOR_DialogueFile => Path.Combine(Application.dataPath, "Data", "Dialogue", DIALOGUE_FILENAME); // TODO: neither of these should be in the repository! ideally, move them behind secure // gitlab variables before there is too much data at risk private const string SPREADSHEET_ID = "1H42653xGbKLxbsDzz7CSR0iaNMz1hU42LX5hWiFtn-Y"; private const string API_KEY = "AIzaSyD-tKzeKpAj5sFnbXbj2k4H2RQxipw8B8s"; private const string SERVICE_ACCOUNT_EMAIL = "gitlab-runner-dialogue-import@revival-304616.iam.gserviceaccount.com"; private const string CERTIFICATE_NAME = "revival-304616-7f150d13c408.p12"; private static string _workFolder => Path.Combine(Application.dataPath, "Data", "Dialogue"); private static string _certificateFile => Path.Combine(_workFolder, CERTIFICATE_NAME); // makes a web request via google // returns 0 on success public static int Import() { var certificate = new X509Certificate2(_certificateFile, "notasecret", X509KeyStorageFlags.Exportable); var credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(SERVICE_ACCOUNT_EMAIL) { Scopes = new[] {SheetsService.Scope.SpreadsheetsReadonly} }.FromCertificate(certificate)); var service = new SheetsService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "Kernel Panic - Revival Dialogue Importer", ApiKey = API_KEY }); // first two columns, ignore first row var range = "Sheet1!A2:B"; var request = service.Spreadsheets.Values.Get(SPREADSHEET_ID, range); var response = request.Execute(); var values = response.Values; if (values == null || values.Count == 0) { Debug.LogError("no data"); return 1; } Debug.Log($":: importing dialogue"); var sb = new StringBuilder(); var lines = 0; foreach (var row in values) { sb.AppendLine($"{row[0]}{SPLIT_CHAR}{row[1]}"); lines++; } Debug.Log($":: imported {lines} lines of dialogue\n{sb}"); File.WriteAllText(EDITOR_DialogueFile, sb.ToString()); if (!File.Exists(EDITOR_DialogueFile)) { Debug.LogError($":: import failed: file doesn't exist: {EDITOR_DialogueFile}"); return 1; } return 0; } [MenuItem("ktyl/Import Dialogue")] private static void ImportDialogueMenuItem() { Import(); } } #endif #endregion public static partial class DialogueDatabase { private const char SPLIT_CHAR = '|'; public const string DIALOGUE_FILENAME = "dialogue.data"; public static string DialogueFile { get { #if UNITY_EDITOR // ensure file exists and run import if necessary if (!File.Exists(EDITOR_DialogueFile) && Import() != 0) return null; return EDITOR_DialogueFile; #else return Path.Combine(Application.dataPath, DIALOGUE_FILENAME); #endif } } static DialogueDatabase() { Debug.Log($":: building dialogue database from file at {DialogueFile}"); int count = 0; foreach (var line in File.ReadLines(DialogueFile)) { var parts = line.Split(SPLIT_CHAR); var key = parts[0]; var text = parts[1]; if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(text)) { Debug.LogWarning($"bad line in csv: {line}"); continue; } _dict[key] = text; count++; } Debug.Log($":: imported {count} lines of dialogue"); } private static readonly Dictionary _dict = new Dictionary(); public static string[] Keys => _dict.Keys.ToArray(); public static string ReadDialogue(string key) { return _dict.ContainsKey(key) ? _dict[key] : $"MISSING[{key}]"; } }