317 lines
9.1 KiB
C#
317 lines
9.1 KiB
C#
|
using System;
|
||
|
using System.Diagnostics;
|
||
|
using System.IO;
|
||
|
using System.Reflection;
|
||
|
using System.Text.RegularExpressions;
|
||
|
using game;
|
||
|
using UnityEditor;
|
||
|
using UnityEditor.Compilation;
|
||
|
using UnityEngine;
|
||
|
using Debug = UnityEngine.Debug;
|
||
|
using Object = UnityEngine.Object;
|
||
|
#if UNITY_IOS
|
||
|
using UnityEditor.iOS.Xcode;
|
||
|
#endif
|
||
|
|
||
|
public class EditorDevUtils
|
||
|
{
|
||
|
private static Process StartGamectlTask(string task)
|
||
|
{
|
||
|
var p = new Process();
|
||
|
|
||
|
//NOTE: Invoking command in shell on OSX to setup proper paths
|
||
|
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
||
|
{
|
||
|
PatchPATH();
|
||
|
p.StartInfo.FileName = "bash";
|
||
|
p.StartInfo.Arguments =
|
||
|
"-c 'source ~/.bash_profile; php " + Application.dataPath + "/../gamectl " + task + "'";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p.StartInfo.FileName = "php";
|
||
|
p.StartInfo.Arguments = Application.dataPath + "/../gamectl " + task;
|
||
|
}
|
||
|
|
||
|
p.StartInfo.UseShellExecute = false;
|
||
|
p.StartInfo.RedirectStandardOutput = true;
|
||
|
p.StartInfo.RedirectStandardError = true;
|
||
|
p.Start();
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
private static void PatchPATH()
|
||
|
{
|
||
|
string path = Environment.GetEnvironmentVariable("PATH");
|
||
|
Environment.SetEnvironmentVariable("PATH", "/usr/local/bin:" + path);
|
||
|
}
|
||
|
|
||
|
public static void ProcessAdbTask(string task)
|
||
|
{
|
||
|
var p = new Process();
|
||
|
|
||
|
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
||
|
{
|
||
|
p.StartInfo.FileName = "bash";
|
||
|
p.StartInfo.Arguments = "-c 'source ~/.bash_profile; adb " + task + "'";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p.StartInfo.FileName = "adb";
|
||
|
p.StartInfo.Arguments = task;
|
||
|
}
|
||
|
|
||
|
p.StartInfo.UseShellExecute = false;
|
||
|
p.StartInfo.RedirectStandardOutput = true;
|
||
|
p.StartInfo.RedirectStandardError = true;
|
||
|
p.Start();
|
||
|
|
||
|
p.WaitForExit();
|
||
|
Log.Debug($"Done running 'adb {task}', exit code: {p.ExitCode}");
|
||
|
if (p.ExitCode != 0)
|
||
|
ShowProcError(p);
|
||
|
}
|
||
|
|
||
|
public static void ShowProcError(Process p)
|
||
|
{
|
||
|
string err_msg = p.StandardError.ReadToEnd();
|
||
|
string out_msg = p.StandardOutput.ReadToEnd();
|
||
|
//NOTE: showing error first so that it's visible immediately in the console log
|
||
|
Debug.LogError(err_msg + "\n\n" + out_msg + err_msg);
|
||
|
}
|
||
|
|
||
|
public static void ProcessGamectlTask(string task, Action<Process, StreamReader> cb)
|
||
|
{
|
||
|
ClearCompilationErrors();
|
||
|
|
||
|
var p = StartGamectlTask(task);
|
||
|
|
||
|
var s = p.StandardOutput;
|
||
|
|
||
|
p.WaitForExit();
|
||
|
|
||
|
cb(p, s);
|
||
|
|
||
|
if (p.ExitCode == 0)
|
||
|
return;
|
||
|
|
||
|
string err_msg = p.StandardError.ReadToEnd();
|
||
|
string out_msg = p.StandardOutput.ReadToEnd();
|
||
|
|
||
|
string file;
|
||
|
int line;
|
||
|
if (TryGuessErrorFileAndLine(err_msg, out file, out line))
|
||
|
EmulateCompilationError(err_msg, file, line);
|
||
|
else
|
||
|
Debug.LogError(err_msg + "\n\n" + out_msg + err_msg);
|
||
|
}
|
||
|
|
||
|
private static bool TryGuessErrorFileAndLine(string err_msg, out string file, out int line)
|
||
|
{
|
||
|
file = "";
|
||
|
line = -1;
|
||
|
|
||
|
var m = Regex.Match(err_msg, "File '([^']+)'");
|
||
|
if (!m.Success)
|
||
|
return false;
|
||
|
|
||
|
line = 0;
|
||
|
file = m.Groups[1].ToString();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private static void EmulateCompilationError(string err_msg, string file, int line)
|
||
|
{
|
||
|
var m = typeof(CompilationPipeline).GetMethod("LogEditorCompilationError",
|
||
|
BindingFlags.Static | BindingFlags.NonPublic);
|
||
|
|
||
|
string rel_file = Path.GetFullPath(file)
|
||
|
.Replace(Path.GetFullPath(Application.dataPath) + Path.DirectorySeparatorChar,
|
||
|
$"Assets{Path.DirectorySeparatorChar}");
|
||
|
var asset = AssetDatabase.LoadAssetAtPath<Object>(rel_file);
|
||
|
|
||
|
if (asset == null)
|
||
|
return;
|
||
|
|
||
|
m.Invoke(null, new object[] { rel_file + "(1,1): error: " + err_msg, asset.GetInstanceID() });
|
||
|
}
|
||
|
|
||
|
private static void ClearCompilationErrors()
|
||
|
{
|
||
|
var m = typeof(CompilationPipeline).GetMethod("ClearEditorCompilationErrors",
|
||
|
BindingFlags.Static | BindingFlags.NonPublic);
|
||
|
m?.Invoke(null, null);
|
||
|
}
|
||
|
|
||
|
public static bool ProcessGamectlTask(string task)
|
||
|
{
|
||
|
var success = false;
|
||
|
|
||
|
ProcessGamectlTask(task, (process, reader) => { success = process.ExitCode == 0; });
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
public static bool IsBatchMode()
|
||
|
{
|
||
|
string cli_opts = Environment.CommandLine;
|
||
|
return cli_opts.Contains("-batchmode");
|
||
|
}
|
||
|
|
||
|
public static string GameRoot()
|
||
|
{
|
||
|
char sep = Path.DirectorySeparatorChar;
|
||
|
return Path.GetFullPath(Application.dataPath + $"{sep}..{sep}..{sep}");
|
||
|
}
|
||
|
|
||
|
public static void DrawHorizontalSeparator(Color color, float padding = 4)
|
||
|
{
|
||
|
const float thickness = 1;
|
||
|
const float padding_y = 3;
|
||
|
|
||
|
GUILayout.Space(padding_y);
|
||
|
|
||
|
var r = EditorGUILayout.GetControlRect(GUILayout.Height(thickness));
|
||
|
r.height = thickness;
|
||
|
r.x += padding;
|
||
|
r.width -= 2 * padding;
|
||
|
EditorGUI.DrawRect(r, color);
|
||
|
|
||
|
GUILayout.Space(padding_y);
|
||
|
}
|
||
|
|
||
|
public static string GetFilePath(ConfBase cb)
|
||
|
{
|
||
|
return AliasToFilePath(cb.strid);
|
||
|
}
|
||
|
|
||
|
public static string AliasToFilePath(string conf_alias)
|
||
|
{
|
||
|
return conf_alias.Replace("@", GameRoot() + "configs/") + ".conf.js";
|
||
|
}
|
||
|
|
||
|
public static string FilePathToAlias(string file_path)
|
||
|
{
|
||
|
return file_path.Replace(GameRoot() + "configs/", "@").Replace(".conf.js", "");
|
||
|
}
|
||
|
|
||
|
public static void WriteConfByAlias(string alias, string content)
|
||
|
{
|
||
|
string file_path = AliasToFilePath(alias);
|
||
|
Directory.CreateDirectory(Path.GetDirectoryName(file_path));
|
||
|
WriteConfigPreserveHeader(file_path, content);
|
||
|
}
|
||
|
|
||
|
public static string ExtractJsonHeader(string json, out uint id, out string strid)
|
||
|
{
|
||
|
uint tmp_id = 0;
|
||
|
var tmp_strid = "";
|
||
|
|
||
|
json = Regex.Replace(json, "^\\{\\s*/\\*\\s*proto_id\\s*=\\s*(\\d+)\\s*;\\s*alias\\s*=\\s*(\\S+)\\s*\\*/",
|
||
|
delegate(Match m)
|
||
|
{
|
||
|
if (m.Success)
|
||
|
{
|
||
|
tmp_id = uint.Parse("" + m.Groups[1]);
|
||
|
tmp_strid = "" + m.Groups[2];
|
||
|
return "{\n";
|
||
|
}
|
||
|
|
||
|
return m.Value;
|
||
|
}
|
||
|
);
|
||
|
|
||
|
id = tmp_id;
|
||
|
strid = tmp_strid;
|
||
|
|
||
|
return json;
|
||
|
}
|
||
|
|
||
|
public static void RemoveConfigHeader(string path)
|
||
|
{
|
||
|
if (!File.Exists(path))
|
||
|
return;
|
||
|
|
||
|
string[] lines = File.ReadAllLines(path);
|
||
|
|
||
|
int idx = lines[0].IndexOf('/');
|
||
|
if (idx != -1)
|
||
|
lines[0] = lines[0].Remove(idx);
|
||
|
|
||
|
File.WriteAllLines(path, lines);
|
||
|
}
|
||
|
|
||
|
public static void WriteConfigPreserveHeader(string path, string json)
|
||
|
{
|
||
|
if (File.Exists(path))
|
||
|
{
|
||
|
string prev_json = File.ReadAllText(path);
|
||
|
//NOTE: let's preserve header info
|
||
|
uint id = 0;
|
||
|
var strid = "";
|
||
|
ExtractJsonHeader(prev_json, out id, out strid);
|
||
|
|
||
|
if (id != 0)
|
||
|
{
|
||
|
int first_bracket = json.IndexOf("{");
|
||
|
|
||
|
Error.Assert(first_bracket != -1);
|
||
|
|
||
|
json =
|
||
|
"{ /* proto_id = " + id + " ; alias = " + strid + " */" +
|
||
|
json.Substring(first_bracket + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
File.WriteAllText(path, json);
|
||
|
}
|
||
|
|
||
|
//public static string ReplaceIdToStrid<T>(string json, string field_name, Configs configs) where T : ConfBase
|
||
|
//{
|
||
|
// for(int i = 0; i < configs.Count; ++i)
|
||
|
// {
|
||
|
// uint id = configs[i].id;
|
||
|
// var conf = configs.Find<T>(id);
|
||
|
// if(conf != null && conf is T)
|
||
|
// json = json.Replace($"\"{field_name}\": {conf.id}", $"\"{field_name}\": \"{conf.strid}\"");
|
||
|
// }
|
||
|
// return json;
|
||
|
//}
|
||
|
|
||
|
public static string ReplaceI18N(string json, string field_name)
|
||
|
{
|
||
|
json = json.Replace($"\"_{field_name}\": ", $"\"{field_name}\": ");
|
||
|
json = Regex.Replace(json, $",(\n|\r|\r\n) .*\"__{field_name}\": \\[\\]", "", RegexOptions.Multiline);
|
||
|
json = json.Replace(@"\r", "");
|
||
|
return json;
|
||
|
}
|
||
|
|
||
|
//public static string ReplaceStridToId<T>(string json, string field_name, Configs configs) where T : ConfBase
|
||
|
//{
|
||
|
// for(int i = 0; i < configs.Count; ++i)
|
||
|
// {
|
||
|
// uint id = configs[i].id;
|
||
|
// var conf = configs.Find<T>(id);
|
||
|
// if(conf != null && conf is T)
|
||
|
// json = json.Replace($"\"{field_name}\": \"{conf.strid}\"", $"\"{field_name}\": {conf.id}");
|
||
|
// }
|
||
|
// return json;
|
||
|
//}
|
||
|
|
||
|
#if UNITY_IOS
|
||
|
public static void IOSAddLocalFile(string buildPath, PBXProject proj, string targetName, string fileDir,
|
||
|
string fileName)
|
||
|
{
|
||
|
string filePath = Path.Combine(fileDir, fileName);
|
||
|
File.Copy(filePath, Path.Combine(buildPath, fileName));
|
||
|
proj.AddFileToBuild(targetName, proj.AddFile(fileName, fileName));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
public static string ToShortName(string file, string path)
|
||
|
{
|
||
|
return file.Replace(path + "/", "");
|
||
|
}
|
||
|
}
|