You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
7.1 KiB
C#

1 year ago
using System;
using System.Collections.Generic;
using System.Linq;
namespace Unity.VisualScripting
{
public sealed class UnitPreservation : IPoolable
{
private struct UnitPortPreservation
{
public readonly IUnit unit;
public readonly string key;
public UnitPortPreservation(IUnitPort port)
{
unit = port.unit;
key = port.key;
}
public UnitPortPreservation(IUnit unit, string key)
{
this.unit = unit;
this.key = key;
}
public IUnitPort GetOrCreateInput(out InvalidInput newInvalidInput)
{
var key = this.key;
if (!unit.inputs.Any(p => p.key == key))
{
newInvalidInput = new InvalidInput(key);
unit.invalidInputs.Add(newInvalidInput);
}
else
{
newInvalidInput = null;
}
return unit.inputs.Single(p => p.key == key);
}
public IUnitPort GetOrCreateOutput(out InvalidOutput newInvalidOutput)
{
var key = this.key;
if (!unit.outputs.Any(p => p.key == key))
{
newInvalidOutput = new InvalidOutput(key);
unit.invalidOutputs.Add(newInvalidOutput);
}
else
{
newInvalidOutput = null;
}
return unit.outputs.Single(p => p.key == key);
}
}
private readonly Dictionary<string, object> defaultValues = new Dictionary<string, object>();
private readonly Dictionary<string, List<UnitPortPreservation>> inputConnections = new Dictionary<string, List<UnitPortPreservation>>();
private readonly Dictionary<string, List<UnitPortPreservation>> outputConnections = new Dictionary<string, List<UnitPortPreservation>>();
private bool disposed;
void IPoolable.New()
{
disposed = false;
}
void IPoolable.Free()
{
disposed = true;
foreach (var inputConnection in inputConnections)
{
ListPool<UnitPortPreservation>.Free(inputConnection.Value);
}
foreach (var outputConnection in outputConnections)
{
ListPool<UnitPortPreservation>.Free(outputConnection.Value);
}
defaultValues.Clear();
inputConnections.Clear();
outputConnections.Clear();
}
private UnitPreservation() { }
public static UnitPreservation Preserve(IUnit unit)
{
var preservation = GenericPool<UnitPreservation>.New(() => new UnitPreservation());
foreach (var defaultValue in unit.defaultValues)
{
preservation.defaultValues.Add(defaultValue.Key, defaultValue.Value);
}
foreach (var input in unit.inputs)
{
if (input.hasAnyConnection)
{
preservation.inputConnections.Add(input.key, ListPool<UnitPortPreservation>.New());
foreach (var connectedPort in input.connectedPorts)
{
preservation.inputConnections[input.key].Add(new UnitPortPreservation(connectedPort));
}
}
}
foreach (var output in unit.outputs)
{
if (output.hasAnyConnection)
{
preservation.outputConnections.Add(output.key, ListPool<UnitPortPreservation>.New());
foreach (var connectedPort in output.connectedPorts)
{
preservation.outputConnections[output.key].Add(new UnitPortPreservation(connectedPort));
}
}
}
return preservation;
}
public void RestoreTo(IUnit unit)
{
if (disposed)
{
throw new ObjectDisposedException(ToString());
}
// Restore inline values if possible
foreach (var previousDefaultValue in defaultValues)
{
if (unit.defaultValues.ContainsKey(previousDefaultValue.Key) &&
unit.valueInputs.Contains(previousDefaultValue.Key) &&
unit.valueInputs[previousDefaultValue.Key].type.IsAssignableFrom(previousDefaultValue.Value))
{
unit.defaultValues[previousDefaultValue.Key] = previousDefaultValue.Value;
}
}
// Restore connections if possible
foreach (var previousInputConnections in inputConnections)
{
var previousInputPort = new UnitPortPreservation(unit, previousInputConnections.Key);
var previousOutputPorts = previousInputConnections.Value;
foreach (var previousOutputPort in previousOutputPorts)
{
RestoreConnection(previousOutputPort, previousInputPort);
}
}
foreach (var previousOutputConnections in outputConnections)
{
var previousOutputPort = new UnitPortPreservation(unit, previousOutputConnections.Key);
var previousInputPorts = previousOutputConnections.Value;
foreach (var previousInputPort in previousInputPorts)
{
RestoreConnection(previousOutputPort, previousInputPort);
}
}
GenericPool<UnitPreservation>.Free(this);
}
private void RestoreConnection(UnitPortPreservation sourcePreservation, UnitPortPreservation destinationPreservation)
{
InvalidOutput newInvalidSource;
InvalidInput newInvalidDestination;
var source = sourcePreservation.GetOrCreateOutput(out newInvalidSource);
var destination = destinationPreservation.GetOrCreateInput(out newInvalidDestination);
if (source.CanValidlyConnectTo(destination))
{
source.ValidlyConnectTo(destination);
}
else if (source.CanInvalidlyConnectTo(destination))
{
source.InvalidlyConnectTo(destination);
}
else
{
// In this case, we created invalid ports to attempt a connection,
// but even that failed (due to, for example, a cross-graph restoration).
// Therefore, we need to delete the invalid ports we created.
if (newInvalidSource != null)
{
sourcePreservation.unit.invalidOutputs.Remove(newInvalidSource);
}
if (newInvalidDestination != null)
{
destinationPreservation.unit.invalidInputs.Remove(newInvalidDestination);
}
}
}
}
}