namespace Guru.Editor { using System; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.Networking; using System.Collections.Generic; using System.Linq; public class GuruServiceJsonBuilder: EditorWindow { public enum GuruProjects { Default = 0, BallSortPlus, DOF, D2, DO, TileConnect, FindOut, FindIt, AP, TileBurst, Parking3D, BallSort, BallSort2, WaterSort, WaterSort2, Arrow, APFindDifference, CookingBlitz, WoodNuts, MahjongSolitaire, GoodsSort, } // Tool Version public const string Version = "0.2.0"; private const string K_APP_SETTINGS = "app_settings"; private const string K_ADJUST_SETTINGS = "adjust_settings"; private const string K_FB_SETTINGS = "fb_settings"; private const string K_AD_SETTINGS = "ad_settings"; private const string K_IAP_SETTINGS = "iap_settings"; private const char K_SPLITTER_TAB = '\t'; private const char K_SPLITTER_COMMA = ','; private const string NoSelectionName = "------"; private const string STATE_IDLE = "s_idle"; private const string STATE_LOADING = "s_loading"; private const string STATE_SUCCESS = "s_success"; private static GuruServiceJsonBuilder _instance; private List _projectNames; private int _selectedProjectIndex = 0; #region Link & Keys private const string TSVLink = "https://docs.google.com/spreadsheets/d/e/{0}/pub?gid=0&single=true&output=tsv"; private static readonly Dictionary PublishLinks = new Dictionary() { [GuruProjects.BallSortPlus.ToString()] = "2PACX-1vTa324nQIwNmXNtHC5sZZXYBdpUryKXdb8rFbTAwBa4KVlQ5ZXnErx2IvifkRxD8qKcNupfO1Kv5pJw", [GuruProjects.DOF.ToString()] = "2PACX-1vSDpYZEUrCioCBRSkoZLra5nWk53Ks1f180TD1g2dnKcm-MZtAYvFSXDieAF4xromZCmxIoZuIfGyCJ", [GuruProjects.D2.ToString()] = "2PACX-1vS1V35WTTPx2Jdeu6sVjPbaqFpjVwFu1Rn9tZkvxm8aEHbuRWibxt2pxPkLdDzwmCmrGBtU-PilABJm", [GuruProjects.DO.ToString()] = "2PACX-1vT981NxQldx3qXELplYPoyFSfrMC3QvGciUwRu1sJmsHOu7dB3WSOPDGF4R55cXTq-Q5k_Dq4pmNpF2", [GuruProjects.TileConnect.ToString()] = "2PACX-1vRfSd9UbrLilele8Fw1BafFjlvHG5EMnODfbppPiUaacr7wZ62IvgM65SYSjGEXJmU9g9AczollaFCf", [GuruProjects.FindOut.ToString()] = "2PACX-1vQA3R66yWRHmUn6sneeIUU1qXEiaXv6h9QdzYzVRTEOg-yZf7WCJ6tuvXMWzwcOgGihiSnr9shMX__d", [GuruProjects.FindIt.ToString()] = "2PACX-1vQs75JLsmTv1PESzOP6ANRqVk4zA3Y7PD5OV0yQbA5MM3wmp-hu4qE9gztZy7ETCP2nHJgDziwVpcFE", [GuruProjects.AP.ToString()] = "2PACX-1vSvXTqbTcNCuHZYbKGA-fPKQj8XuixYUB9UXWqTQXz5QLXNurCtmBlziMbUxte_eqsGO0tB2GXe-sEC", [GuruProjects.TileBurst.ToString()] = "2PACX-1vS5wvSZ5eSlK8TmYwVvgScJstaqN8cAB7Uxlq_nsbQwQ4VeD1BkhQuDbRdcO9ncuOCtoKjzSnviHVmc", [GuruProjects.Parking3D.ToString()] = "2PACX-1vQY18hIrYjYNOqQNE12BcPtF4gtFMhBbfDrakBcIVQKoStmfPBY7C4O_Efj1Y8sNbytbKZr-0lPmBml", [GuruProjects.BallSort.ToString()] = "2PACX-1vSPus7415l66-zScY1B1JOgLfSkm0yRPnRDG-BW0JgtnVZJziGDzvtBZr8D9oEZL2x478SdCS2yh0S_", [GuruProjects.BallSort2.ToString()] = "2PACX-1vSZIZYbFuURK_ZMeMHV9ruL0SWBGMPA13er_J_DLRPVw5HBnU8_6c4mvek5UBFo1Ebbk63TMr-rsY6y", [GuruProjects.WaterSort.ToString()] = "2PACX-1vTPxWbpP7KnT-e2xg9Uz-nukbLmqYc8SjwNL9MgycIhInNgmAjOmRnmyH6PWm3-hsEugKTJHKDcoKdI", [GuruProjects.WaterSort2.ToString()] = "2PACX-1vQnZNtE7ZpT6eYagQDt686Be9Jr0tg22sRFg5cGiqFIsCVhWLu6jxDUg7qmyfIrX3iL5awat8FFnN7B", [GuruProjects.Arrow.ToString()] = "2PACX-1vS5w8rNcJycK-VYgum7gkrLAx2Ln_7wykXLBX4EulI8XNGnlPsVlENQ8LupDdtiIu-JlngJaTw5dIzR", [GuruProjects.APFindDifference.ToString()] = "2PACX-1vSmodKOvKjxmiNoYbrXFI6n1XC0aqvlnbYkEGyabLKqS6-C3Yi3nstmC89Hc31cdIzCk1FAl54beW2P", [GuruProjects.CookingBlitz.ToString()] = "2PACX-1vShqtSSpwYQ8CvK-BVr-wQ5ygGeKKLdpaaWysSN_QumoDZYATemayAQIOdnFRzMP69nEwFYSx51oey8", [GuruProjects.WoodNuts.ToString()] = "2PACX-1vQhtRg354eThBpWGCEk5f_2cLVbz1clQXv0n6w4Cyip0Knl6EQ4XwWMlcCec-legZdHU3E0-_cqKipc", [GuruProjects.MahjongSolitaire.ToString()] = "2PACX-1vQpcaj8CpO__K1KGl-mg_940WOOIXBVzi0lmcjYTt1sqBI2PtK37s29McLTGU2I6N3fWM0ZepChedq7", [GuruProjects.GoodsSort.ToString()] = "2PACX-1vQrg6Ss2bNYAI7A250t3_zp10UJtbbQXb8I4LtDJ2Q3uopUG_PUadoJy7T4w0jlf4xcTqot2NWsfp1m", }; #endregion #region Export JSON /// /// 从 TSV 文件进行转化 /// /// /// public static void ConvertFromTSV(string tsv, string savePath = "") { if (string.IsNullOrEmpty(tsv)) { EditorUtility.DisplayDialog("空文件!", $"文件格式错误!\n{tsv}", "OK"); return; } var guru_service = EditorGuruServiceIO.CreateEmpty(); var lines = tsv.Split("\n"); string line = ""; for (int index = 0; index < lines.Length; index++) { line = lines[index]; if (!IsInvalidLine(line)) { //---------------- app_settings ---------------- if (line.StartsWith(K_APP_SETTINGS)) { index++; while (!line.StartsWith(K_ADJUST_SETTINGS)) { line = lines[index]; FillAppSettings(guru_service, line); index++; line = lines[index]; } } //---------------- adjust_settings ---------------- if (line.StartsWith(K_ADJUST_SETTINGS)) { index++; FillAdjustSettings(guru_service, lines, ref index); } //---------------- fb_settings ---------------- if (line.StartsWith(K_FB_SETTINGS)) { index++; FillFacebookSettings(guru_service, lines, ref index); } //---------------- ad_settings ---------------- if (line.StartsWith(K_AD_SETTINGS)) { index++; FillAdSettings(guru_service, lines, ref index); } //---------------- iap_settings ---------------- if (line.StartsWith(K_IAP_SETTINGS)) { index++; FillProducts(guru_service, lines, ref index); } } } guru_service.version = GetFileVersionByDate(); if (string.IsNullOrEmpty(savePath)) { var dir = Path.GetFullPath($"{Application.dataPath}/../output"); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); savePath = $"{dir}/guru-services-{guru_service.app_settings.app_id.ToLower()}.json"; } var arr = savePath.Split('/'); var fileName = arr[arr.Length - 1]; EditorGuruServiceIO.SourceServiceFilePath = savePath; EditorGuruServiceIO.SaveConfig(guru_service, savePath); if (EditorUtility.DisplayDialog("CONVERT SUCCESS!", $"Export Json File\n{fileName}\nto:\n{savePath}", "OK")) { GuruEditorHelper.OpenPath(Directory.GetParent(savePath)?.FullName ?? Application.dataPath); } } public static void ConvertFromTsvFile(string tsvPath, string savePath = "") { if (!File.Exists(tsvPath)) { EditorUtility.DisplayDialog("FILE NOT FOUND!", $"File not exist:\n{tsvPath}", "OK"); return; } var tsvString = File.ReadAllText(tsvPath); if (!string.IsNullOrEmpty(tsvString)) { ConvertFromTSV(tsvString, savePath); } } /// /// AppSettings 填充 /// /// /// private static void FillAppSettings(GuruServicesConfig settings, string line) { // 对于空行和空值直接跳过 if (IsInvalidLine(line)) return; string value = ""; if (settings.app_settings == null) settings.app_settings = new GuruAppSettings(); if (settings.parameters == null) settings.parameters = new GuruParameters(); //------------------- AppSettings ------------------------------- // 拾取值和注入 if (GetValue(line, "app_id", out value)) { settings.app_settings.app_id = value; } else if (GetValue(line, "product_name", out value)) { settings.app_settings.product_name = value; } else if (GetValue(line, "bundle_id", out value)) { settings.app_settings.bundle_id = value; } else if (GetValue(line, "support_email", out value)) { settings.app_settings.support_email = value; } else if (GetValue(line, "privacy_url", out value)) { settings.app_settings.privacy_url = value; } else if (GetValue(line, "terms_url", out value)) { settings.app_settings.terms_url = value; } else if (GetValue(line, "android_store", out value)) { settings.app_settings.android_store = value; } else if (GetValue(line, "ios_store", out value)) { settings.app_settings.ios_store = value; } else if (GetValue(line, "enable_firebase", out value)) { settings.app_settings.enable_firebase = GetBool(value); } else if (GetValue(line, "enable_facebook", out value)) { settings.app_settings.enable_facebook = GetBool(value); } else if (GetValue(line, "enable_adjust", out value)) { settings.app_settings.enable_adjust = GetBool(value); } else if (GetValue(line, "enable_iap", out value)) { settings.app_settings.enable_iap = GetBool(value); } else if (GetValue(line, "custom_keystore", out value)) { settings.app_settings.custom_keystore = GetBool(value); } //------------------- Parameters ------------------------------- else if (GetValue(line, "tch_020", out value)) { settings.parameters.tch_020 = GetDouble(value); } else if (GetValue(line, "using_uuid", out value)) { settings.parameters.using_uuid = GetBool(value); } else if (GetValue(line, "cdn_host", out value)) { settings.parameters.cdn_host = value; } else if (GetValue(line, "enable_errorlog", out value)) { settings.parameters.enable_errorlog = GetBool(value); } else if (GetValue(line, "level_end_success_num", out value)) { settings.parameters.level_end_success_num = GetInt(value); } } /// /// AdjustSettings 填充 /// /// /// private static void FillAdjustSettings(GuruServicesConfig settings, string[] lines, ref int index) { if(settings.adjust_settings == null) settings.adjust_settings = new GuruAdjustSettings(); string[] list = null; string line = lines[index]; bool pass = false; List events = new List(40); while (!lines[index].StartsWith(K_FB_SETTINGS)) { line = lines[index]; if (!IsInvalidLine(line)) { if (line.StartsWith("app_token")) { list = GetStringArray(line, 1, 2); settings.adjust_settings.app_token = list; } else { list = GetStringArray(line, 0, 3); pass = list != null && !string.IsNullOrEmpty(list[0]) && (!string.IsNullOrEmpty(list[1]) || !string.IsNullOrEmpty(list[2])); if( pass) events.Add(string.Join(",", list)); } } index++; } settings.adjust_settings.events = events.ToArray(); index--; } private static long GetFileVersionByDate() { var startDt = new DateTime(1970,1,1,0,0,0); return (long) (DateTime.UtcNow.Ticks - startDt.Ticks) / 10000; } private static void FillFacebookSettings(GuruServicesConfig settings, string[] lines, ref int index) { string value = ""; if(settings.fb_settings == null) settings.fb_settings = new GuruFbSettings(); var line = ""; while (!lines[index].StartsWith(K_AD_SETTINGS)) { line = lines[index]; if (!IsInvalidLine(line)) { // 拾取值和注入 if (GetValue(line, "fb_app_id", out value)) { settings.fb_settings.fb_app_id = value; } else if (GetValue(line, "fb_client_token", out value)) { settings.fb_settings.fb_client_token = value; } } index++; } index--; } private static void FillAdSettings(GuruServicesConfig settings, string[] lines, ref int index) { string value = ""; if(settings.ad_settings == null) settings.ad_settings = new GuruAdSettings(); var line = lines[index]; // SDK Key string[] max_ids_android = new string[3]; string[] max_ids_ios = new string[3]; string[] amazon_ids_android = new string[4]; string[] amazon_ids_ios = new string[4]; string[] pubmatic_ids_android = new string[3]; string[] pubmatic_ids_ios = new string[3]; string[] moloco_ids_android = new string[3]; string[] moloco_ids_ios = new string[3]; string[] tradplus_ids_android = new string[3]; string[] tradplus_ids_ios = new string[3]; //------- 开始记录广告配置; string[] arr; while (!lines[index].StartsWith(K_IAP_SETTINGS)) { line = lines[index]; if (GetValue(line, "sdk_key", out value)) { settings.ad_settings.sdk_key = value; } else if (line.StartsWith("admob_app_id")) { settings.ad_settings.admob_app_id = GetStringArray(line, 1, 2); } else if (line.StartsWith("max_bads")) { arr = GetStringArray(line, 1, 2); max_ids_android[0] = arr[0]; max_ids_ios[0] = arr[1]; } else if (line.StartsWith("max_iads")) { arr = GetStringArray(line, 1, 2); max_ids_android[1] = arr[0]; max_ids_ios[1] = arr[1]; } else if (line.StartsWith("max_rads")) { arr = GetStringArray(line, 1, 2); max_ids_android[2] = arr[0]; max_ids_ios[2] = arr[1]; } else if (line.StartsWith("amazon_app_id")) { arr = GetStringArray(line, 1, 2); amazon_ids_android[0] = arr[0]; amazon_ids_ios[0] = arr[1]; } else if (line.StartsWith("amazon_bads")) { arr = GetStringArray(line, 1, 2); amazon_ids_android[1] = arr[0]; amazon_ids_ios[1] = arr[1]; } else if (line.StartsWith("amazon_iads")) { arr = GetStringArray(line, 1, 2); amazon_ids_android[2] = arr[0]; amazon_ids_ios[2] = arr[1]; } else if (line.StartsWith("amazon_rads")) { arr = GetStringArray(line, 1, 2); amazon_ids_android[3] = arr[0]; amazon_ids_ios[3] = arr[1]; } else if (line.StartsWith("pubmatic_bads")) { arr = GetStringArray(line, 1, 2); pubmatic_ids_android[0] = arr[0]; pubmatic_ids_ios[0] = arr[1]; } else if (line.StartsWith("pubmatic_iads")) { arr = GetStringArray(line, 1, 2); pubmatic_ids_android[1] = arr[0]; pubmatic_ids_ios[1] = arr[1]; } else if (line.StartsWith("pubmatic_rads")) { arr = GetStringArray(line, 1, 2); pubmatic_ids_android[2] = arr[0]; pubmatic_ids_ios[2] = arr[1]; } else if (line.StartsWith("moloco_bads")) { arr = GetStringArray(line, 1, 2); moloco_ids_android[0] = arr[0]; moloco_ids_ios[0] = arr[1]; } else if (line.StartsWith("moloco_iads")) { arr = GetStringArray(line, 1, 2); moloco_ids_android[1] = arr[0]; moloco_ids_ios[1] = arr[1]; } else if (line.StartsWith("moloco_rads")) { arr = GetStringArray(line, 1, 2); moloco_ids_android[2] = arr[0]; moloco_ids_ios[2] = arr[1]; } else if (line.StartsWith("tradplus_bads")) { arr = GetStringArray(line, 1, 2); tradplus_ids_android[0] = arr[0]; tradplus_ids_ios[0] = arr[1]; } else if (line.StartsWith("tradplus_iads")) { arr = GetStringArray(line, 1, 2); tradplus_ids_android[1] = arr[0]; tradplus_ids_ios[1] = arr[1]; } else if (line.StartsWith("tradplus_rads")) { arr = GetStringArray(line, 1, 2); tradplus_ids_android[2] = arr[0]; tradplus_ids_ios[2] = arr[1]; } index++; } //-------- Fill all data ----------- settings.ad_settings.max_ids_android = max_ids_android; settings.ad_settings.max_ids_ios = max_ids_ios; settings.ad_settings.amazon_ids_android = amazon_ids_android; settings.ad_settings.amazon_ids_ios = amazon_ids_ios; settings.ad_settings.pubmatic_ids_android = pubmatic_ids_android; settings.ad_settings.pubmatic_ids_ios = pubmatic_ids_ios; settings.ad_settings.moloco_ids_android = moloco_ids_android; settings.ad_settings.moloco_ids_ios = moloco_ids_ios; settings.ad_settings.tradplus_ids_android = tradplus_ids_android; settings.ad_settings.tradplus_ids_ios = tradplus_ids_ios; index--; } private static void FillProducts(GuruServicesConfig settings, string[] lines, ref int index) { string line = ""; List iaps = new List(30); string[] arr; while (index < lines.Length) { line = lines[index]; if(IsInvalidLine(line)) continue; arr = GetStringArray(line, 0, 7); if(string.IsNullOrEmpty(arr[5])) arr[5] = "Store"; if(string.IsNullOrEmpty(arr[6])) arr[6] = "0"; iaps.Add(string.Join("," , arr)); index++; } settings.products = iaps.ToArray(); index--; } #endregion #region Utils private static bool GetBool(string value) { return value.ToLower() == "true" || value == "1"; } private static int GetInt(string value) { int val = 0; int.TryParse(value, out val); return val; } private static double GetDouble(string value) { double val = 0; double.TryParse(value, out val); return val; } private static bool IsInvalidLine(string line) { return string.IsNullOrEmpty(line) || line.StartsWith(K_SPLITTER_TAB.ToString()); } private static bool GetValue(string line, string key, out string value) { value = ""; // default if (line.StartsWith(key)) { value = line.Split(K_SPLITTER_TAB)[1]; if (string.IsNullOrEmpty(value)) value = "empty"; return true; } return false; } private static string[] GetStringArray(string line, int startIndex = 0, int length = 0, char spliter = K_SPLITTER_TAB) { if (IsInvalidLine(line)) return null; var raw = line.Split(spliter); if (length == 0) length = raw.Length; var a = new List(length); for (int i = startIndex; i < length + startIndex; i++) { if (i < raw.Length) { a.Add(raw[i]); } else { a.Add(""); } } return a.ToArray(); } #endregion #region Menu Items #if GURU_SDK_DEV [MenuItem("Tools/Export Guru Service", false, 0 )] #endif private static void ExportJsonFile() { string saveDir = Path.GetFullPath($"{Application.dataPath}/../output"); string saveFile = Path.Combine(saveDir,$"guru-services____{DateTime.Now:yyyy-M-d-HH-mm}.json"); if(!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir); string searchPath = "~/Downloads/"; string tsvPath = EditorUtility.OpenFilePanel("Load Guru Service TSV", searchPath, ".tsv"); if (!string.IsNullOrEmpty(tsvPath)) { ConvertFromTsvFile(tsvPath, saveFile); } } [MenuItem("Guru/Guru-Service Json Builder...", false, 0)] private static void OpenWindow() { if(_instance != null ) _instance.Close(); _instance = GetWindow(); _instance.Show(); } #endregion #region Window private void Awake() { Debug.Log($"------- Awake -------"); this.titleContent = new GUIContent("Json Builder"); _projectNames = new List(20); string[] names = Enum.GetNames(typeof(GuruProjects)); string name = ""; for(int i = 0; i < names.Length; i++) { name = names[i]; if (name == "Default") { _projectNames.Insert(0, NoSelectionName); } else { _projectNames.Add(name); } } _selectedProjectIndex = 0; _state = STATE_IDLE; } private void OnEnable() { // Debug.Log($"------- OnEnable -------"); } private void OnGUI() { GUI_Title(); switch (_state) { case STATE_IDLE: GUI_Projects(); break; case STATE_LOADING: GUI_Loading(); break; } } #endregion #region GUI private string _state = ""; private void GUI_Title() { var s = new GUIStyle("box") { fontSize = 20, fontStyle = FontStyle.Bold, stretchWidth = true, alignment = TextAnchor.MiddleCenter, padding = new RectOffset(4, 4, 4, 4), }; GUILayout.Label("Guru-Service Json Builder", s, GUILayout.Height(60)); s.fontSize = 14; GUILayout.Label($"Version: {Version}", s); GUILayout.Space(10); } private void GUI_Loading() { var s = new GUIStyle("box") { fontSize = 16, alignment = TextAnchor.MiddleCenter, stretchWidth = true, padding = new RectOffset(4, 4, 4, 4), }; GUILayout.Label("Loading...", s); GUILayout.Space(10); } private void GUI_Projects() { _selectedProjectIndex = EditorGUILayout.Popup("选择生成项目", _selectedProjectIndex, _projectNames.ToArray()); GUILayout.Space(5); if (GUILayout.Button("生成 guru-services.json", GUILayout.Height(40))) { var id = _projectNames[_selectedProjectIndex]; if (id == NoSelectionName) { ShowDialog("选择错误", "请选择一个存在的项目"); } else { DownloadTsvAndBuild(_selectedProjectIndex, (success, txt) => { if (success) { ConvertFromTSV(txt); } else { ShowDialog("网络错误", txt); } _state = STATE_IDLE; }); _state = STATE_LOADING; } } } private void ShowDialog(string title, string content, string okName = "OK", Action onOKCallback = null, string cancelName = "", Action onCancelCallback = null) { if (EditorUtility.DisplayDialog(title, content, okName, cancelName)) { onOKCallback?.Invoke(); } else { onCancelCallback?.Invoke(); } } #endregion #region Networking private static string GetProjectTSVUrl(GuruProjects pid) { return GetProjectTSVUrl(pid.ToString()); } private static string GetProjectTSVUrl(string pid) { if (PublishLinks.TryGetValue(pid, out var id)) { string url = string.Format(TSVLink, id); return url; } return ""; } private void DownloadTsvAndBuild(int projIndex, Action loadCompleted = null) { var pid = _projectNames[projIndex]; var id = GetProjectTSVUrl(pid); string title, msg; if(string.IsNullOrEmpty(id)) { title = "参数错误"; msg = $"项目 {pid} 不正确, 请重新选择..."; ShowDialog(title, msg); Debug.LogError($"{title}\n{msg}"); return; } var www = UnityEngine.Networking.UnityWebRequest.Get(id); www.SendWebRequest().completed += ap => { if (www.result == UnityWebRequest.Result.Success) { Debug.Log($"--- Load Success ---"); // Debug.Log(www.downloadHandler.text); loadCompleted?.Invoke(true, www.downloadHandler.text); } else { msg = $"Result {www.result}: {www.responseCode}\n\r{www.error}"; Debug.LogError(msg); loadCompleted?.Invoke(false, msg); } }; } #endregion #region TEST #if GURU_SDK_DEV // [MenuItem("Tools/Test/Fetch Config File", false, 1)] #endif private static void Test_FetchConfigFile() { var pid = GuruProjects.FindOut; var url = GetProjectTSVUrl(pid); if(string.IsNullOrEmpty(url)) { Debug.LogError($"Wrong ProjectId: {pid}"); return; } var www = UnityEngine.Networking.UnityWebRequest.Get(url); www.SendWebRequest().completed += ap => { if (www.result == UnityWebRequest.Result.Success) { Debug.Log($"--- Load Success ---"); Debug.Log(www.downloadHandler.text); } else { Debug.LogError($"Loading Failed: {www.error} : {www.result} : {www.responseCode}"); } }; } #endregion } }