stack
This commit is contained in:
@@ -36,6 +36,11 @@ public class ComponentBase : MonoBehaviour,IPointerClickHandler
|
||||
|
||||
}
|
||||
|
||||
public virtual void getpath()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void FitCollider()
|
||||
{
|
||||
var renderers = GetComponentsInChildren<Renderer>();
|
||||
@@ -58,5 +63,6 @@ public class ComponentBase : MonoBehaviour,IPointerClickHandler
|
||||
{
|
||||
onComponentClicked?.Invoke(componentType, data);
|
||||
Debug.Log(componentType);
|
||||
getpath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Simulator.Data
|
||||
{
|
||||
public class SourceComponent : ComponentBase
|
||||
{
|
||||
SourceDataClass sourceData = new SourceDataClass();
|
||||
public SourceDataClass sourceData = new SourceDataClass();
|
||||
SourceModelData sourceModelData = new SourceModelData();
|
||||
public List<GameObject> entitySocket;
|
||||
List<Entity> possessEntities = new List<Entity>();
|
||||
@@ -155,5 +155,12 @@ namespace Simulator.Data
|
||||
// 텍스트 갱신
|
||||
currentBubble.SetMessage(text);
|
||||
}
|
||||
|
||||
public override void getpath()
|
||||
{
|
||||
PathIndexer.Build(ComponentsManager.Instance.logicDetailData);
|
||||
var path = PathIndexer.GetNodePath(sourceData);
|
||||
Debug.Log(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Assets/Scripts/Simulator/PatchIndexer.cs
Normal file
168
Assets/Scripts/Simulator/PatchIndexer.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using UnityEngine;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
public static class PathIndexer
|
||||
{
|
||||
private sealed class NodeMeta
|
||||
{
|
||||
public object Parent;
|
||||
public string Segment; // 예: "production_system", "asrs[0]"
|
||||
}
|
||||
|
||||
// 모델 클래스를 수정하지 않기 위해 외부 메타 테이블 사용
|
||||
private static readonly ConditionalWeakTable<object, NodeMeta> _meta = new();
|
||||
|
||||
/// <summary>
|
||||
/// 루트부터 전체 그래프를 순회하며 (Parent, Segment) 메타를 구축합니다.
|
||||
/// 로직 데이터 로딩/디시리얼라이즈 직후 1회 호출을 권장합니다.
|
||||
/// </summary>
|
||||
public static void Build(object root)
|
||||
{
|
||||
if (root == null) throw new ArgumentNullException(nameof(root));
|
||||
var visited = new HashSet<object>(ReferenceEqualityComparer.Instance);
|
||||
Visit(root, parent: null, segment: null, visited);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 객체 노드의 경로(노드까지)를 반환합니다. 예: "production_system.asrs[0]"
|
||||
/// </summary>
|
||||
public static string GetNodePath(object node, string rootPrefix = null)
|
||||
{
|
||||
if (node == null) throw new ArgumentNullException(nameof(node));
|
||||
|
||||
var segments = new List<string>();
|
||||
var cur = node;
|
||||
|
||||
while (cur != null && _meta.TryGetValue(cur, out var m))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(m.Segment))
|
||||
segments.Add(m.Segment);
|
||||
|
||||
cur = m.Parent;
|
||||
}
|
||||
|
||||
segments.Reverse();
|
||||
|
||||
var path = string.Join(".", segments.Where(s => !string.IsNullOrWhiteSpace(s)));
|
||||
if (!string.IsNullOrWhiteSpace(rootPrefix))
|
||||
{
|
||||
// 서버가 production_system[0] 같은 추가 프리픽스를 요구하면 여기서 붙이세요.
|
||||
// 예: rootPrefix = "production_system[0]" 또는 "data" 등
|
||||
path = string.IsNullOrWhiteSpace(path) ? rootPrefix : $"{rootPrefix}.{path}";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (변경된 객체) + (변경된 프로퍼티명)으로 최종 patch path를 만듭니다.
|
||||
/// 예: GetPropertyPath(asrs0, "name") => "production_system.asrs[0].name"
|
||||
/// </summary>
|
||||
public static string GetPropertyPath(object node, string propertyName, string rootPrefix = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(propertyName))
|
||||
throw new ArgumentException("propertyName is required.", nameof(propertyName));
|
||||
|
||||
var basePath = GetNodePath(node, rootPrefix);
|
||||
return string.IsNullOrWhiteSpace(basePath) ? propertyName : $"{basePath}.{propertyName}";
|
||||
}
|
||||
|
||||
// ----------------- 내부 구현 -----------------
|
||||
|
||||
private static void Visit(object node, object parent, string segment, HashSet<object> visited)
|
||||
{
|
||||
if (node == null) return;
|
||||
|
||||
var t = node.GetType();
|
||||
if (IsTerminalType(t)) return;
|
||||
if (!visited.Add(node)) return;
|
||||
|
||||
AttachMeta(node, parent, segment);
|
||||
|
||||
// 1) 프로퍼티 순회(기존)
|
||||
var props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(p => p.CanRead && p.GetIndexParameters().Length == 0);
|
||||
|
||||
foreach (var p in props)
|
||||
{
|
||||
object child;
|
||||
try { child = p.GetValue(node); }
|
||||
catch { continue; }
|
||||
|
||||
VisitChild(node, p.Name, child, visited);
|
||||
}
|
||||
|
||||
// 2) 필드 순회(추가)
|
||||
var fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(f => !f.IsStatic);
|
||||
|
||||
foreach (var f in fields)
|
||||
{
|
||||
object child;
|
||||
try { child = f.GetValue(node); }
|
||||
catch { continue; }
|
||||
|
||||
VisitChild(node, f.Name, child, visited);
|
||||
}
|
||||
}
|
||||
|
||||
private static void VisitChild(object parentNode, string memberName, object child, HashSet<object> visited)
|
||||
{
|
||||
if (child == null) return;
|
||||
|
||||
var ct = child.GetType();
|
||||
if (IsTerminalType(ct)) return;
|
||||
|
||||
// IList면 원소에 memberName[i] 형태로 세그먼트 부여
|
||||
if (child is IList list && child is not string)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var elem = list[i];
|
||||
if (elem == null) continue;
|
||||
|
||||
var et = elem.GetType();
|
||||
if (IsTerminalType(et)) continue;
|
||||
|
||||
Visit(elem, parent: parentNode, segment: $"{memberName}[{i}]", visited);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 일반 객체
|
||||
Visit(child, parent: parentNode, segment: memberName, visited);
|
||||
}
|
||||
|
||||
private static void AttachMeta(object node, object parent, string segment)
|
||||
{
|
||||
// ConditionalWeakTable은 같은 키 add가 안 되므로 교체
|
||||
_meta.Remove(node);
|
||||
_meta.Add(node, new NodeMeta { Parent = parent, Segment = segment });
|
||||
}
|
||||
|
||||
private static bool IsTerminalType(Type t)
|
||||
{
|
||||
if (t.IsPrimitive || t.IsEnum) return true;
|
||||
if (t == typeof(string) || t == typeof(decimal)) return true;
|
||||
if (t == typeof(DateTime) || t == typeof(DateTimeOffset) || t == typeof(Guid) || t == typeof(TimeSpan)) return true;
|
||||
|
||||
// Nullable<T> 처리
|
||||
var nt = Nullable.GetUnderlyingType(t);
|
||||
if (nt != null) return IsTerminalType(nt);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private sealed class ReferenceEqualityComparer : IEqualityComparer<object>
|
||||
{
|
||||
public static readonly ReferenceEqualityComparer Instance = new();
|
||||
public new bool Equals(object x, object y) => ReferenceEquals(x, y);
|
||||
public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Simulator/PatchIndexer.cs.meta
Normal file
2
Assets/Scripts/Simulator/PatchIndexer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3198bec84bd9c4a459fde3c9ec54fae4
|
||||
@@ -1,4 +1,5 @@
|
||||
using Simulator.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UVC.UI.Window.PropertyWindow;
|
||||
@@ -46,5 +47,17 @@ public class SimulatorProperty : MonoBehaviour
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
PropertyWindow.Instance.PropertyValueChanged += (sender, e) =>
|
||||
{
|
||||
PathIndexer.Build(ComponentsManager.Instance.logicDetailData);
|
||||
var path = PathIndexer.GetNodePath(source);
|
||||
Debug.Log($"{path}.{e.PropertyId}, type:{e.PropertyType}, newValue: {e.NewValue}");
|
||||
Patch updateData = new Patch();
|
||||
updateData.value = e.NewValue;
|
||||
UpdateValueStack.AddPatch($"{path}.{e.PropertyId}", e.NewValue);
|
||||
UpdateValueStack.Save();
|
||||
//CursorManager.Instance.SetCursor(CursorType.HandPoint);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
47
Assets/Scripts/Simulator/UpdateValueStack.cs
Normal file
47
Assets/Scripts/Simulator/UpdateValueStack.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework.Constraints;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[Serializable]
|
||||
public class Patches
|
||||
{
|
||||
public List<Patch> patches=new List<Patch>();
|
||||
}
|
||||
[Serializable]
|
||||
public class Patch
|
||||
{
|
||||
public string path;
|
||||
public object value;
|
||||
}
|
||||
public class UpdateValueStack : MonoBehaviour
|
||||
{
|
||||
static Dictionary<string,object> patches=new Dictionary<string,object>();
|
||||
public static void AddPatch(string path, object value)
|
||||
{
|
||||
if (patches.ContainsKey(path))
|
||||
{
|
||||
patches[path] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
patches.Add(path, value);
|
||||
}
|
||||
}
|
||||
public static void Save()
|
||||
{
|
||||
Patches change = new Patches();
|
||||
foreach (var patch in patches)
|
||||
{
|
||||
Patch pa = new Patch();
|
||||
pa.path = patch.Key;
|
||||
pa.value = patch.Value;
|
||||
change.patches.Add(pa);
|
||||
}
|
||||
var data=JsonConvert.SerializeObject(change);
|
||||
Debug.Log(data);
|
||||
|
||||
patches.Clear();
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Simulator/UpdateValueStack.cs.meta
Normal file
2
Assets/Scripts/Simulator/UpdateValueStack.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6d985cb8a1e605499e1d47036464cb1
|
||||
Reference in New Issue
Block a user