Files
2026-06-01 17:19:55 +02:00

3605 lines
97 KiB
C#

using System;
using System.Collections.Generic;
using BlockSpace.Voxels;
using BlockSpace.Worlds;
using UnityEngine;
using UnityEngine.XR;
[DisallowMultipleComponent]
public sealed class NodeGraphManager : MonoBehaviour
{
private sealed class CircuitUndoStep
{
public readonly List<ICircuitMutation> undo = new List<ICircuitMutation>();
public readonly List<ICircuitMutation> redo = new List<ICircuitMutation>();
}
private interface ICircuitMutation
{
void Apply(NodeGraphManager manager, bool broadcast);
}
private sealed class UpsertNodeMutation : ICircuitMutation
{
private readonly WorldGroupsCodec.WorldNodeRecord record;
public UpsertNodeMutation(WorldGroupsCodec.WorldNodeRecord record)
{
this.record = record;
}
public void Apply(NodeGraphManager manager, bool broadcast)
{
manager.ApplyNodeRecord(record, broadcast);
}
}
private sealed class DeleteNodeMutation : ICircuitMutation
{
private readonly int nodeId;
public DeleteNodeMutation(int nodeId)
{
this.nodeId = nodeId;
}
public void Apply(NodeGraphManager manager, bool broadcast)
{
if (manager.nodesById.TryGetValue(nodeId, out var value) && value != null)
{
manager.DeleteNodeInternal(value, broadcast, recordUndo: false);
}
}
}
private sealed class ConnectWireMutation : ICircuitMutation
{
private readonly WorldGroupsCodec.WorldWireRecord record;
public ConnectWireMutation(WorldGroupsCodec.WorldWireRecord record)
{
this.record = record;
}
public void Apply(NodeGraphManager manager, bool broadcast)
{
manager.ApplyWireRecord(record, broadcast);
}
}
private sealed class DisconnectWireMutation : ICircuitMutation
{
private readonly WorldGroupsCodec.WorldWireRecord record;
public DisconnectWireMutation(WorldGroupsCodec.WorldWireRecord record)
{
this.record = record;
}
public void Apply(NodeGraphManager manager, bool broadcast)
{
Node value;
Node value2;
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId) || WorldGroupsCodec.TryGetGroupSavedWireIndex(record, out groupId))
{
new DisconnectInputMutation(record.inputNodeId, record.inputPortName).Apply(manager, broadcast);
}
else if (manager.nodesById.TryGetValue(record.outputNodeId, out value) && manager.nodesById.TryGetValue(record.inputNodeId, out value2) && value != null && value2 != null)
{
manager.Disconnect(value, record.outputPortName, value2, record.inputPortName, broadcast);
}
}
}
private sealed class DisconnectInputMutation : ICircuitMutation
{
private readonly int inputNodeId;
private readonly string inputPortName;
public DisconnectInputMutation(int nodeId, string portName)
{
inputNodeId = nodeId;
inputPortName = portName;
}
public void Apply(NodeGraphManager manager, bool broadcast)
{
if (!manager.nodesById.TryGetValue(inputNodeId, out var value) || !(value != null) || !value.TryGetPort(inputPortName, PortDirection.Input, out var port))
{
return;
}
bool num = port.SourceGroupId > 0;
manager.DisconnectInputPortInternal(port, broadcast);
if (num)
{
manager.ClearGroupInputSourceInternal(port, clearValue: true, pulsePort: false);
if (broadcast && !manager.suppressNetworkBroadcast)
{
manager.ResolveNetworkSync()?.BroadcastDisconnectInput(inputNodeId, inputPortName);
}
}
}
}
private sealed class DisconnectOutputMutation : ICircuitMutation
{
private readonly int outputNodeId;
private readonly string outputPortName;
public DisconnectOutputMutation(int nodeId, string portName)
{
outputNodeId = nodeId;
outputPortName = portName;
}
public void Apply(NodeGraphManager manager, bool broadcast)
{
if (manager.nodesById.TryGetValue(outputNodeId, out var value) && value != null && value.TryGetPort(outputPortName, PortDirection.Output, out var port))
{
manager.DisconnectOutputPortInternal(port, broadcast);
}
}
}
private sealed class WireVisual
{
public string key;
public int sourceGroupId;
public NodePort output;
public NodePort input;
public PortKind Kind;
public LineRenderer renderer;
public float flashUntil;
}
private sealed class NodeGrabState
{
public Node node;
public Vector3 startPosition;
public Quaternion startRotation;
public float lastPreviewSentAt = float.NegativeInfinity;
}
private sealed class PendingWirePress
{
public NodePort startPort;
public VoxelGroup startGroup;
public Vector3 startWorldPosition;
public float pressedAt;
}
private sealed class ActiveValueEditContext
{
public NodePort port;
public string buffer;
public string originalBuffer;
public float startedAt;
}
private sealed class ActiveWireContext
{
public NodePort startPort;
public VoxelGroup startGroup;
public Vector3 startWorldPosition;
public PortKind Kind;
}
private const string NodePrefabResourcePath = "Node";
private static readonly Color NoneWireColor = NodeWirePalette.None;
private static readonly Color BoolWireColor = NodeWirePalette.Bool;
private static readonly Color NumberWireColor = NodeWirePalette.Number;
private static readonly Color TextWireColor = NodeWirePalette.Text;
private static readonly Color PlayerWireColor = NodeWirePalette.Player;
private static readonly Color GroupWireColor = NodeWirePalette.Group;
private static readonly Color RunWireColor = NodeWirePalette.Run;
private static readonly Color WireFlashColor = new Color(1f, 1f, 0.7f, 1f);
private static readonly Color AimRayColor = new Color(0.55f, 0.9f, 1f, 0.72f);
private static readonly Color PreviewRunColor = new Color(1f, 0.55f, 0.28f, 0.92f);
[SerializeField]
private VoxelGroupManager groupManager;
[SerializeField]
private VRVoxelBrush brush;
[SerializeField]
private Transform trackingOrigin;
[SerializeField]
private Transform leftHandAnchor;
[SerializeField]
private Transform rightHandAnchor;
[SerializeField]
private List<NodeDefinition> preloadedDefinitions = new List<NodeDefinition>();
[SerializeField]
private float portPickRadius = 0.08f;
[SerializeField]
private float nodePickRadius = 0.16f;
[SerializeField]
private float wireRayDistance = 2f;
[SerializeField]
private float wireTriggerThreshold = 0.7f;
[SerializeField]
private float rightGripThreshold = 0.85f;
[SerializeField]
private float quickTapThreshold = 0.16f;
[SerializeField]
private float movePreviewSendInterval = 0.05f;
[SerializeField]
private float nodeMovePositionSnap = 0.05f;
[SerializeField]
private float nodeMoveRotationSnapDegrees = 90f;
[SerializeField]
private float undoChordWindow = 0.12f;
[SerializeField]
private float undoTriggerThreshold = 0.7f;
[SerializeField]
private LayerMask wireRaycastMask = -5;
[SerializeField]
private KeyCode keyboardSelectKey = KeyCode.G;
[SerializeField]
private KeyCode keyboardDuplicateKey = KeyCode.D;
[SerializeField]
private KeyCode keyboardDeleteKey = KeyCode.Delete;
[SerializeField]
private KeyCode keyboardWireKey = KeyCode.Space;
[SerializeField]
private int maxInlineValueChars = 64;
private readonly Dictionary<int, Node> nodesById = new Dictionary<int, Node>();
private readonly Dictionary<string, NodeDefinition> definitionsByName = new Dictionary<string, NodeDefinition>(StringComparer.Ordinal);
private readonly HashSet<Node> selectedNodes = new HashSet<Node>();
private readonly List<Node> selectionBuffer = new List<Node>();
private readonly List<NodeGrabState> grabbedNodes = new List<NodeGrabState>();
private readonly Dictionary<string, WireVisual> wireVisuals = new Dictionary<string, WireVisual>(StringComparer.Ordinal);
private readonly List<string> wireCleanupBuffer = new List<string>();
private readonly List<WorldGroupsCodec.WorldWireRecord> pendingWireRecords = new List<WorldGroupsCodec.WorldWireRecord>();
private readonly List<VoxelGroup> groupPickBuffer = new List<VoxelGroup>();
private readonly Stack<CircuitUndoStep> undoStack = new Stack<CircuitUndoStep>();
private readonly Stack<CircuitUndoStep> redoStack = new Stack<CircuitUndoStep>();
[SerializeField]
private int maxUndoSteps = 64;
private NodeGraphPhotonSync networkSync;
private InputDevice leftHandDevice;
private InputDevice rightHandDevice;
private GameObject nodePrefab;
private Material lineMaterial;
private LineRenderer previewWireRenderer;
private LineRenderer aimRayRenderer;
private NodePort hoveredPort;
private Node hoveredNode;
private VoxelGroup hoveredGroup;
private PendingWirePress pendingWirePress;
private ActiveWireContext activeWire;
private ActiveValueEditContext activeValueEdit;
private bool wireInputPrevious;
private bool selectInputPrevious;
private bool duplicateInputPrevious;
private bool nodeGrabActive;
private bool nodeSelectLatchedThisHold;
private bool editVisibilityApplied = true;
private bool suppressNetworkBroadcast;
private bool suppressUndoRecording;
private bool undoChordActive;
private bool suppressUndoUntilNeutral;
private bool undoGripHeldPrevious;
private bool undoTriggerHeldPrevious;
private float lastUndoGripPressTime = float.NegativeInfinity;
private float lastUndoTriggerPressTime = float.NegativeInfinity;
private int nextNodeId = 1;
private Vector3 grabStartReferencePosition;
private Quaternion grabStartReferenceRotation;
private Vector3 grabStartSelectionCenter;
private Vector3 grabStartSelectionOffset;
public static NodeGraphManager Instance { get; private set; }
public bool HasSelection => selectedNodes.Count > 0;
public bool BlocksRightHandActions => nodeGrabActive;
public bool CanUndo => undoStack.Count > 0;
public bool CanRedo => redoStack.Count > 0;
public event Action<int> PlayerJoined;
public event Action<int> PlayerLeft;
public bool UndoLastCircuitEdit()
{
if (!CanEdit() || undoStack.Count == 0)
{
return false;
}
CircuitUndoStep circuitUndoStep = undoStack.Pop();
redoStack.Push(circuitUndoStep);
bool flag = suppressUndoRecording;
suppressUndoRecording = true;
try
{
for (int i = 0; i < circuitUndoStep.undo.Count; i++)
{
circuitUndoStep.undo[i]?.Apply(this, broadcast: true);
}
}
finally
{
suppressUndoRecording = flag;
}
return true;
}
public bool RedoLastCircuitEdit()
{
if (!CanEdit() || redoStack.Count == 0)
{
return false;
}
CircuitUndoStep circuitUndoStep = redoStack.Pop();
undoStack.Push(circuitUndoStep);
bool flag = suppressUndoRecording;
suppressUndoRecording = true;
try
{
for (int i = 0; i < circuitUndoStep.redo.Count; i++)
{
circuitUndoStep.redo[i]?.Apply(this, broadcast: true);
}
}
finally
{
suppressUndoRecording = flag;
}
return true;
}
private void PushUndoStep(CircuitUndoStep step)
{
if (step == null || suppressUndoRecording)
{
return;
}
undoStack.Push(step);
redoStack.Clear();
int num = Mathf.Clamp(maxUndoSteps, 0, 256);
if (num <= 0)
{
undoStack.Clear();
redoStack.Clear();
}
else if (undoStack.Count > num)
{
CircuitUndoStep[] array = undoStack.ToArray();
undoStack.Clear();
for (int num2 = Mathf.Min(num - 1, array.Length - 1); num2 >= 0; num2--)
{
undoStack.Push(array[num2]);
}
}
}
public int SelectNodesWithinSphere(Vector3 worldCenter, float radius, bool additive)
{
if (!CanEdit())
{
return 0;
}
if (!additive)
{
ClearSelectionInternal(keepSuppression: false);
}
float num = Mathf.Max(0.0001f, radius) * Mathf.Max(0.0001f, radius);
int num2 = 0;
foreach (KeyValuePair<int, Node> item in nodesById)
{
Node value = item.Value;
if (!(value == null) && !((value.transform.position - worldCenter).sqrMagnitude > num))
{
bool num3 = selectedNodes.Contains(value);
AddSelection(value);
if (!num3)
{
num2++;
}
}
}
return num2;
}
public int DeleteNodesWithinSphere(Vector3 worldCenter, float radius, bool broadcast)
{
if (!CanEdit())
{
return 0;
}
float num = Mathf.Max(0.0001f, radius) * Mathf.Max(0.0001f, radius);
selectionBuffer.Clear();
foreach (KeyValuePair<int, Node> item in nodesById)
{
Node value = item.Value;
if (!(value == null) && (value.transform.position - worldCenter).sqrMagnitude <= num)
{
selectionBuffer.Add(value);
}
}
int num2 = 0;
CircuitUndoStep circuitUndoStep = null;
if (!suppressUndoRecording)
{
circuitUndoStep = new CircuitUndoStep();
}
for (int i = 0; i < selectionBuffer.Count; i++)
{
Node node = selectionBuffer[i];
if (node == null || !nodesById.ContainsKey(node.NodeId))
{
continue;
}
if (circuitUndoStep != null)
{
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
List<WorldGroupsCodec.WorldWireRecord> wiresTouchingNode = GetWiresTouchingNode(node.NodeId);
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
for (int j = 0; j < wiresTouchingNode.Count; j++)
{
circuitUndoStep.undo.Add(new ConnectWireMutation(wiresTouchingNode[j]));
}
circuitUndoStep.redo.Add(new DeleteNodeMutation(node.NodeId));
}
DeleteNodeInternal(node, broadcast, recordUndo: false);
num2++;
}
if (circuitUndoStep != null && num2 > 0)
{
PushUndoStep(circuitUndoStep);
}
selectionBuffer.Clear();
return num2;
}
private List<WorldGroupsCodec.WorldWireRecord> GetWiresTouchingNode(int nodeId)
{
List<WorldGroupsCodec.WorldWireRecord> list = BuildWorldWireRecords();
List<WorldGroupsCodec.WorldWireRecord> list2 = new List<WorldGroupsCodec.WorldWireRecord>();
for (int i = 0; i < list.Count; i++)
{
WorldGroupsCodec.WorldWireRecord item = list[i];
if (item.inputNodeId == nodeId || item.outputNodeId == nodeId)
{
list2.Add(item);
}
}
return list2;
}
private static List<WorldGroupsCodec.WorldWireRecord> GetExistingWiresForPort(NodePort port)
{
List<WorldGroupsCodec.WorldWireRecord> list = new List<WorldGroupsCodec.WorldWireRecord>();
if (port == null || port.Owner == null)
{
return list;
}
int nodeId = port.Owner.NodeId;
if (port.Direction == PortDirection.Input)
{
if (port.Kind == PortKind.Run)
{
for (int i = 0; i < port.Connections.Count; i++)
{
NodePort nodePort = port.Connections[i];
if (!(nodePort?.Owner == null))
{
list.Add(new WorldGroupsCodec.WorldWireRecord
{
outputNodeId = nodePort.Owner.NodeId,
outputPortName = nodePort.Name,
inputNodeId = nodeId,
inputPortName = port.Name
});
}
}
return list;
}
if (port.SourceGroupId > 0)
{
list.Add(WorldGroupsCodec.CreateGroupRuntimeWireRecord(port.SourceGroupId, nodeId, port.Name));
}
else if (port.IncomingConnection != null && port.IncomingConnection.Owner != null)
{
list.Add(new WorldGroupsCodec.WorldWireRecord
{
outputNodeId = port.IncomingConnection.Owner.NodeId,
outputPortName = port.IncomingConnection.Name,
inputNodeId = nodeId,
inputPortName = port.Name
});
}
return list;
}
for (int j = 0; j < port.Connections.Count; j++)
{
NodePort nodePort2 = port.Connections[j];
if (!(nodePort2?.Owner == null))
{
list.Add(new WorldGroupsCodec.WorldWireRecord
{
outputNodeId = nodeId,
outputPortName = port.Name,
inputNodeId = nodePort2.Owner.NodeId,
inputPortName = nodePort2.Name
});
}
}
return list;
}
internal void NotifyPlayerJoined(int actorNumber)
{
if (actorNumber > 0)
{
this.PlayerJoined?.Invoke(actorNumber);
}
}
internal void NotifyPlayerLeft(int actorNumber)
{
if (actorNumber > 0)
{
this.PlayerLeft?.Invoke(actorNumber);
}
}
private void Awake()
{
if (Instance != null && Instance != this)
{
Debug.LogError("Multiple NodeGraphManager instances found.", this);
return;
}
Instance = this;
ResolveReferences();
CachePreloadedDefinitions();
CacheLoadedDefinitions();
EnsureLineResources();
}
private void OnEnable()
{
RefreshDevices();
}
private void Start()
{
RefreshSceneNodesFromHierarchy();
}
private void OnDestroy()
{
if (Instance == this)
{
Instance = null;
}
if (lineMaterial != null)
{
UnityEngine.Object.Destroy(lineMaterial);
}
}
private void Update()
{
ResolveReferences();
RefreshDevices();
CleanupDestroyedNodes();
TickNodeBehaviors();
bool flag = CanEdit();
ApplyEditVisibility(flag);
if (!flag)
{
CancelEditingInteractions(clearSelection: true);
return;
}
UpdateHoverTargets();
if (!HandleUndoChord() && !HandleInlineValueEdit())
{
HandleSelectionInput();
HandleDuplicateInput();
HandleDeleteInput();
HandleGrabInput();
HandleWireInput();
}
}
private bool HandleInlineValueEdit()
{
if (UseXRInput())
{
activeValueEdit = null;
return false;
}
if (activeValueEdit != null)
{
if (activeValueEdit.port == null || activeValueEdit.port.Owner == null)
{
activeValueEdit = null;
return false;
}
if (Input.GetKeyDown(KeyCode.Escape))
{
RestoreInlineEditLabel(activeValueEdit.port);
activeValueEdit = null;
return true;
}
string inputString = Input.inputString;
if (!string.IsNullOrEmpty(inputString))
{
for (int i = 0; i < inputString.Length; i++)
{
char c = inputString[i];
switch (c)
{
case '\b':
if (!string.IsNullOrEmpty(activeValueEdit.buffer))
{
activeValueEdit.buffer = activeValueEdit.buffer.Substring(0, activeValueEdit.buffer.Length - 1);
}
continue;
case '\n':
case '\r':
CommitInlineValueEdit();
return true;
}
if (!char.IsControl(c))
{
if (activeValueEdit.buffer == null)
{
activeValueEdit.buffer = string.Empty;
}
if (activeValueEdit.buffer.Length < Mathf.Clamp(maxInlineValueChars, 1, 256))
{
activeValueEdit.buffer += c;
}
}
}
}
UpdateInlineEditLabel(activeValueEdit.port, activeValueEdit.buffer);
return true;
}
if ((!Input.GetKeyDown(KeyCode.Return) && !Input.GetKeyDown(KeyCode.KeypadEnter)) || hoveredPort == null)
{
return false;
}
if (hoveredPort.Direction != PortDirection.Input || hoveredPort.Kind != PortKind.Value)
{
return false;
}
if (hoveredPort.IncomingConnection != null || hoveredPort.SourceGroupId > 0)
{
return false;
}
activeValueEdit = new ActiveValueEditContext
{
port = hoveredPort,
originalBuffer = hoveredPort.Value.AsText(),
buffer = hoveredPort.Value.AsText(),
startedAt = Time.unscaledTime
};
UpdateInlineEditLabel(hoveredPort, activeValueEdit.buffer);
return true;
}
private void UpdateInlineEditLabel(NodePort port, string buffer)
{
if (port != null && !(port.LabelText == null))
{
port.LabelText.text = port.Name + ": " + buffer + "_";
}
}
private void RestoreInlineEditLabel(NodePort port)
{
if (port != null && !(port.Owner == null))
{
port.Owner.PulsePort(port);
}
}
private void CommitInlineValueEdit()
{
if (activeValueEdit == null || activeValueEdit.port == null || activeValueEdit.port.Owner == null)
{
activeValueEdit = null;
return;
}
NodePort port = activeValueEdit.port;
Node owner = port.Owner;
string value = activeValueEdit.buffer ?? string.Empty;
activeValueEdit = null;
RestoreInlineEditLabel(port);
AssignInputValue(owner, port, BSValue.FromText(value), broadcast: true);
}
private bool HandleUndoChord()
{
if (!UseXRInput())
{
undoChordActive = false;
suppressUndoUntilNeutral = false;
undoGripHeldPrevious = false;
undoTriggerHeldPrevious = false;
return false;
}
bool flag = ReadGrabHeld(rightHandDevice);
bool flag2 = ReadFloat(rightHandDevice, CommonUsages.trigger) >= undoTriggerThreshold;
if (!flag && undoGripHeldPrevious)
{
lastUndoGripPressTime = float.NegativeInfinity;
}
if (!flag2 && undoTriggerHeldPrevious)
{
lastUndoTriggerPressTime = float.NegativeInfinity;
}
if (flag && !undoGripHeldPrevious)
{
lastUndoGripPressTime = Time.unscaledTime;
}
if (flag2 && !undoTriggerHeldPrevious)
{
lastUndoTriggerPressTime = Time.unscaledTime;
}
if (suppressUndoUntilNeutral)
{
if (!flag && !flag2)
{
suppressUndoUntilNeutral = false;
undoChordActive = false;
}
undoGripHeldPrevious = flag;
undoTriggerHeldPrevious = flag2;
return suppressUndoUntilNeutral;
}
if (!undoChordActive)
{
float num = Mathf.Max(lastUndoGripPressTime, lastUndoTriggerPressTime);
if (flag && flag2 && num > 0f && Math.Abs(lastUndoGripPressTime - lastUndoTriggerPressTime) <= undoChordWindow && Time.unscaledTime - num <= undoChordWindow)
{
if (nodeGrabActive)
{
EndNodeGrab(broadcastFinal: true);
}
UndoLastCircuitEdit();
undoChordActive = true;
suppressUndoUntilNeutral = true;
}
}
else if (!flag || !flag2)
{
undoChordActive = false;
}
undoGripHeldPrevious = flag;
undoTriggerHeldPrevious = flag2;
return suppressUndoUntilNeutral;
}
private void TickNodeBehaviors()
{
if (nodesById.Count == 0)
{
return;
}
selectionBuffer.Clear();
foreach (KeyValuePair<int, Node> item in nodesById)
{
if (item.Value != null)
{
selectionBuffer.Add(item.Value);
}
}
for (int i = 0; i < selectionBuffer.Count; i++)
{
Node node = selectionBuffer[i];
if (!(node == null))
{
node.TickBehavior();
}
}
}
private void LateUpdate()
{
UpdateWireVisuals();
UpdateTransientLines();
}
public void Configure(VoxelGroupManager manager, VRVoxelBrush voxelBrush, Transform origin, Transform leftAnchor, Transform rightAnchor)
{
if (manager != null)
{
groupManager = manager;
}
if (voxelBrush != null)
{
brush = voxelBrush;
}
if (origin != null)
{
trackingOrigin = origin;
}
if (leftAnchor != null)
{
leftHandAnchor = leftAnchor;
}
if (rightAnchor != null)
{
rightHandAnchor = rightAnchor;
}
}
public void RegisterNetworkSync(NodeGraphPhotonSync sync)
{
networkSync = sync;
}
public void RegisterNode(Node node)
{
if (!(node == null))
{
CacheDefinition(node.Definition);
if (node.NodeId <= 0 || (nodesById.TryGetValue(node.NodeId, out var value) && value != null && value != node))
{
node.SetNodeId(AllocateNodeId());
}
else
{
nextNodeId = Mathf.Max(nextNodeId, node.NodeId + 1);
}
node.AssignGraph(this);
nodesById[node.NodeId] = node;
node.SetVisibleInEditMode(CanEdit());
ResolvePendingWireRecords();
}
}
public List<Node> GetSelectedNodesSnapshot()
{
selectionBuffer.Clear();
foreach (Node selectedNode in selectedNodes)
{
if (selectedNode != null)
{
selectionBuffer.Add(selectedNode);
}
}
return new List<Node>(selectionBuffer);
}
public List<Node> GetAllNodesSnapshot()
{
selectionBuffer.Clear();
foreach (KeyValuePair<int, Node> item in nodesById)
{
if (item.Value != null)
{
selectionBuffer.Add(item.Value);
}
}
return new List<Node>(selectionBuffer);
}
public List<NodeDefinition> GetAllDefinitionsSnapshot()
{
CacheLoadedDefinitions();
List<NodeDefinition> list = new List<NodeDefinition>(definitionsByName.Count);
foreach (KeyValuePair<string, NodeDefinition> item in definitionsByName)
{
if (item.Value != null)
{
list.Add(item.Value);
}
}
return list;
}
public void ClearSelectionForToolUse()
{
ClearSelectionInternal(keepSuppression: false);
}
public void CancelEditingInteractions(bool clearSelection)
{
CancelPendingWirePress();
ClearActiveWire();
EndNodeGrab(broadcastFinal: true);
ClearHoverState();
if (clearSelection)
{
ClearSelectionInternal(keepSuppression: false);
}
}
public bool SelectNode(Node node, bool additive)
{
if (!CanEdit() || node == null || !nodesById.ContainsKey(node.NodeId))
{
return false;
}
if (!additive)
{
ClearSelectionInternal(keepSuppression: false);
}
AddSelection(node);
return true;
}
public bool MoveNode(Node node, Vector3 position, Quaternion rotation, bool broadcast)
{
if (!CanEdit() || node == null || !nodesById.ContainsKey(node.NodeId))
{
return false;
}
node.transform.SetPositionAndRotation(position, rotation);
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastUpsertNode(node);
}
return true;
}
public bool DeleteNode(Node node, bool broadcast)
{
if (!CanEdit() || node == null || !nodesById.ContainsKey(node.NodeId))
{
return false;
}
DeleteNodeInternal(node, broadcast, recordUndo: true);
return true;
}
public Node SpawnNode(NodeDefinition definition, Vector3 position, Quaternion rotation, bool broadcast)
{
if (!CanEdit() || definition == null)
{
return null;
}
Node node = SpawnNodeInternal(definition, position, rotation, 0);
if (node != null && broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastUpsertNode(node);
}
if (node != null && !suppressUndoRecording)
{
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
circuitUndoStep.undo.Add(new DeleteNodeMutation(node.NodeId));
circuitUndoStep.redo.Add(new UpsertNodeMutation(record));
PushUndoStep(circuitUndoStep);
}
return node;
}
public Node SpawnNodeSystem(NodeDefinition definition, Vector3 position, Quaternion rotation, bool broadcast)
{
if (definition == null)
{
return null;
}
Node node = SpawnNodeInternal(definition, position, rotation, 0);
if (node != null && broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastUpsertNode(node);
}
return node;
}
public Node SpawnNode(string definitionName, Vector3 position, Quaternion rotation, bool broadcast)
{
NodeDefinition definition = ResolveDefinition(definitionName);
return SpawnNode(definition, position, rotation, broadcast);
}
public void AssignInputValue(Node node, NodePort inputPort, BSValue value, bool broadcast)
{
if (node == null || inputPort == null || inputPort.Direction != PortDirection.Input || inputPort.Kind != PortKind.Value)
{
return;
}
bool flag = ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
DisconnectInputPortInternal(inputPort, broadcast: false);
node.SetInputValueLocal(inputPort, value, pulsePort: true);
if (broadcast && !suppressNetworkBroadcast)
{
if (flag)
{
ResolveNetworkSync()?.BroadcastDisconnectInput(node.NodeId, inputPort.Name);
}
ResolveNetworkSync()?.BroadcastUpsertNode(node);
}
}
public void ApplyOutputValue(Node node, NodePort outputPort, BSValue value, bool broadcast)
{
if (!(node == null) && outputPort != null && outputPort.Direction == PortDirection.Output && outputPort.Kind == PortKind.Value)
{
value = value.Normalize();
outputPort.Value = value;
node.PulsePort(outputPort);
PropagateOutputValue(outputPort, value, flashWire: true, pulseInputPorts: true);
}
}
public void TriggerOutputRun(Node node, NodePort outputPort, bool broadcast)
{
if (node == null || outputPort == null || outputPort.Direction != PortDirection.Output || outputPort.Kind != PortKind.Run)
{
return;
}
node.PulsePort(outputPort);
for (int i = 0; i < outputPort.Connections.Count; i++)
{
NodePort nodePort = outputPort.Connections[i];
if (nodePort != null && !(nodePort.Owner == null))
{
nodePort.Owner.PulsePort(nodePort);
FlashWire(outputPort, nodePort);
nodePort.Owner.ReceiveRun(nodePort.Name);
}
}
}
public bool Connect(Node outputNode, string outputPortName, Node inputNode, string inputPortName, bool broadcast)
{
if (outputNode == null || inputNode == null)
{
return false;
}
if (!outputNode.TryGetPort(outputPortName, PortDirection.Output, out var port) || !inputNode.TryGetPort(inputPortName, PortDirection.Input, out var port2))
{
return false;
}
return ConnectPorts(port, port2, broadcast);
}
public bool Disconnect(Node outputNode, string outputPortName, Node inputNode, string inputPortName, bool broadcast)
{
if (outputNode == null || inputNode == null)
{
return false;
}
if (!outputNode.TryGetPort(outputPortName, PortDirection.Output, out var port) || !inputNode.TryGetPort(inputPortName, PortDirection.Input, out var port2))
{
return false;
}
return DisconnectPortsInternal(port, port2, broadcast);
}
public void ClearAllNodesForNetworkSync()
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = true;
try
{
CancelEditingInteractions(clearSelection: true);
selectionBuffer.Clear();
foreach (KeyValuePair<int, Node> item in nodesById)
{
if (item.Value != null)
{
selectionBuffer.Add(item.Value);
}
}
for (int i = 0; i < selectionBuffer.Count; i++)
{
DeleteNodeInternal(selectionBuffer[i], broadcast: false, recordUndo: false);
}
nodesById.Clear();
pendingWireRecords.Clear();
nextNodeId = 1;
}
finally
{
suppressNetworkBroadcast = flag;
}
}
public List<WorldGroupsCodec.WorldNodeRecord> BuildWorldNodeRecords(HashSet<int> filterNodeIds = null)
{
List<WorldGroupsCodec.WorldNodeRecord> list = new List<WorldGroupsCodec.WorldNodeRecord>();
foreach (KeyValuePair<int, Node> item in nodesById)
{
Node value = item.Value;
if (!(value == null) && !(value.Definition == null) && (filterNodeIds == null || filterNodeIds.Contains(value.NodeId)))
{
list.Add(BuildNodeRecord(value));
}
}
return list;
}
public WorldGroupsCodec.WorldNodeRecord BuildWorldNodeRecord(Node node)
{
return BuildNodeRecord(node);
}
public WorldGroupsCodec.WorldNodeRecord BuildNetworkNodeRecord(Node node)
{
if (node == null)
{
return default(WorldGroupsCodec.WorldNodeRecord);
}
return new WorldGroupsCodec.WorldNodeRecord
{
nodeId = node.NodeId,
definitionName = ((node.Definition != null) ? node.Definition.name : string.Empty),
position = node.transform.position,
rotation = node.transform.rotation,
inputValues = BuildNetworkInputValueRecords(node.InputPorts),
outputValues = null
};
}
private static WorldGroupsCodec.NodePortValueRecord[] BuildNetworkInputValueRecords(List<NodePort> ports)
{
List<WorldGroupsCodec.NodePortValueRecord> list = new List<WorldGroupsCodec.NodePortValueRecord>();
if (ports == null)
{
return list.ToArray();
}
for (int i = 0; i < ports.Count; i++)
{
NodePort nodePort = ports[i];
if (nodePort != null && nodePort.Kind == PortKind.Value && nodePort.Direction == PortDirection.Input && nodePort.IncomingConnection == null && nodePort.SourceGroupId <= 0)
{
list.Add(new WorldGroupsCodec.NodePortValueRecord
{
portName = nodePort.Name,
value = nodePort.Value.Normalize()
});
}
}
return list.ToArray();
}
public List<WorldGroupsCodec.WorldWireRecord> BuildWorldWireRecords(HashSet<int> filterNodeIds = null, Dictionary<int, int> sourceGroupSaveIndices = null)
{
List<WorldGroupsCodec.WorldWireRecord> list = new List<WorldGroupsCodec.WorldWireRecord>();
foreach (KeyValuePair<int, Node> item in nodesById)
{
Node value = item.Value;
if (value == null)
{
continue;
}
for (int i = 0; i < value.OutputPorts.Count; i++)
{
NodePort nodePort = value.OutputPorts[i];
if (nodePort == null)
{
continue;
}
for (int j = 0; j < nodePort.Connections.Count; j++)
{
NodePort nodePort2 = nodePort.Connections[j];
if (nodePort2 != null && !(nodePort2.Owner == null) && (filterNodeIds == null || (filterNodeIds.Contains(value.NodeId) && filterNodeIds.Contains(nodePort2.Owner.NodeId))))
{
list.Add(new WorldGroupsCodec.WorldWireRecord
{
outputNodeId = value.NodeId,
outputPortName = nodePort.Name,
inputNodeId = nodePort2.Owner.NodeId,
inputPortName = nodePort2.Name
});
}
}
}
for (int k = 0; k < value.InputPorts.Count; k++)
{
NodePort nodePort3 = value.InputPorts[k];
if (nodePort3 == null || nodePort3.Kind != PortKind.Value || nodePort3.Direction != PortDirection.Input || nodePort3.SourceGroupId <= 0 || (filterNodeIds != null && !filterNodeIds.Contains(value.NodeId)))
{
continue;
}
if (sourceGroupSaveIndices != null)
{
if (sourceGroupSaveIndices.TryGetValue(nodePort3.SourceGroupId, out var value2))
{
list.Add(WorldGroupsCodec.CreateGroupSavedWireRecord(value2, value.NodeId, nodePort3.Name));
}
}
else
{
list.Add(WorldGroupsCodec.CreateGroupRuntimeWireRecord(nodePort3.SourceGroupId, value.NodeId, nodePort3.Name));
}
}
}
return list;
}
public void ApplySavedGraph(List<WorldGroupsCodec.WorldNodeRecord> nodeRecords, List<WorldGroupsCodec.WorldWireRecord> wireRecords, bool clearExisting, bool broadcast, Vector3 positionOffset, Dictionary<int, int> savedGroupIndexToRuntimeId = null)
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = suppressNetworkBroadcast || !broadcast;
List<int> list = ((broadcast && clearExisting && !flag) ? new List<int>(nodesById.Keys) : null);
List<WorldGroupsCodec.WorldNodeRecord> list2 = ((broadcast && nodeRecords != null) ? new List<WorldGroupsCodec.WorldNodeRecord>(nodeRecords.Count) : null);
List<WorldGroupsCodec.WorldWireRecord> list3 = ((broadcast && wireRecords != null) ? new List<WorldGroupsCodec.WorldWireRecord>(wireRecords.Count) : null);
try
{
if (clearExisting)
{
ClearAllNodesForNetworkSync();
}
if (nodeRecords != null)
{
for (int i = 0; i < nodeRecords.Count; i++)
{
WorldGroupsCodec.WorldNodeRecord worldNodeRecord = nodeRecords[i];
worldNodeRecord.position += positionOffset;
list2?.Add(worldNodeRecord);
ApplyNodeRecord(worldNodeRecord, broadcast: false);
}
}
if (wireRecords != null)
{
for (int j = 0; j < wireRecords.Count; j++)
{
if (TryResolveSavedGroupWireRecord(wireRecords[j], savedGroupIndexToRuntimeId, out var resolvedRecord))
{
list3?.Add(resolvedRecord);
ApplyWireRecord(resolvedRecord, broadcast: false);
}
}
}
ResolvePendingWireRecords();
if (!broadcast || flag)
{
return;
}
if (list != null)
{
for (int k = 0; k < list.Count; k++)
{
ResolveNetworkSync()?.BroadcastDeleteNode(list[k]);
}
}
if (list2 != null)
{
for (int l = 0; l < list2.Count; l++)
{
if (nodesById.TryGetValue(list2[l].nodeId, out var value) && value != null)
{
ResolveNetworkSync()?.BroadcastUpsertNode(value);
}
}
}
if (list3 != null)
{
for (int m = 0; m < list3.Count; m++)
{
ResolveNetworkSync()?.BroadcastConnectWire(list3[m]);
}
}
}
finally
{
suppressNetworkBroadcast = flag;
}
}
public void ApplyNetworkNodeRecord(WorldGroupsCodec.WorldNodeRecord record)
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = true;
bool flag2 = suppressUndoRecording;
suppressUndoRecording = true;
try
{
ApplyNodeRecord(record, broadcast: false);
}
finally
{
suppressNetworkBroadcast = flag;
suppressUndoRecording = flag2;
}
}
public void ApplyNetworkDelete(int nodeId)
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = true;
bool flag2 = suppressUndoRecording;
suppressUndoRecording = true;
try
{
if (nodesById.TryGetValue(nodeId, out var value))
{
DeleteNodeInternal(value, broadcast: false, recordUndo: false);
}
}
finally
{
suppressNetworkBroadcast = flag;
suppressUndoRecording = flag2;
}
}
public void ApplyNetworkWireRecord(WorldGroupsCodec.WorldWireRecord record)
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = true;
bool flag2 = suppressUndoRecording;
suppressUndoRecording = true;
try
{
ApplyWireRecord(record, broadcast: false);
}
finally
{
suppressNetworkBroadcast = flag;
suppressUndoRecording = flag2;
}
}
public void ApplyNetworkDisconnectInput(int inputNodeId, string inputPortName)
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = true;
bool flag2 = suppressUndoRecording;
suppressUndoRecording = true;
try
{
if (nodesById.TryGetValue(inputNodeId, out var value) && value != null && value.TryGetPort(inputPortName, PortDirection.Input, out var port))
{
bool num = port.SourceGroupId > 0;
DisconnectInputPortInternal(port, broadcast: false);
if (num)
{
ClearGroupInputSourceInternal(port, clearValue: true, pulsePort: false);
}
}
}
finally
{
suppressNetworkBroadcast = flag;
suppressUndoRecording = flag2;
}
}
public void ApplyNetworkDisconnectOutput(int outputNodeId, string outputPortName)
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = true;
bool flag2 = suppressUndoRecording;
suppressUndoRecording = true;
try
{
if (nodesById.TryGetValue(outputNodeId, out var value) && value != null && value.TryGetPort(outputPortName, PortDirection.Output, out var port))
{
DisconnectOutputPortInternal(port, broadcast: false);
}
}
finally
{
suppressNetworkBroadcast = flag;
suppressUndoRecording = flag2;
}
}
public void ApplyNetworkTriggerRun(int outputNodeId, string outputPortName)
{
bool flag = suppressNetworkBroadcast;
suppressNetworkBroadcast = true;
try
{
if (nodesById.TryGetValue(outputNodeId, out var value) && value != null && value.TryGetPort(outputPortName, PortDirection.Output, PortKind.Run, out var port))
{
TriggerOutputRun(value, port, broadcast: false);
}
}
finally
{
suppressNetworkBroadcast = flag;
}
}
private void ResolveReferences()
{
if (groupManager == null)
{
groupManager = ((VoxelGroupManager.Instance != null) ? VoxelGroupManager.Instance : UnityEngine.Object.FindAnyObjectByType<VoxelGroupManager>());
}
if (brush == null)
{
brush = ((VRVoxelBrush.Instance != null) ? VRVoxelBrush.Instance : GetComponent<VRVoxelBrush>());
}
if (trackingOrigin == null && brush != null)
{
trackingOrigin = brush.TrackingOrigin;
}
if (leftHandAnchor == null && brush != null)
{
leftHandAnchor = brush.LeftHandAnchor;
}
if (rightHandAnchor == null && brush != null)
{
rightHandAnchor = brush.RightHandAnchor;
}
if (networkSync == null)
{
networkSync = GetComponent<NodeGraphPhotonSync>();
}
}
private void RefreshDevices()
{
if (!leftHandDevice.isValid)
{
leftHandDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
}
if (!rightHandDevice.isValid)
{
rightHandDevice = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
}
}
private void RefreshSceneNodesFromHierarchy()
{
Node[] array = UnityEngine.Object.FindObjectsByType<Node>(FindObjectsInactive.Include);
Array.Sort(array, CompareNodesByHierarchyPath);
nodesById.Clear();
nextNodeId = 1;
foreach (Node node in array)
{
if (!(node == null))
{
CacheDefinition(node.Definition);
if (node.NodeId <= 0 || nodesById.ContainsKey(node.NodeId))
{
node.SetNodeId(nextNodeId++);
}
else
{
nextNodeId = Mathf.Max(nextNodeId, node.NodeId + 1);
}
node.AssignGraph(this);
node.SetVisibleInEditMode(CanEdit());
nodesById[node.NodeId] = node;
}
}
foreach (Node node2 in array)
{
if (node2 == null)
{
continue;
}
List<NodeSerializedConnection> list = node2.ConsumePendingSerializedConnections();
for (int k = 0; k < list.Count; k++)
{
NodeSerializedConnection nodeSerializedConnection = list[k];
if (nodeSerializedConnection != null && !(nodeSerializedConnection.inputNode == null) && node2.TryGetPort(nodeSerializedConnection.outputPortName, PortDirection.Output, out var port) && nodeSerializedConnection.inputNode.TryGetPort(nodeSerializedConnection.inputPortName, PortDirection.Input, out var port2))
{
ConnectPorts(port, port2, broadcast: false);
}
}
}
}
private void CleanupDestroyedNodes()
{
bool flag = false;
foreach (KeyValuePair<int, Node> item in nodesById)
{
if (item.Value == null)
{
flag = true;
break;
}
}
if (!flag)
{
return;
}
List<int> list = new List<int>();
foreach (KeyValuePair<int, Node> item2 in nodesById)
{
if (item2.Value == null)
{
list.Add(item2.Key);
}
}
for (int i = 0; i < list.Count; i++)
{
nodesById.Remove(list[i]);
}
selectedNodes.RemoveWhere((Node node) => node == null);
grabbedNodes.RemoveAll((NodeGrabState state) => state == null || state.node == null);
}
private void ApplyEditVisibility(bool visible)
{
if (editVisibilityApplied == visible)
{
return;
}
editVisibilityApplied = visible;
foreach (KeyValuePair<int, Node> item in nodesById)
{
if (item.Value != null)
{
item.Value.SetVisibleInEditMode(visible);
}
}
}
private void UpdateHoverTargets()
{
ClearHoverState();
if (TryGetLeftRay(out var ray))
{
hoveredPort = TryPickPort(ray, wireRayDistance, GetExpectedWireTargetKind(), GetExpectedWireTargetDirection());
if (hoveredPort != null)
{
hoveredPort.Owner?.SetPortHighlight(hoveredPort, highlighted: true);
}
if (hoveredPort == null && activeWire == null)
{
hoveredGroup = TryPickGroup(ray);
}
}
if (TryGetRightRay(out var ray2))
{
hoveredNode = TryPickNode(ray2);
}
}
private void ClearHoverState()
{
if (hoveredPort != null && hoveredPort.Owner != null)
{
hoveredPort.Owner.SetPortHighlight(hoveredPort, highlighted: false);
}
hoveredPort = null;
hoveredNode = null;
hoveredGroup = null;
}
private void HandleSelectionInput()
{
bool flag = ((!UseXRInput()) ? (Input.GetMouseButton(2) || Input.GetKey(keyboardSelectKey)) : (ReadBool(rightHandDevice, CommonUsages.primary2DAxisClick) || Input.GetKey(keyboardSelectKey)));
if (flag && !selectInputPrevious)
{
nodeSelectLatchedThisHold = false;
}
if (!flag && selectInputPrevious)
{
nodeSelectLatchedThisHold = false;
}
if (flag && hoveredNode != null)
{
nodeSelectLatchedThisHold = true;
}
if (flag && nodeSelectLatchedThisHold && hoveredNode != null)
{
AddSelection(hoveredNode);
}
selectInputPrevious = flag;
}
private void HandleDuplicateInput()
{
bool flag = UseXRInput() && ReadBool(leftHandDevice, CommonUsages.primaryButton);
bool flag2 = Input.GetKeyDown(keyboardDuplicateKey) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl));
if (!HasSelection)
{
duplicateInputPrevious = flag;
return;
}
if ((flag && !duplicateInputPrevious) || flag2)
{
DuplicateSelectedNodes();
}
duplicateInputPrevious = flag;
}
private void HandleDeleteInput()
{
if (HasSelection && Input.GetKeyDown(keyboardDeleteKey))
{
DeleteSelectedNodes();
}
}
private void HandleGrabInput()
{
bool flag = (UseXRInput() ? ReadGrabHeld(rightHandDevice) : Input.GetMouseButton(1));
if (HasSelection && flag)
{
if (!nodeGrabActive)
{
BeginNodeGrab();
}
UpdateNodeGrab();
}
else if (nodeGrabActive)
{
EndNodeGrab(broadcastFinal: true);
}
}
private void HandleWireInput()
{
bool num = UseXRInput();
bool flag = num && ReadGrabHeld(leftHandDevice);
bool flag2 = (num ? (ReadFloat(leftHandDevice, CommonUsages.trigger) >= wireTriggerThreshold) : Input.GetKey(keyboardWireKey));
bool flag3 = flag2 && !wireInputPrevious;
bool flag4 = !flag2 && wireInputPrevious;
if (flag)
{
wireInputPrevious = flag2;
return;
}
if (activeWire != null && (flag3 || flag4))
{
if (hoveredPort == null || !IsWireTargetCompatible(hoveredPort))
{
ClearActiveWire();
}
else
{
CompleteActiveWire(hoveredPort);
}
wireInputPrevious = flag2;
return;
}
if (pendingWirePress != null)
{
if (!flag2)
{
if (Time.unscaledTime - pendingWirePress.pressedAt <= quickTapThreshold)
{
HandleQuickWireTap(pendingWirePress);
}
else
{
StartActiveWire(pendingWirePress.startPort, pendingWirePress.startGroup, pendingWirePress.startWorldPosition);
}
pendingWirePress = null;
}
else if (Time.unscaledTime - pendingWirePress.pressedAt > quickTapThreshold)
{
StartActiveWire(pendingWirePress.startPort, pendingWirePress.startGroup, pendingWirePress.startWorldPosition);
pendingWirePress = null;
}
}
else if (flag3)
{
if (hoveredPort != null)
{
pendingWirePress = new PendingWirePress
{
startPort = hoveredPort,
startWorldPosition = ((hoveredPort.Owner != null) ? hoveredPort.Owner.GetPortWorldPosition(hoveredPort) : base.transform.position),
pressedAt = Time.unscaledTime
};
}
else if (hoveredGroup != null)
{
pendingWirePress = new PendingWirePress
{
startGroup = hoveredGroup,
startWorldPosition = hoveredGroup.GetWorldBounds().center,
pressedAt = Time.unscaledTime
};
}
}
if (flag4 && activeWire == null)
{
CancelPendingWirePress();
}
wireInputPrevious = flag2;
}
private void BeginNodeGrab()
{
if (!TryGetGrabReferencePose(out grabStartReferencePosition, out grabStartReferenceRotation) || !TryGetSelectionBounds(out var bounds))
{
return;
}
grabbedNodes.Clear();
foreach (Node selectedNode in selectedNodes)
{
if (!(selectedNode == null))
{
grabbedNodes.Add(new NodeGrabState
{
node = selectedNode,
startPosition = selectedNode.transform.position,
startRotation = selectedNode.transform.rotation
});
}
}
if (grabbedNodes.Count != 0)
{
nodeGrabActive = true;
grabStartSelectionCenter = bounds.center;
grabStartSelectionOffset = bounds.center - grabStartReferencePosition;
}
}
private void UpdateNodeGrab()
{
if (!nodeGrabActive || !TryGetGrabReferencePose(out var worldPosition, out var worldRotation))
{
return;
}
Quaternion quaternion = SnapRotation(worldRotation * Quaternion.Inverse(grabStartReferenceRotation), nodeMoveRotationSnapDegrees);
Vector3 vector = SnapVector(worldPosition + grabStartSelectionOffset, nodeMovePositionSnap);
for (int num = grabbedNodes.Count - 1; num >= 0; num--)
{
NodeGrabState nodeGrabState = grabbedNodes[num];
if (nodeGrabState == null || nodeGrabState.node == null || !selectedNodes.Contains(nodeGrabState.node))
{
grabbedNodes.RemoveAt(num);
}
else
{
Vector3 vector2 = nodeGrabState.startPosition - grabStartSelectionCenter;
Vector3 position = vector + quaternion * vector2;
Quaternion rotation = quaternion * nodeGrabState.startRotation;
nodeGrabState.node.transform.SetPositionAndRotation(position, rotation);
nodeGrabState.node.SetSelected(isSelected: true);
if (!suppressNetworkBroadcast && Time.unscaledTime - nodeGrabState.lastPreviewSentAt >= Mathf.Max(0.01f, movePreviewSendInterval))
{
ResolveNetworkSync()?.BroadcastUpsertNode(nodeGrabState.node);
nodeGrabState.lastPreviewSentAt = Time.unscaledTime;
}
}
}
}
private void EndNodeGrab(bool broadcastFinal)
{
if (!nodeGrabActive)
{
grabbedNodes.Clear();
return;
}
nodeGrabActive = false;
if (!suppressUndoRecording && grabbedNodes.Count > 0)
{
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
int num = 0;
for (int i = 0; i < grabbedNodes.Count; i++)
{
NodeGrabState nodeGrabState = grabbedNodes[i];
if (nodeGrabState != null && !(nodeGrabState.node == null) && nodesById.ContainsKey(nodeGrabState.node.NodeId))
{
Vector3 position = nodeGrabState.node.transform.position;
Quaternion rotation = nodeGrabState.node.transform.rotation;
if (!((position - nodeGrabState.startPosition).sqrMagnitude <= 1E-06f) || !(Quaternion.Angle(rotation, nodeGrabState.startRotation) <= 0.01f))
{
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(nodeGrabState.node);
record.position = nodeGrabState.startPosition;
record.rotation = nodeGrabState.startRotation;
WorldGroupsCodec.WorldNodeRecord record2 = BuildNetworkNodeRecord(nodeGrabState.node);
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
circuitUndoStep.redo.Add(new UpsertNodeMutation(record2));
num++;
}
}
}
if (num > 0)
{
PushUndoStep(circuitUndoStep);
}
}
if (broadcastFinal && !suppressNetworkBroadcast)
{
for (int j = 0; j < grabbedNodes.Count; j++)
{
if (grabbedNodes[j] != null && grabbedNodes[j].node != null)
{
ResolveNetworkSync()?.BroadcastUpsertNode(grabbedNodes[j].node);
}
}
}
grabbedNodes.Clear();
}
private void StartActiveWire(NodePort startPort, VoxelGroup startGroup, Vector3 startWorldPosition)
{
PortKind kind = startPort?.Kind ?? PortKind.Value;
activeWire = new ActiveWireContext
{
startPort = startPort,
startGroup = startGroup,
startWorldPosition = startWorldPosition,
Kind = kind
};
}
private void CompleteActiveWire(NodePort targetPort)
{
if (activeWire == null || targetPort == null)
{
ClearActiveWire();
return;
}
if (activeWire.startGroup != null)
{
if (targetPort.Direction == PortDirection.Input && targetPort.Kind == PortKind.Value)
{
List<WorldGroupsCodec.WorldWireRecord> list = (suppressUndoRecording ? null : GetExistingWiresForPort(targetPort));
if (ConnectGroupToInputPort(activeWire.startGroup.id, targetPort, broadcast: true) && !suppressUndoRecording)
{
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
WorldGroupsCodec.WorldWireRecord record = WorldGroupsCodec.CreateGroupRuntimeWireRecord(activeWire.startGroup.id, (targetPort.Owner != null) ? targetPort.Owner.NodeId : 0, targetPort.Name);
circuitUndoStep.undo.Add(new DisconnectWireMutation(record));
if (list != null)
{
for (int i = 0; i < list.Count; i++)
{
circuitUndoStep.undo.Add(new ConnectWireMutation(list[i]));
}
}
circuitUndoStep.redo.Add(new ConnectWireMutation(record));
PushUndoStep(circuitUndoStep);
}
}
ClearActiveWire();
return;
}
if (activeWire.startPort == null)
{
ClearActiveWire();
return;
}
if (activeWire.startPort == targetPort)
{
ClearActiveWire();
return;
}
NodePort startPort = activeWire.startPort;
List<WorldGroupsCodec.WorldWireRecord> list2 = (suppressUndoRecording ? null : GetExistingWiresForPort(startPort));
List<WorldGroupsCodec.WorldWireRecord> list3 = (suppressUndoRecording ? null : GetExistingWiresForPort(targetPort));
if (ConnectPortsDirectional(startPort, targetPort, broadcast: true) && !suppressUndoRecording)
{
NodePort nodePort = ((startPort.Direction == PortDirection.Output) ? startPort : targetPort);
NodePort nodePort2 = ((startPort.Direction == PortDirection.Input) ? startPort : targetPort);
WorldGroupsCodec.WorldWireRecord record2 = new WorldGroupsCodec.WorldWireRecord
{
outputNodeId = ((nodePort.Owner != null) ? nodePort.Owner.NodeId : 0),
outputPortName = nodePort.Name,
inputNodeId = ((nodePort2.Owner != null) ? nodePort2.Owner.NodeId : 0),
inputPortName = nodePort2.Name
};
CircuitUndoStep circuitUndoStep2 = new CircuitUndoStep();
circuitUndoStep2.undo.Add(new DisconnectWireMutation(record2));
if (list2 != null)
{
for (int j = 0; j < list2.Count; j++)
{
circuitUndoStep2.undo.Add(new ConnectWireMutation(list2[j]));
}
}
if (list3 != null)
{
for (int k = 0; k < list3.Count; k++)
{
circuitUndoStep2.undo.Add(new ConnectWireMutation(list3[k]));
}
}
circuitUndoStep2.redo.Add(new ConnectWireMutation(record2));
PushUndoStep(circuitUndoStep2);
}
ClearActiveWire();
}
private void HandleQuickWireTap(PendingWirePress pending)
{
if (pending == null || pending.startGroup != null)
{
return;
}
NodePort startPort = pending.startPort;
if (startPort == null)
{
return;
}
if (startPort.Direction == PortDirection.Input)
{
bool num = startPort.Kind == PortKind.Run && startPort.Connections.Count > 0;
bool flag = startPort.Kind != PortKind.Run && (startPort.IncomingConnection != null || startPort.SourceGroupId > 0);
if (num || flag)
{
List<WorldGroupsCodec.WorldWireRecord> list = (suppressUndoRecording ? null : GetExistingWiresForPort(startPort));
bool num2 = startPort.SourceGroupId > 0;
DisconnectInputPortInternal(startPort, broadcast: true);
if (num2)
{
ClearGroupInputSourceInternal(startPort, clearValue: true, pulsePort: false);
if (!suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastDisconnectInput((startPort.Owner != null) ? startPort.Owner.NodeId : 0, startPort.Name);
}
}
if (!suppressUndoRecording && list != null && list.Count > 0)
{
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
circuitUndoStep.redo.Add(new DisconnectInputMutation((startPort.Owner != null) ? startPort.Owner.NodeId : 0, startPort.Name));
for (int i = 0; i < list.Count; i++)
{
circuitUndoStep.undo.Add(new ConnectWireMutation(list[i]));
}
PushUndoStep(circuitUndoStep);
}
}
else
{
if (startPort.Kind != PortKind.Value || !(startPort.Owner != null))
{
return;
}
if (!suppressUndoRecording)
{
BSValue a = startPort.Value.Normalize();
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(startPort.Owner);
AssignInputValue(startPort.Owner, startPort, BSValue.None, broadcast: true);
WorldGroupsCodec.WorldNodeRecord record2 = BuildNetworkNodeRecord(startPort.Owner);
BSValue b = startPort.Value.Normalize();
if (!BSValueUtility.Equals(a, b))
{
CircuitUndoStep circuitUndoStep2 = new CircuitUndoStep();
circuitUndoStep2.undo.Add(new UpsertNodeMutation(record));
circuitUndoStep2.redo.Add(new UpsertNodeMutation(record2));
PushUndoStep(circuitUndoStep2);
}
}
else
{
AssignInputValue(startPort.Owner, startPort, BSValue.None, broadcast: true);
}
}
return;
}
List<WorldGroupsCodec.WorldWireRecord> list2 = (suppressUndoRecording ? null : GetExistingWiresForPort(startPort));
DisconnectOutputPortInternal(startPort, broadcast: true);
if (!suppressUndoRecording && list2 != null && list2.Count > 0)
{
CircuitUndoStep circuitUndoStep3 = new CircuitUndoStep();
circuitUndoStep3.redo.Add(new DisconnectOutputMutation((startPort.Owner != null) ? startPort.Owner.NodeId : 0, startPort.Name));
for (int j = 0; j < list2.Count; j++)
{
circuitUndoStep3.undo.Add(new ConnectWireMutation(list2[j]));
}
PushUndoStep(circuitUndoStep3);
}
}
private void CancelPendingWirePress()
{
pendingWirePress = null;
}
private void ClearActiveWire()
{
activeWire = null;
}
private void AddSelection(Node node)
{
if (!(node == null) && selectedNodes.Add(node))
{
node.SetSelected(isSelected: true);
}
}
private void ClearSelectionInternal(bool keepSuppression)
{
selectionBuffer.Clear();
foreach (Node selectedNode in selectedNodes)
{
if (selectedNode != null)
{
selectionBuffer.Add(selectedNode);
}
}
for (int i = 0; i < selectionBuffer.Count; i++)
{
selectionBuffer[i].SetSelected(isSelected: false);
}
selectedNodes.Clear();
if (!keepSuppression)
{
grabbedNodes.Clear();
}
}
private void DeleteSelectedNodes()
{
CircuitUndoStep circuitUndoStep = (suppressUndoRecording ? null : new CircuitUndoStep());
selectionBuffer.Clear();
foreach (Node selectedNode in selectedNodes)
{
if (selectedNode != null)
{
selectionBuffer.Add(selectedNode);
}
}
ClearSelectionInternal(keepSuppression: false);
for (int i = 0; i < selectionBuffer.Count; i++)
{
Node node = selectionBuffer[i];
if (node == null)
{
continue;
}
if (circuitUndoStep != null && nodesById.ContainsKey(node.NodeId))
{
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
List<WorldGroupsCodec.WorldWireRecord> wiresTouchingNode = GetWiresTouchingNode(node.NodeId);
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
for (int j = 0; j < wiresTouchingNode.Count; j++)
{
circuitUndoStep.undo.Add(new ConnectWireMutation(wiresTouchingNode[j]));
}
circuitUndoStep.redo.Add(new DeleteNodeMutation(node.NodeId));
}
DeleteNodeInternal(node, broadcast: true, recordUndo: false);
}
if (circuitUndoStep != null && circuitUndoStep.redo.Count > 0)
{
PushUndoStep(circuitUndoStep);
}
}
private void DuplicateSelectedNodes()
{
if (selectedNodes.Count == 0)
{
return;
}
CircuitUndoStep circuitUndoStep = (suppressUndoRecording ? null : new CircuitUndoStep());
Dictionary<int, Node> dictionary = new Dictionary<int, Node>();
List<WorldGroupsCodec.WorldWireRecord> list = BuildWorldWireRecords();
List<Node> selectedNodesSnapshot = GetSelectedNodesSnapshot();
HashSet<int> hashSet = new HashSet<int>();
for (int i = 0; i < selectedNodesSnapshot.Count; i++)
{
if (selectedNodesSnapshot[i] != null)
{
hashSet.Add(selectedNodesSnapshot[i].NodeId);
}
}
ClearSelectionInternal(keepSuppression: false);
Vector3 vector = new Vector3(0.25f, 0.1f, 0.25f);
for (int j = 0; j < selectedNodesSnapshot.Count; j++)
{
Node node = selectedNodesSnapshot[j];
if (node == null || node.Definition == null)
{
continue;
}
Node node2 = SpawnNodeInternal(node.Definition, node.transform.position + vector, node.transform.rotation, 0);
if (!(node2 == null))
{
CopyNodeValues(node, node2);
dictionary[node.NodeId] = node2;
AddSelection(node2);
if (!suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastUpsertNode(node2);
}
}
}
for (int k = 0; k < list.Count; k++)
{
WorldGroupsCodec.WorldWireRecord record = list[k];
Node value2;
Node value3;
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId))
{
if (hashSet.Contains(record.inputNodeId) && dictionary.TryGetValue(record.inputNodeId, out var value))
{
ConnectGroupToInput(groupId, value, record.inputPortName, broadcast: true);
}
}
else if (hashSet.Contains(record.outputNodeId) && hashSet.Contains(record.inputNodeId) && dictionary.TryGetValue(record.outputNodeId, out value2) && dictionary.TryGetValue(record.inputNodeId, out value3))
{
Connect(value2, record.outputPortName, value3, record.inputPortName, broadcast: true);
}
}
if (circuitUndoStep == null || dictionary.Count <= 0)
{
return;
}
HashSet<int> hashSet2 = new HashSet<int>();
foreach (KeyValuePair<int, Node> item2 in dictionary)
{
if (item2.Value != null)
{
hashSet2.Add(item2.Value.NodeId);
}
}
List<WorldGroupsCodec.WorldNodeRecord> list2 = new List<WorldGroupsCodec.WorldNodeRecord>();
foreach (int item3 in hashSet2)
{
if (nodesById.TryGetValue(item3, out var value4) && value4 != null)
{
list2.Add(BuildNetworkNodeRecord(value4));
}
}
List<WorldGroupsCodec.WorldWireRecord> list3 = BuildWorldWireRecords();
List<WorldGroupsCodec.WorldWireRecord> list4 = new List<WorldGroupsCodec.WorldWireRecord>();
for (int l = 0; l < list3.Count; l++)
{
WorldGroupsCodec.WorldWireRecord item = list3[l];
if (hashSet2.Contains(item.inputNodeId) || hashSet2.Contains(item.outputNodeId))
{
list4.Add(item);
}
}
foreach (int item4 in hashSet2)
{
circuitUndoStep.undo.Add(new DeleteNodeMutation(item4));
}
for (int m = 0; m < list2.Count; m++)
{
circuitUndoStep.redo.Add(new UpsertNodeMutation(list2[m]));
}
for (int n = 0; n < list4.Count; n++)
{
circuitUndoStep.redo.Add(new ConnectWireMutation(list4[n]));
}
PushUndoStep(circuitUndoStep);
}
private void CopyNodeValues(Node source, Node target)
{
if (source == null || target == null)
{
return;
}
for (int i = 0; i < source.InputPorts.Count; i++)
{
NodePort nodePort = source.InputPorts[i];
if (nodePort != null && nodePort.Kind == PortKind.Value && target.TryGetPort(nodePort.Name, PortDirection.Input, PortKind.Value, out var port))
{
target.SetInputValueLocal(port, nodePort.Value, pulsePort: false);
}
}
for (int j = 0; j < source.OutputPorts.Count; j++)
{
NodePort nodePort2 = source.OutputPorts[j];
if (nodePort2 != null && nodePort2.Kind == PortKind.Value && target.TryGetPort(nodePort2.Name, PortDirection.Output, PortKind.Value, out var port2))
{
port2.Value = nodePort2.Value.Normalize();
}
}
}
private void DeleteNodeInternal(Node node, bool broadcast, bool recordUndo)
{
if (node == null)
{
return;
}
if (recordUndo && !suppressUndoRecording && nodesById.ContainsKey(node.NodeId))
{
CircuitUndoStep circuitUndoStep = new CircuitUndoStep();
WorldGroupsCodec.WorldNodeRecord record = BuildNetworkNodeRecord(node);
List<WorldGroupsCodec.WorldWireRecord> wiresTouchingNode = GetWiresTouchingNode(node.NodeId);
circuitUndoStep.undo.Add(new UpsertNodeMutation(record));
for (int i = 0; i < wiresTouchingNode.Count; i++)
{
circuitUndoStep.undo.Add(new ConnectWireMutation(wiresTouchingNode[i]));
}
circuitUndoStep.redo.Add(new DeleteNodeMutation(node.NodeId));
PushUndoStep(circuitUndoStep);
}
DisconnectAllForNode(node, broadcast: false);
selectedNodes.Remove(node);
grabbedNodes.RemoveAll((NodeGrabState state) => state == null || state.node == node);
nodesById.Remove(node.NodeId);
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastDeleteNode(node.NodeId);
}
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(node.gameObject);
}
else
{
UnityEngine.Object.DestroyImmediate(node.gameObject);
}
}
private void DisconnectAllForNode(Node node, bool broadcast)
{
if (!(node == null))
{
for (int i = 0; i < node.InputPorts.Count; i++)
{
DisconnectInputPortInternal(node.InputPorts[i], broadcast: false);
ClearGroupInputSourceInternal(node.InputPorts[i], clearValue: false, pulsePort: false);
}
for (int j = 0; j < node.OutputPorts.Count; j++)
{
DisconnectOutputPortInternal(node.OutputPorts[j], broadcast: false);
}
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastUpsertNode(node);
}
}
}
private bool ConnectPortsDirectional(NodePort firstPort, NodePort secondPort, bool broadcast)
{
if (!TryResolveDirectionalPorts(firstPort, secondPort, out var outputPort, out var inputPort))
{
return false;
}
return ConnectPorts(outputPort, inputPort, broadcast);
}
private bool ConnectPorts(NodePort outputPort, NodePort inputPort, bool broadcast)
{
if (outputPort == null || inputPort == null)
{
return false;
}
if (outputPort.Direction != PortDirection.Output || inputPort.Direction != PortDirection.Input || outputPort.Kind != inputPort.Kind)
{
return false;
}
if (outputPort.Kind == PortKind.Run)
{
if (outputPort.Connections.Count > 0 && !outputPort.Connections.Contains(inputPort))
{
DisconnectOutputPortInternal(outputPort, broadcast: false);
}
ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
inputPort.IncomingConnection = null;
if (!outputPort.Connections.Contains(inputPort))
{
outputPort.Connections.Add(inputPort);
}
if (!inputPort.Connections.Contains(outputPort))
{
inputPort.Connections.Add(outputPort);
}
EnsureWireVisual(outputPort, inputPort);
outputPort.Owner?.PulsePort(outputPort);
inputPort.Owner?.PulsePort(inputPort);
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastConnectWire(new WorldGroupsCodec.WorldWireRecord
{
outputNodeId = ((outputPort.Owner != null) ? outputPort.Owner.NodeId : 0),
outputPortName = outputPort.Name,
inputNodeId = ((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0),
inputPortName = inputPort.Name
});
}
return true;
}
if (inputPort.IncomingConnection != null && inputPort.IncomingConnection != outputPort)
{
DisconnectInputPortInternal(inputPort, broadcast: false);
}
ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
if (!outputPort.Connections.Contains(inputPort))
{
outputPort.Connections.Add(inputPort);
}
inputPort.IncomingConnection = outputPort;
EnsureWireVisual(outputPort, inputPort);
outputPort.Owner?.PulsePort(outputPort);
inputPort.Owner?.PulsePort(inputPort);
if (outputPort.Kind == PortKind.Value)
{
inputPort.Owner?.SetInputValueLocal(inputPort, outputPort.Value, pulsePort: true);
FlashWire(outputPort, inputPort);
}
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastConnectWire(new WorldGroupsCodec.WorldWireRecord
{
outputNodeId = ((outputPort.Owner != null) ? outputPort.Owner.NodeId : 0),
outputPortName = outputPort.Name,
inputNodeId = ((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0),
inputPortName = inputPort.Name
});
}
return true;
}
private bool DisconnectPortsInternal(NodePort outputPort, NodePort inputPort, bool broadcast)
{
if (outputPort == null || inputPort == null)
{
return false;
}
bool num = outputPort.Connections.Remove(inputPort);
if (outputPort.Kind == PortKind.Run)
{
inputPort.Connections.Remove(outputPort);
if (inputPort.IncomingConnection == outputPort)
{
inputPort.IncomingConnection = null;
}
}
else if (inputPort.IncomingConnection == outputPort)
{
inputPort.IncomingConnection = null;
}
RemoveWireVisual(outputPort, inputPort);
if (num && broadcast && !suppressNetworkBroadcast)
{
NodeGraphPhotonSync nodeGraphPhotonSync = ResolveNetworkSync();
if ((object)nodeGraphPhotonSync == null)
{
return num;
}
nodeGraphPhotonSync.BroadcastDisconnectInput((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0, inputPort.Name);
}
return num;
}
private void DisconnectInputPortInternal(NodePort inputPort, bool broadcast)
{
if (inputPort == null)
{
return;
}
if (inputPort.Kind == PortKind.Run)
{
if (inputPort.Connections.Count == 0)
{
return;
}
List<NodePort> list = new List<NodePort>(inputPort.Connections);
for (int i = 0; i < list.Count; i++)
{
if (list[i] != null)
{
DisconnectPortsInternal(list[i], inputPort, broadcast: false);
}
}
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastDisconnectInput((inputPort.Owner != null) ? inputPort.Owner.NodeId : 0, inputPort.Name);
}
}
else if (inputPort.IncomingConnection != null)
{
NodePort incomingConnection = inputPort.IncomingConnection;
DisconnectPortsInternal(incomingConnection, inputPort, broadcast);
}
}
private void DisconnectOutputPortInternal(NodePort outputPort, bool broadcast)
{
if (outputPort != null && outputPort.Connections.Count != 0)
{
List<NodePort> list = new List<NodePort>(outputPort.Connections);
for (int i = 0; i < list.Count; i++)
{
DisconnectPortsInternal(outputPort, list[i], broadcast: false);
}
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastDisconnectOutput((outputPort.Owner != null) ? outputPort.Owner.NodeId : 0, outputPort.Name);
}
}
}
private void PropagateOutputValue(NodePort outputPort, BSValue value, bool flashWire, bool pulseInputPorts)
{
for (int i = 0; i < outputPort.Connections.Count; i++)
{
NodePort nodePort = outputPort.Connections[i];
if (nodePort != null && !(nodePort.Owner == null))
{
nodePort.Owner.SetInputValueLocal(nodePort, value, pulseInputPorts);
if (flashWire)
{
FlashWire(outputPort, nodePort);
}
}
}
}
private void ApplyNodeRecord(WorldGroupsCodec.WorldNodeRecord record, bool broadcast)
{
if (string.IsNullOrEmpty(record.definitionName))
{
return;
}
NodeDefinition nodeDefinition = ResolveDefinition(record.definitionName);
if (nodeDefinition == null)
{
Debug.LogWarning("NodeGraphManager could not resolve node definition '" + record.definitionName + "'.");
return;
}
Node value;
Node node = (nodesById.TryGetValue(record.nodeId, out value) ? value : null);
bool flag = node != null && node.Definition != nodeDefinition;
if (node == null)
{
node = SpawnNodeInternal(nodeDefinition, record.position, record.rotation, record.nodeId);
}
else
{
if (flag)
{
DisconnectAllForNode(node, broadcast: false);
node.Definition = nodeDefinition;
node.ReinitializeFromDefinition();
node.AssignGraph(this);
}
node.SetNodeId(record.nodeId);
node.transform.SetPositionAndRotation(record.position, record.rotation);
}
if (node == null)
{
return;
}
List<NodePort> list = new List<NodePort>();
ApplyPortValues(node, record.inputValues, PortDirection.Input, null);
ApplyPortValues(node, record.outputValues, PortDirection.Output, list);
for (int i = 0; i < node.OutputPorts.Count; i++)
{
NodePort nodePort = node.OutputPorts[i];
if (nodePort != null && nodePort.Kind == PortKind.Value)
{
PropagateOutputValue(nodePort, nodePort.Value, flashWire: false, pulseInputPorts: false);
if (list.Contains(nodePort))
{
FlashAllConnections(nodePort);
}
}
}
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastUpsertNode(node);
}
}
private void ApplyPortValues(Node node, WorldGroupsCodec.NodePortValueRecord[] values, PortDirection direction, List<NodePort> changedOutputPorts)
{
if (node == null || values == null)
{
return;
}
for (int i = 0; i < values.Length; i++)
{
WorldGroupsCodec.NodePortValueRecord nodePortValueRecord = values[i];
if (!node.TryGetPort(nodePortValueRecord.portName, direction, PortKind.Value, out var port))
{
continue;
}
if (direction == PortDirection.Input)
{
node.SetInputValueLocal(port, nodePortValueRecord.value, pulsePort: false);
continue;
}
if (!BSValueEquals(port.Value, nodePortValueRecord.value))
{
changedOutputPorts?.Add(port);
}
port.Value = nodePortValueRecord.value.Normalize();
}
}
private void FlashAllConnections(NodePort outputPort)
{
if (outputPort == null)
{
return;
}
for (int i = 0; i < outputPort.Connections.Count; i++)
{
NodePort nodePort = outputPort.Connections[i];
if (nodePort != null)
{
FlashWire(outputPort, nodePort);
}
}
}
private bool ConnectGroupToInput(int groupId, Node inputNode, string inputPortName, bool broadcast)
{
if (groupId <= 0 || inputNode == null || string.IsNullOrEmpty(inputPortName))
{
return false;
}
if (inputNode.TryGetPort(inputPortName, PortDirection.Input, PortKind.Value, out var port))
{
return ConnectGroupToInputPort(groupId, port, broadcast);
}
return false;
}
private bool ConnectGroupToInputPort(int groupId, NodePort inputPort, bool broadcast)
{
if (groupId <= 0 || inputPort == null || inputPort.Owner == null || inputPort.Direction != PortDirection.Input || inputPort.Kind != PortKind.Value)
{
return false;
}
DisconnectInputPortInternal(inputPort, broadcast: false);
ClearGroupInputSourceInternal(inputPort, clearValue: false, pulsePort: false);
inputPort.SourceGroupId = groupId;
EnsureGroupWireVisual(groupId, inputPort);
inputPort.Owner.SetInputValueLocal(inputPort, BSValue.FromGroup(groupId), pulsePort: true);
FlashGroupWire(groupId, inputPort);
if (broadcast && !suppressNetworkBroadcast)
{
ResolveNetworkSync()?.BroadcastConnectWire(WorldGroupsCodec.CreateGroupRuntimeWireRecord(groupId, inputPort.Owner.NodeId, inputPort.Name));
}
return true;
}
private bool ClearGroupInputSourceInternal(NodePort inputPort, bool clearValue, bool pulsePort)
{
if (inputPort == null || inputPort.Direction != PortDirection.Input || inputPort.Kind != PortKind.Value)
{
return false;
}
int sourceGroupId = inputPort.SourceGroupId;
if (sourceGroupId <= 0)
{
return false;
}
inputPort.SourceGroupId = 0;
RemoveGroupWireVisual(sourceGroupId, inputPort);
if (clearValue && inputPort.Owner != null)
{
inputPort.Owner.SetInputValueLocal(inputPort, BSValue.None, pulsePort);
}
return true;
}
private void ApplyWireRecord(WorldGroupsCodec.WorldWireRecord record, bool broadcast)
{
Node value2;
Node value3;
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId))
{
if (!nodesById.TryGetValue(record.inputNodeId, out var value) || value == null)
{
pendingWireRecords.Add(record);
}
else
{
ConnectGroupToInput(groupId, value, record.inputPortName, broadcast);
}
}
else if (!nodesById.TryGetValue(record.outputNodeId, out value2) || !nodesById.TryGetValue(record.inputNodeId, out value3) || value2 == null || value3 == null)
{
pendingWireRecords.Add(record);
}
else
{
Connect(value2, record.outputPortName, value3, record.inputPortName, broadcast);
}
}
private void ResolvePendingWireRecords()
{
if (pendingWireRecords.Count == 0)
{
return;
}
for (int num = pendingWireRecords.Count - 1; num >= 0; num--)
{
WorldGroupsCodec.WorldWireRecord record = pendingWireRecords[num];
Node value2;
Node value3;
if (WorldGroupsCodec.TryGetGroupRuntimeWireSourceId(record, out var groupId))
{
if (nodesById.TryGetValue(record.inputNodeId, out var value) && value != null)
{
ConnectGroupToInput(groupId, value, record.inputPortName, broadcast: false);
pendingWireRecords.RemoveAt(num);
}
}
else if (nodesById.TryGetValue(record.outputNodeId, out value2) && nodesById.TryGetValue(record.inputNodeId, out value3) && value2 != null && value3 != null)
{
Connect(value2, record.outputPortName, value3, record.inputPortName, broadcast: false);
pendingWireRecords.RemoveAt(num);
}
}
}
private static bool TryResolveSavedGroupWireRecord(WorldGroupsCodec.WorldWireRecord record, Dictionary<int, int> savedGroupIndexToRuntimeId, out WorldGroupsCodec.WorldWireRecord resolvedRecord)
{
resolvedRecord = record;
if (!WorldGroupsCodec.TryGetGroupSavedWireIndex(record, out var savedGroupIndex))
{
return true;
}
if (savedGroupIndexToRuntimeId == null || !savedGroupIndexToRuntimeId.TryGetValue(savedGroupIndex, out var value) || value <= 0)
{
return false;
}
resolvedRecord = WorldGroupsCodec.CreateGroupRuntimeWireRecord(value, record.inputNodeId, record.inputPortName);
return true;
}
private WorldGroupsCodec.WorldNodeRecord BuildNodeRecord(Node node)
{
return new WorldGroupsCodec.WorldNodeRecord
{
nodeId = node.NodeId,
definitionName = ((node.Definition != null) ? node.Definition.name : string.Empty),
position = node.transform.position,
rotation = node.transform.rotation,
inputValues = BuildPortValueRecords(node.InputPorts),
outputValues = BuildPortValueRecords(node.OutputPorts)
};
}
private static WorldGroupsCodec.NodePortValueRecord[] BuildPortValueRecords(List<NodePort> ports)
{
List<WorldGroupsCodec.NodePortValueRecord> list = new List<WorldGroupsCodec.NodePortValueRecord>();
if (ports == null)
{
return list.ToArray();
}
for (int i = 0; i < ports.Count; i++)
{
NodePort nodePort = ports[i];
if (nodePort != null && nodePort.Kind == PortKind.Value)
{
list.Add(new WorldGroupsCodec.NodePortValueRecord
{
portName = nodePort.Name,
value = nodePort.Value.Normalize()
});
}
}
return list.ToArray();
}
private Node SpawnNodeInternal(NodeDefinition definition, Vector3 position, Quaternion rotation, int forcedNodeId)
{
if (definition == null)
{
return null;
}
EnsureNodePrefabLoaded();
if (nodePrefab == null)
{
return null;
}
GameObject gameObject = UnityEngine.Object.Instantiate(nodePrefab, position, rotation);
Node component = gameObject.GetComponent<Node>();
if (component == null)
{
UnityEngine.Object.Destroy(gameObject);
return null;
}
component.Definition = definition;
component.ReinitializeFromDefinition();
component.SetNodeId((forcedNodeId > 0) ? forcedNodeId : AllocateNodeId());
component.AssignGraph(this);
component.SetVisibleInEditMode(CanEdit());
CacheDefinition(definition);
nodesById[component.NodeId] = component;
nextNodeId = Mathf.Max(nextNodeId, component.NodeId + 1);
return component;
}
private void EnsureNodePrefabLoaded()
{
if (nodePrefab == null)
{
nodePrefab = Resources.Load<GameObject>("Node");
}
}
private void CacheDefinition(NodeDefinition definition)
{
if (definition != null && !definitionsByName.ContainsKey(definition.name))
{
definitionsByName.Add(definition.name, definition);
}
}
private void CachePreloadedDefinitions()
{
if (preloadedDefinitions != null && preloadedDefinitions.Count != 0)
{
for (int i = 0; i < preloadedDefinitions.Count; i++)
{
CacheDefinition(preloadedDefinitions[i]);
}
}
}
private void CacheLoadedDefinitions()
{
CachePreloadedDefinitions();
NodeDefinitionCatalog nodeDefinitionCatalog = Resources.Load<NodeDefinitionCatalog>("NodeDefinitionCatalog");
if (nodeDefinitionCatalog != null && nodeDefinitionCatalog.Definitions != null)
{
for (int i = 0; i < nodeDefinitionCatalog.Definitions.Count; i++)
{
CacheDefinition(nodeDefinitionCatalog.Definitions[i]);
}
}
NodeDefinition[] array = Resources.FindObjectsOfTypeAll<NodeDefinition>();
for (int j = 0; j < array.Length; j++)
{
CacheDefinition(array[j]);
}
}
private NodeDefinition ResolveDefinition(string definitionName)
{
if (string.IsNullOrEmpty(definitionName))
{
return null;
}
if (definitionsByName.TryGetValue(definitionName, out var value) && value != null)
{
return value;
}
CacheLoadedDefinitions();
if (definitionsByName.TryGetValue(definitionName, out value) && value != null)
{
return value;
}
foreach (KeyValuePair<int, Node> item in nodesById)
{
if (item.Value != null && item.Value.Definition != null && item.Value.Definition.name == definitionName)
{
CacheDefinition(item.Value.Definition);
return item.Value.Definition;
}
}
return null;
}
private int AllocateNodeId()
{
while (nodesById.ContainsKey(nextNodeId))
{
nextNodeId++;
}
return nextNodeId++;
}
private void EnsureLineResources()
{
if (lineMaterial == null)
{
Shader shader = Shader.Find("Sprites/Default");
if (shader != null)
{
lineMaterial = new Material(shader);
}
}
if (previewWireRenderer == null)
{
previewWireRenderer = CreateLineRenderer("Node Preview Wire", 0.018f);
}
if (aimRayRenderer == null)
{
aimRayRenderer = CreateLineRenderer("Node Aim Ray", 0.0085f);
}
}
private LineRenderer CreateLineRenderer(string name, float width)
{
GameObject obj = new GameObject(name);
obj.transform.SetParent(base.transform, worldPositionStays: false);
LineRenderer lineRenderer = obj.AddComponent<LineRenderer>();
lineRenderer.positionCount = 5;
lineRenderer.loop = false;
lineRenderer.useWorldSpace = true;
lineRenderer.textureMode = LineTextureMode.Stretch;
lineRenderer.numCapVertices = 4;
lineRenderer.alignment = LineAlignment.View;
lineRenderer.widthMultiplier = width;
lineRenderer.material = lineMaterial;
lineRenderer.enabled = false;
return lineRenderer;
}
private void EnsureWireVisual(NodePort outputPort, NodePort inputPort)
{
string wireKey = GetWireKey(outputPort, inputPort);
if (!wireVisuals.ContainsKey(wireKey))
{
WireVisual wireVisual = new WireVisual
{
key = wireKey,
output = outputPort,
input = inputPort,
Kind = (outputPort?.Kind ?? PortKind.Value),
renderer = CreateLineRenderer("Wire " + wireKey, 0.015f)
};
wireVisuals.Add(wireKey, wireVisual);
UpdateWireVisual(wireVisual);
}
}
private void EnsureGroupWireVisual(int groupId, NodePort inputPort)
{
if (groupId > 0 && inputPort != null)
{
string groupWireKey = GetGroupWireKey(groupId, inputPort);
if (!wireVisuals.ContainsKey(groupWireKey))
{
WireVisual wireVisual = new WireVisual
{
key = groupWireKey,
sourceGroupId = groupId,
input = inputPort,
Kind = PortKind.Value,
renderer = CreateLineRenderer("Wire " + groupWireKey, 0.015f)
};
wireVisuals.Add(groupWireKey, wireVisual);
UpdateWireVisual(wireVisual);
}
}
}
private void RemoveWireVisual(NodePort outputPort, NodePort inputPort)
{
string wireKey = GetWireKey(outputPort, inputPort);
if (wireVisuals.TryGetValue(wireKey, out var value))
{
DestroyWireVisualRenderer(value);
wireVisuals.Remove(wireKey);
}
}
private void RemoveGroupWireVisual(int groupId, NodePort inputPort)
{
string groupWireKey = GetGroupWireKey(groupId, inputPort);
if (wireVisuals.TryGetValue(groupWireKey, out var value))
{
DestroyWireVisualRenderer(value);
wireVisuals.Remove(groupWireKey);
}
}
private void FlashWire(NodePort outputPort, NodePort inputPort)
{
string wireKey = GetWireKey(outputPort, inputPort);
if (wireVisuals.TryGetValue(wireKey, out var value))
{
value.flashUntil = Time.unscaledTime + 0.18f;
}
}
private void FlashGroupWire(int groupId, NodePort inputPort)
{
string groupWireKey = GetGroupWireKey(groupId, inputPort);
if (wireVisuals.TryGetValue(groupWireKey, out var value))
{
value.flashUntil = Time.unscaledTime + 0.18f;
}
}
private void UpdateWireVisuals()
{
wireCleanupBuffer.Clear();
foreach (KeyValuePair<string, WireVisual> wireVisual in wireVisuals)
{
WireVisual value = wireVisual.Value;
bool flag = value != null && value.sourceGroupId > 0;
if (value == null || value.renderer == null || value.input == null || value.input.Owner == null || (!flag && (value.output == null || value.output.Owner == null)))
{
wireCleanupBuffer.Add(wireVisual.Key);
}
else
{
UpdateWireVisual(value);
}
}
for (int i = 0; i < wireCleanupBuffer.Count; i++)
{
if (wireVisuals.TryGetValue(wireCleanupBuffer[i], out var value2))
{
DestroyWireVisualRenderer(value2);
wireVisuals.Remove(wireCleanupBuffer[i]);
}
}
}
private void UpdateWireVisual(WireVisual visual)
{
if (visual == null || visual.renderer == null || visual.input == null || visual.input.Owner == null)
{
return;
}
Vector3 portWorldPosition = visual.input.Owner.GetPortWorldPosition(visual.input);
Vector3 normalized = visual.input.Owner.GetPortWorldDirection(visual.input).normalized;
Color color;
if (visual.sourceGroupId > 0)
{
if (!TryResolveGroupSource(visual.sourceGroupId, out var group) || group == null)
{
visual.renderer.enabled = false;
return;
}
Bounds worldBounds = group.GetWorldBounds();
Vector3 vector = worldBounds.ClosestPoint(portWorldPosition);
if ((vector - portWorldPosition).sqrMagnitude < 0.0001f)
{
vector = worldBounds.center;
}
Vector3 startDirection = (((portWorldPosition - vector).sqrMagnitude > 0.0001f) ? (portWorldPosition - vector).normalized : Vector3.up);
SetCurvedLinePositions(visual.renderer, vector, startDirection, portWorldPosition, normalized, 12);
color = GroupWireColor;
}
else
{
if (visual.output == null || visual.output.Owner == null)
{
visual.renderer.enabled = false;
return;
}
Vector3 portWorldPosition2 = visual.output.Owner.GetPortWorldPosition(visual.output);
Vector3 normalized2 = visual.output.Owner.GetPortWorldDirection(visual.output).normalized;
SetCurvedLinePositions(visual.renderer, portWorldPosition2, normalized2, portWorldPosition, normalized, 12);
color = ((visual.Kind != PortKind.Run) ? (visual.output.Value.Normalize().Kind switch
{
BSValueKind.Bool => BoolWireColor,
BSValueKind.Number => NumberWireColor,
BSValueKind.Text => TextWireColor,
BSValueKind.Player => PlayerWireColor,
BSValueKind.Group => GroupWireColor,
_ => NoneWireColor,
}) : RunWireColor);
}
if (visual.flashUntil > Time.unscaledTime)
{
float t = Mathf.Clamp01((visual.flashUntil - Time.unscaledTime) / 0.18f);
color = Color.Lerp(color, WireFlashColor, t);
visual.renderer.widthMultiplier = Mathf.Lerp(0.015f, 0.024f, t);
}
else
{
visual.renderer.widthMultiplier = 0.015f;
}
visual.renderer.startColor = color;
visual.renderer.endColor = color;
visual.renderer.enabled = editVisibilityApplied;
}
private void DestroyWireVisualRenderer(WireVisual visual)
{
if (visual != null && !(visual.renderer == null))
{
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(visual.renderer.gameObject);
}
else
{
UnityEngine.Object.DestroyImmediate(visual.renderer.gameObject);
}
}
}
private void UpdateTransientLines()
{
bool num = CanEdit();
bool flag = UseXRInput();
Ray ray;
if (!num)
{
if (previewWireRenderer != null)
{
previewWireRenderer.enabled = false;
}
if (aimRayRenderer != null)
{
aimRayRenderer.enabled = false;
}
}
else if (TryGetLeftRay(out ray))
{
Vector3 vector = GetPreviewRayEndpoint(ray);
if (hoveredPort != null && hoveredPort.Owner != null)
{
vector = hoveredPort.Owner.GetPortWorldPosition(hoveredPort);
}
if (aimRayRenderer != null)
{
aimRayRenderer.enabled = flag;
if (flag)
{
aimRayRenderer.positionCount = 2;
aimRayRenderer.SetPosition(0, ray.origin);
aimRayRenderer.SetPosition(1, vector);
aimRayRenderer.startColor = AimRayColor;
aimRayRenderer.endColor = AimRayColor;
}
}
if (activeWire != null && previewWireRenderer != null)
{
previewWireRenderer.enabled = true;
Vector3 end = ((hoveredPort != null && IsWireTargetCompatible(hoveredPort)) ? hoveredPort.Owner.GetPortWorldPosition(hoveredPort) : vector);
Color color = ((activeWire.Kind == PortKind.Run) ? RunWireColor : ((activeWire.startGroup != null) ? GroupWireColor : ((activeWire.startPort == null) ? NoneWireColor : (activeWire.startPort.Value.Normalize().Kind switch
{
BSValueKind.Bool => BoolWireColor,
BSValueKind.Number => NumberWireColor,
BSValueKind.Text => TextWireColor,
BSValueKind.Player => PlayerWireColor,
BSValueKind.Group => GroupWireColor,
_ => NoneWireColor,
}))));
previewWireRenderer.startColor = color;
previewWireRenderer.endColor = color;
SetCurvedLinePositions(previewWireRenderer, activeWire.startWorldPosition, ray.direction, end, -ray.direction, 30);
}
else if (previewWireRenderer != null)
{
previewWireRenderer.enabled = false;
}
}
else
{
if (previewWireRenderer != null)
{
previewWireRenderer.enabled = false;
}
if (aimRayRenderer != null)
{
aimRayRenderer.enabled = false;
}
}
}
private Vector3 GetPreviewRayEndpoint(Ray ray)
{
if (Physics.Raycast(ray, out var hitInfo, wireRayDistance, wireRaycastMask, QueryTriggerInteraction.Ignore))
{
return hitInfo.point;
}
return ray.origin + ray.direction.normalized * wireRayDistance;
}
private void SetCurvedLinePositions(LineRenderer renderer, Vector3 start, Vector3 startDirection, Vector3 end, Vector3 endDirection, int vertexCount)
{
if (!(renderer == null))
{
float magnitude = (end - start).magnitude;
Vector3 vector = Vector3.up * Mathf.Clamp(magnitude * 0.12f, 0.04f, 0.18f);
float num = Mathf.Clamp(magnitude * 0.22f, 0.06f, 0.28f);
Vector3 p = start + startDirection.normalized * num + vector;
Vector3 p2 = end + endDirection.normalized * num + vector;
int num2 = (renderer.positionCount = Mathf.Max(2, vertexCount));
for (int i = 0; i < num2; i++)
{
float t = ((num2 == 1) ? 1f : ((float)i / ((float)num2 - 1f)));
renderer.SetPosition(i, EvaluateCubicBezier(start, p, p2, end, t));
}
}
}
private static Vector3 EvaluateCubicBezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float num = 1f - t;
float num2 = num * num;
float num3 = t * t;
return num2 * num * p0 + 3f * num2 * t * p1 + 3f * num * num3 * p2 + num3 * t * p3;
}
private bool TryGetLeftRay(out Ray ray)
{
ray = default(Ray);
if (UseXRInput())
{
if (leftHandAnchor == null)
{
return false;
}
ray = new Ray(leftHandAnchor.position, leftHandAnchor.forward);
return true;
}
Camera main = Camera.main;
if (main == null)
{
return false;
}
ray = new Ray(main.transform.position, main.transform.forward);
return true;
}
private bool TryGetRightRay(out Ray ray)
{
ray = default(Ray);
if (UseXRInput())
{
if (rightHandAnchor == null)
{
return false;
}
ray = new Ray(rightHandAnchor.position, rightHandAnchor.forward);
return true;
}
Camera main = Camera.main;
if (main == null)
{
return false;
}
ray = new Ray(main.transform.position, main.transform.forward);
return true;
}
private Node TryPickNode(Ray ray)
{
Node result = null;
float num = float.PositiveInfinity;
foreach (KeyValuePair<int, Node> item in nodesById)
{
Node value = item.Value;
if (value == null)
{
continue;
}
Vector3 position = value.transform.position;
float num2 = Vector3.Dot(position - ray.origin, ray.direction);
if (!(num2 < 0f) && !(num2 > wireRayDistance))
{
Vector3 vector = ray.origin + ray.direction * num2;
if (!((position - vector).sqrMagnitude > nodePickRadius * nodePickRadius) && num2 < num)
{
num = num2;
result = value;
}
}
}
return result;
}
private NodePort TryPickPort(Ray ray, float maxDistance, PortKind? requiredKind, PortDirection? requiredDirection)
{
NodePort nodePort = null;
float bestDistance = float.PositiveInfinity;
foreach (KeyValuePair<int, Node> item in nodesById)
{
Node value = item.Value;
if (!(value == null))
{
nodePort = EvaluatePorts(value.InputPorts, ray, maxDistance, requiredKind, requiredDirection, nodePort, ref bestDistance);
nodePort = EvaluatePorts(value.OutputPorts, ray, maxDistance, requiredKind, requiredDirection, nodePort, ref bestDistance);
}
}
return nodePort;
}
private NodePort EvaluatePorts(List<NodePort> ports, Ray ray, float maxDistance, PortKind? requiredKind, PortDirection? requiredDirection, NodePort bestPort, ref float bestDistance)
{
for (int i = 0; i < ports.Count; i++)
{
NodePort nodePort = ports[i];
if (nodePort == null || nodePort.Owner == null || nodePort.VisualTransform == null || (requiredKind.HasValue && nodePort.Kind != requiredKind.Value) || (requiredDirection.HasValue && nodePort.Direction != requiredDirection.Value) || !IsWireTargetCompatible(nodePort))
{
continue;
}
Vector3 portWorldPosition = nodePort.Owner.GetPortWorldPosition(nodePort);
float num = Vector3.Dot(portWorldPosition - ray.origin, ray.direction);
if (!(num < 0f) && !(num > maxDistance) && !(num > bestDistance))
{
Vector3 vector = ray.origin + ray.direction * num;
if (!((portWorldPosition - vector).sqrMagnitude > portPickRadius * portPickRadius))
{
bestDistance = num;
bestPort = nodePort;
}
}
}
return bestPort;
}
private VoxelGroup TryPickGroup(Ray ray)
{
VoxelGroup voxelGroup = TryPickGroupFromList(ray, (VRGroupSelector.Instance != null && VRGroupSelector.Instance.HasSelection) ? VRGroupSelector.Instance.GetSelectedGroupsSnapshot() : null);
if (voxelGroup != null)
{
return voxelGroup;
}
if (groupManager == null)
{
return null;
}
groupManager.GetAllGroups(groupPickBuffer);
return TryPickGroupFromList(ray, groupPickBuffer);
}
private VoxelGroup TryPickGroupFromList(Ray ray, List<VoxelGroup> groups)
{
if (groups == null || groups.Count == 0)
{
return null;
}
VoxelGroup result = null;
float num = float.PositiveInfinity;
for (int i = 0; i < groups.Count; i++)
{
VoxelGroup voxelGroup = groups[i];
if (!(voxelGroup == null) && voxelGroup.GetWorldBounds().IntersectRay(ray, out var distance) && distance < num && distance <= wireRayDistance)
{
num = distance;
result = voxelGroup;
}
}
return result;
}
private bool TryResolveDirectionalPorts(NodePort firstPort, NodePort secondPort, out NodePort outputPort, out NodePort inputPort)
{
outputPort = null;
inputPort = null;
if (firstPort == null || secondPort == null || firstPort.Kind != secondPort.Kind)
{
return false;
}
if (firstPort.Direction == PortDirection.Output && secondPort.Direction == PortDirection.Input)
{
outputPort = firstPort;
inputPort = secondPort;
return true;
}
if (firstPort.Direction == PortDirection.Input && secondPort.Direction == PortDirection.Output)
{
outputPort = secondPort;
inputPort = firstPort;
return true;
}
return false;
}
private bool IsWireTargetCompatible(NodePort port)
{
if (port == null)
{
return false;
}
if (activeWire == null)
{
return true;
}
if (activeWire.startGroup != null)
{
if (port.Direction == PortDirection.Input)
{
return port.Kind == PortKind.Value;
}
return false;
}
if (activeWire.startPort == null)
{
return false;
}
NodePort outputPort;
NodePort inputPort;
return TryResolveDirectionalPorts(activeWire.startPort, port, out outputPort, out inputPort);
}
private PortKind? GetExpectedWireTargetKind()
{
if (activeWire == null)
{
return null;
}
return activeWire.Kind;
}
private PortDirection? GetExpectedWireTargetDirection()
{
if (activeWire == null)
{
return null;
}
if (activeWire.startGroup != null)
{
return PortDirection.Input;
}
if (activeWire.startPort == null)
{
return null;
}
return (activeWire.startPort.Direction != PortDirection.Output) ? PortDirection.Output : PortDirection.Input;
}
private bool TryGetSelectionBounds(out Bounds bounds)
{
bounds = default(Bounds);
bool flag = false;
foreach (Node selectedNode in selectedNodes)
{
if (!(selectedNode == null))
{
Bounds worldBounds = selectedNode.GetWorldBounds();
if (!flag)
{
bounds = worldBounds;
flag = true;
}
else
{
bounds.Encapsulate(worldBounds);
}
}
}
return flag;
}
private bool TryGetGrabReferencePose(out Vector3 worldPosition, out Quaternion worldRotation)
{
if (UseXRInput())
{
if (rightHandAnchor != null)
{
worldPosition = rightHandAnchor.position;
worldRotation = rightHandAnchor.rotation;
return true;
}
if (trackingOrigin != null && rightHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out var value) && rightHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out var value2))
{
worldPosition = trackingOrigin.TransformPoint(value);
worldRotation = trackingOrigin.rotation * value2;
return true;
}
}
Camera main = Camera.main;
if (main != null)
{
worldPosition = main.transform.position;
worldRotation = main.transform.rotation;
return true;
}
worldPosition = base.transform.position;
worldRotation = base.transform.rotation;
return false;
}
private static float ProjectBoundsAlongRay(Bounds bounds, Ray ray)
{
return Vector3.Dot(bounds.center - ray.origin, ray.direction);
}
private static Vector3 SnapVector(Vector3 value, float snap)
{
if (snap <= 0.0001f)
{
return value;
}
return new Vector3(Mathf.Round(value.x / snap) * snap, Mathf.Round(value.y / snap) * snap, Mathf.Round(value.z / snap) * snap);
}
private static Quaternion SnapRotation(Quaternion rotation, float snapDegrees)
{
if (snapDegrees <= 0.001f)
{
return rotation;
}
Vector3 eulerAngles = rotation.eulerAngles;
eulerAngles.x = Mathf.Round(eulerAngles.x / snapDegrees) * snapDegrees;
eulerAngles.y = Mathf.Round(eulerAngles.y / snapDegrees) * snapDegrees;
eulerAngles.z = Mathf.Round(eulerAngles.z / snapDegrees) * snapDegrees;
return Quaternion.Euler(eulerAngles);
}
private string GetWireKey(NodePort outputPort, NodePort inputPort)
{
int num = ((outputPort != null && outputPort.Owner != null) ? outputPort.Owner.NodeId : 0);
int num2 = ((inputPort != null && inputPort.Owner != null) ? inputPort.Owner.NodeId : 0);
return $"{num}:{outputPort?.Name}->{num2}:{inputPort?.Name}";
}
private string GetGroupWireKey(int groupId, NodePort inputPort)
{
int num = ((inputPort != null && inputPort.Owner != null) ? inputPort.Owner.NodeId : 0);
return $"group:{groupId}->{num}:{inputPort?.Name}";
}
private bool TryResolveGroupSource(int groupId, out VoxelGroup group)
{
group = null;
if (groupManager == null || groupId <= 0 || groupId > 65535)
{
return false;
}
return groupManager.TryGetGroup((ushort)groupId, out group);
}
private bool CanEdit()
{
if (groupManager != null && groupManager.CreatorMode)
{
return !groupManager.EditingLocked;
}
return false;
}
private bool UseXRInput()
{
if (brush != null)
{
return brush.UsingXRInput;
}
if (!InputDevices.GetDeviceAtXRNode(XRNode.Head).isValid && !InputDevices.GetDeviceAtXRNode(XRNode.LeftHand).isValid)
{
return InputDevices.GetDeviceAtXRNode(XRNode.RightHand).isValid;
}
return true;
}
private NodeGraphPhotonSync ResolveNetworkSync()
{
if (networkSync == null)
{
networkSync = GetComponent<NodeGraphPhotonSync>();
}
return networkSync;
}
private static int CompareNodesByHierarchyPath(Node a, Node b)
{
if ((object)a == b)
{
return 0;
}
if (a == null)
{
return -1;
}
if (b == null)
{
return 1;
}
return string.CompareOrdinal(GetHierarchyPath(a.transform), GetHierarchyPath(b.transform));
}
private static string GetHierarchyPath(Transform target)
{
if (target == null)
{
return string.Empty;
}
List<string> list = new List<string>();
Transform transform = target;
while (transform != null)
{
list.Add($"{transform.GetSiblingIndex():D4}_{transform.name}");
transform = transform.parent;
}
list.Reverse();
return string.Join("/", list);
}
private static bool ReadBool(InputDevice device, InputFeatureUsage<bool> usage)
{
bool value = default(bool);
return device.isValid && device.TryGetFeatureValue(usage, out value) && value;
}
private static float ReadFloat(InputDevice device, InputFeatureUsage<float> usage)
{
if (!device.isValid || !device.TryGetFeatureValue(usage, out var value))
{
return 0f;
}
return value;
}
private bool ReadGrabHeld(InputDevice device)
{
return ReadFloat(device, CommonUsages.grip) >= rightGripThreshold;
}
private static bool BSValueEquals(BSValue a, BSValue b)
{
a = a.Normalize();
b = b.Normalize();
if (a.Kind == b.Kind && Math.Abs(a.Number - b.Number) <= 0.0001 && string.Equals(a.Text ?? string.Empty, b.Text ?? string.Empty, StringComparison.Ordinal) && a.PlayerId == b.PlayerId)
{
return a.GroupId == b.GroupId;
}
return false;
}
}