Files
2025-11-30 08:35:03 +02:00

296 lines
8.1 KiB
C#

using System;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using System.Reflection;
using Needle.Engine.Utils;
using UnityEditor;
#endif
namespace Needle.Engine
{
[Flags]
internal enum TracingScenario
{
Any = 0,
Types = 1 << 0,
NetworkRequests = 1 << 1,
ComponentGeneration = 1 << 2,
ColorSpaces = 1 << 3,
FileExport = 1 << 4,
EditorSync = 1 << 5,
Samples = 1 << 6,
/// <summary>
/// E.g. Tools package or BuildPipeline package logs
/// </summary>
Tools = 1 << 7,
AuthenticationState = 1 << 8,
IPC = 1 << 9,
WebProject = 1 << 10,
}
internal static class OptionOverrides
{
public static bool OverrideNodeJSVersion
{
#if UNITY_EDITOR
get => SessionState.GetBool("NEEDLE_ENGINE_OVERRIDE_NODEJS", false);
set => SessionState.SetBool("NEEDLE_ENGINE_OVERRIDE_NODEJS", value);
#else
get => false;
#endif
}
public static string NodeJSVersion
{
#if UNITY_EDITOR
get => SessionState.GetString("NEEDLE_ENGINE_OVERRIDE_VALUE_NODEJS", "v14").Trim();
set => SessionState.SetString("NEEDLE_ENGINE_OVERRIDE_VALUE_NODEJS", value);
#else
get => "";
#endif
}
public static bool OverrideLicenseType
{
#if UNITY_EDITOR
get => SessionState.GetBool("NEEDLE_ENGINE_OVERRIDE_LICENSE_TYPE", false);
set => SessionState.SetBool("NEEDLE_ENGINE_OVERRIDE_LICENSE_TYPE", value);
#else
get => false;
#endif
}
public static string LicenseType
{
#if UNITY_EDITOR
get => SessionState.GetString("NEEDLE_ENGINE_OVERRIDE_VALUE_LICENSE_TYPE", "free").Trim();
set
{
SessionState.SetString("NEEDLE_ENGINE_OVERRIDE_VALUE_LICENSE_TYPE", value);
#pragma warning disable CS4014
LicenseCheck.QueryLicense(false);
#pragma warning restore CS4014
}
#else
get => "";
#endif
}
}
internal static class NeedleDebug
{
internal static bool DeveloperMode
{
#if UNITY_EDITOR
get {
if (!UnityThreads.IsMainThread()) return false;
return SessionState.GetBool("NEEDLE_ENGINE_DEVELOPER_MODE", false);
}
set => SessionState.SetBool("NEEDLE_ENGINE_DEVELOPER_MODE", value);
#else
get => false;
set { }
#endif
}
private static string LogFilePath => Application.dataPath + "/../Logs/Needle.log";
private static string LogFilePathPrev => Application.dataPath + "/../Logs/Needle-prev.log";
private static StreamWriter LogFileStream { get; set; }
#if UNITY_EDITOR
[InitializeOnLoadMethod]
private static void Init()
{
if (!SessionState.GetBool("NEEDLE_ENGINE_LOG_TO_FILE", false))
{
SessionState.SetBool("NEEDLE_ENGINE_LOG_TO_FILE", true);
if (File.Exists(LogFilePath))
{
File.Copy(LogFilePath, LogFilePathPrev, true);
File.Delete(LogFilePath);
}
}
}
#endif
private static void WriteLogToFile(TracingScenario scenario, object obj, Object context = null)
{
#if UNITY_EDITOR
try
{
switch (scenario)
{
case TracingScenario.Any:
case TracingScenario.Samples:
case TracingScenario.Tools:
case TracingScenario.AuthenticationState:
case TracingScenario.IPC:
case TracingScenario.WebProject:
LogFileStream ??= new StreamWriter(LogFilePath, true);
var dt = DateTime.Now;
var str = dt.ToString("yyyy-MM-dd HH:mm:ss") + " - " + scenario.ToString();
if(context) str += " (" + context.name + ")";
str += ": " + obj;
LogFileStream.WriteLine(str);
LogFileStream.Flush();
break;
}
}
catch(Exception e)
{
if(DeveloperMode) Debug.LogError("Failed to write log to file: " + e);
}
#endif
}
#if HAS_HIDE_IN_CALLSTACKS
[HideInCallstack]
#endif
public static void Log(TracingScenario scenario, object obj, Object context = null)
{
WriteLogToFile(scenario, obj, context);
if (IsEnabled(scenario)) Debug.Log(obj, context);
}
#if HAS_HIDE_IN_CALLSTACKS
[HideInCallstack]
#endif
public static void Log(TracingScenario scenario, Func<object> obj, Object context = null)
{
WriteLogToFile(scenario, obj, context);
if (IsEnabled(scenario)) Debug.Log(obj.Invoke(), context);
}
#if HAS_HIDE_IN_CALLSTACKS
[HideInCallstack]
#endif
public static async void LogAsync(TracingScenario scenario, Func<Task<object>> obj, Object context = null)
{
WriteLogToFile(scenario, obj, context);
if (IsEnabled(scenario)) Debug.Log(await obj.Invoke(), context);
}
#if HAS_HIDE_IN_CALLSTACKS
[HideInCallstack]
#endif
public static void LogWarning(TracingScenario scenario, object obj, Object context = null)
{
WriteLogToFile(scenario, obj, context);
if (IsEnabled(scenario)) Debug.LogWarning(obj, context);
}
#if HAS_HIDE_IN_CALLSTACKS
[HideInCallstack]
#endif
public static void LogError(TracingScenario scenario, object obj, Object context = null)
{
WriteLogToFile(scenario, obj, context);
if (IsEnabled(scenario)) Debug.LogError(obj, context);
}
#if HAS_HIDE_IN_CALLSTACKS
[HideInCallstack]
#endif
public static void LogException(TracingScenario scenario, Exception e, Object context = null)
{
WriteLogToFile(scenario, e, context);
if (IsEnabled(scenario)) Debug.LogException(e, context);
}
public static bool IsEnabled(TracingScenario scenario)
{
if (scenario == TracingScenario.Any) return true;
return currentTracingScenarios.HasFlag(scenario);
}
// We cache it to not access EditorPrefs every time we need it
private static int cachedTracingScenario = -1;
private static TracingScenario currentTracingScenarios
{
get
{
if (cachedTracingScenario != -1)
return (TracingScenario)cachedTracingScenario;
#if UNITY_EDITOR
if(UnityThreads.IsMainThread())
cachedTracingScenario = EditorPrefs.GetInt("NeedleTracingScenario", 0);
else cachedTracingScenario = 0;
#else
cachedTracingScenario = 0;
#endif
return (TracingScenario)cachedTracingScenario;
}
set
{
if (cachedTracingScenario == (int)value) return;
cachedTracingScenario = (int)value;
#if UNITY_EDITOR
EditorPrefs.SetInt("NeedleTracingScenario", (int)value);
#endif
}
}
#if UNITY_EDITOR
private class TracingScenarioEditor : EditorWindow
{
[MenuItem("Needle Engine/Internal/Tracing Scenarios", false, 50)]
private static void Open()
{
var window = GetWindow<TracingScenarioEditor>();
if (window == null) window = CreateInstance<TracingScenarioEditor>();
window.Show();
}
private void OnEnable()
{
titleContent = new GUIContent("Tracing Scenarios");
}
private readonly string[] tracingScenarioTypes = Enum.GetNames(typeof(TracingScenario));
private readonly Array tracingScenarioValues = Enum.GetValues(typeof(TracingScenario));
private PropertyInfo[] optionOverrideFields;
private void OnGUI()
{
var newValue = 0;
GUILayout.Label("Logging", EditorStyles.boldLabel);
for (var i = 0; i < tracingScenarioTypes.Length; i++)
{
var val = (TracingScenario)tracingScenarioValues.GetValue(i);
if ((int)val == 0) continue;
var isFlagEnabled =
currentTracingScenarios.HasFlag((TracingScenario)tracingScenarioValues.GetValue(i));
var enabled = EditorGUILayout.ToggleLeft(ObjectNames.NicifyVariableName(tracingScenarioValues.GetValue(i).ToString()),
isFlagEnabled);
newValue |= enabled ? (int)tracingScenarioValues.GetValue(i) : 0;
}
currentTracingScenarios = (TracingScenario)newValue;
EditorGUILayout.Space();
if (DeveloperMode)
{
GUILayout.Label("Overrides", EditorStyles.boldLabel);
optionOverrideFields ??= typeof(OptionOverrides).GetProperties(BindingFlags.Static | BindingFlags.Public);
foreach (var field in optionOverrideFields)
{
if (field.PropertyType == typeof(bool))
{
var val = (bool)field.GetValue(null);
var enabled = EditorGUILayout.ToggleLeft(ObjectNames.NicifyVariableName(field.Name), val);
field.SetValue(null, enabled);
}
else if (field.PropertyType == typeof(string))
{
var val = (string)field.GetValue(null);
var enabled = EditorGUILayout.DelayedTextField(ObjectNames.NicifyVariableName(field.Name), val);
field.SetValue(null, enabled);
}
}
}
}
}
#endif
}
}