5.14 시작전
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using KAsyncEngineLib;
|
||||
@@ -319,6 +321,12 @@ if (args.Length > 0 && string.Equals(args[0], "--validate-live-cuts", StringComp
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length > 0 && string.Equals(args[0], "--audit-party-colors-live", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Environment.ExitCode = await LiveCutValidation.RunPartyColorAuditAsync(args[1..]).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length > 0 && string.Equals(args[0], "--validate-current-api-cuts", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Environment.ExitCode = await CurrentApiCutDiagnostics.RunAsync(args[1..]).ConfigureAwait(false);
|
||||
@@ -876,6 +884,14 @@ static Task<SaveSceneImageProbeResult> SaveSceneImageAsync(SaveSceneImageOptions
|
||||
var operationResult = ApplySceneOperation(handler, scene!, operation, options.Connection.Timeout);
|
||||
if (!string.Equals(operationResult.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal))
|
||||
{
|
||||
if (operation.ContinueOnFailure)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[SAVE-IMAGE] Optional operation {operationResult.Method} failed for '{operationResult.ObjectName}': " +
|
||||
$"{operationResult.Result} {operationResult.Detail}");
|
||||
continue;
|
||||
}
|
||||
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(
|
||||
true,
|
||||
@@ -1001,31 +1017,38 @@ static Task<SaveSceneImageProbeResult> SaveSceneImageAsync(SaveSceneImageOptions
|
||||
}
|
||||
}
|
||||
|
||||
var positionKeyUpdates = new List<PositionKeyUpdate>();
|
||||
if (options.PositionKey is not null)
|
||||
{
|
||||
positionKeyUpdates.Add(options.PositionKey);
|
||||
}
|
||||
|
||||
positionKeyUpdates.AddRange(options.PositionKeys);
|
||||
foreach (var positionKeyUpdate in positionKeyUpdates)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[SAVE-IMAGE] Setting position key object={options.PositionKey.ObjectName} index={options.PositionKey.KeyIndex} " +
|
||||
$"value=({options.PositionKey.X},{options.PositionKey.Y},{options.PositionKey.Z}) vector={options.PositionKey.VectorType}...");
|
||||
var sceneObject = scene.GetObject(options.PositionKey.ObjectName);
|
||||
$"[SAVE-IMAGE] Setting position key object={positionKeyUpdate.ObjectName} index={positionKeyUpdate.KeyIndex} " +
|
||||
$"value=({positionKeyUpdate.X},{positionKeyUpdate.Y},{positionKeyUpdate.Z}) vector={positionKeyUpdate.VectorType}...");
|
||||
var sceneObject = scene.GetObject(positionKeyUpdate.ObjectName);
|
||||
if (sceneObject is null)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "FAILED", options.OutputPath, $"Object '{options.PositionKey.ObjectName}' was not found."));
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "FAILED", options.OutputPath, $"Object '{positionKeyUpdate.ObjectName}' was not found."));
|
||||
return;
|
||||
}
|
||||
|
||||
handler.ResetPositionKeyTask();
|
||||
sceneObject.SetPositionKey(
|
||||
options.PositionKey.KeyIndex,
|
||||
options.PositionKey.X,
|
||||
options.PositionKey.Y,
|
||||
options.PositionKey.Z,
|
||||
options.PositionKey.VectorType);
|
||||
positionKeyUpdate.KeyIndex,
|
||||
positionKeyUpdate.X,
|
||||
positionKeyUpdate.Y,
|
||||
positionKeyUpdate.Z,
|
||||
positionKeyUpdate.VectorType);
|
||||
|
||||
if (!WaitForTaskWithMessagePump(handler.PositionKeyTask, options.Connection.Timeout))
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "TIMEOUT", options.OutputPath, $"OnSetPositionKey timed out for '{options.PositionKey.ObjectName}'." ));
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "TIMEOUT", options.OutputPath, $"OnSetPositionKey timed out for '{positionKeyUpdate.ObjectName}'." ));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1033,7 +1056,7 @@ static Task<SaveSceneImageProbeResult> SaveSceneImageAsync(SaveSceneImageOptions
|
||||
if (positionKeyResult != eKResult.RESULT_SUCCESS)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", positionKeyResult.ToString(), options.OutputPath, $"OnSetPositionKey result={positionKeyResult} object={options.PositionKey.ObjectName}"));
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", positionKeyResult.ToString(), options.OutputPath, $"OnSetPositionKey result={positionKeyResult} object={positionKeyUpdate.ObjectName}"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1179,53 +1202,161 @@ static Task<SaveSceneImageProbeResult> SaveSceneImageAsync(SaveSceneImageOptions
|
||||
}
|
||||
}
|
||||
|
||||
var outputDirectory = Path.GetDirectoryName(options.OutputPath);
|
||||
if (!string.IsNullOrWhiteSpace(outputDirectory))
|
||||
foreach (var positionUpdate in options.PostPositions)
|
||||
{
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
}
|
||||
|
||||
if (File.Exists(options.OutputPath))
|
||||
{
|
||||
File.Delete(options.OutputPath);
|
||||
}
|
||||
|
||||
Console.WriteLine("[SAVE-IMAGE] Calling SaveSceneImage()...");
|
||||
handler.ResetSaveSceneImageTask();
|
||||
scene.SaveSceneImage(options.OutputPath, options.Width, options.Height, options.Frame);
|
||||
|
||||
if (!WaitForTaskWithMessagePump(handler.SaveSceneImageTask, options.Connection.Timeout))
|
||||
{
|
||||
completion.TrySetResult(new SaveSceneImageProbeResult(true, "SUCCESS", "TIMEOUT", options.OutputPath, "OnSaveSceneImage timed out."));
|
||||
return;
|
||||
}
|
||||
|
||||
var saveResult = handler.SaveSceneImageTask.Result;
|
||||
if (saveResult != eKResult.RESULT_SUCCESS)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", saveResult.ToString(), options.OutputPath, $"OnSaveSceneImage result={saveResult}"));
|
||||
return;
|
||||
}
|
||||
|
||||
var fileWaitDeadline = DateTime.UtcNow + options.Connection.Timeout;
|
||||
while (DateTime.UtcNow < fileWaitDeadline)
|
||||
{
|
||||
if (File.Exists(options.OutputPath))
|
||||
Console.WriteLine(
|
||||
$"[SAVE-IMAGE] Setting post-chart position object={positionUpdate.ObjectName} " +
|
||||
$"value=({positionUpdate.X},{positionUpdate.Y},{positionUpdate.Z}) vector={positionUpdate.VectorType}...");
|
||||
var sceneObject = scene.GetObject(positionUpdate.ObjectName);
|
||||
if (sceneObject is null)
|
||||
{
|
||||
var info = new FileInfo(options.OutputPath);
|
||||
if (info.Length > 0)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "SUCCESS", options.OutputPath, $"Saved {info.Length} bytes."));
|
||||
return;
|
||||
}
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "FAILED", options.OutputPath, $"Object '{positionUpdate.ObjectName}' was not found."));
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep(50);
|
||||
handler.ResetPositionTask();
|
||||
sceneObject.SetPosition(
|
||||
positionUpdate.X,
|
||||
positionUpdate.Y,
|
||||
positionUpdate.Z,
|
||||
positionUpdate.VectorType);
|
||||
|
||||
if (!WaitForTaskWithMessagePump(handler.PositionTask, options.Connection.Timeout))
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "TIMEOUT", options.OutputPath, $"OnSetPosition timed out for '{positionUpdate.ObjectName}'." ));
|
||||
return;
|
||||
}
|
||||
|
||||
var positionResult = handler.PositionTask.Result;
|
||||
if (positionResult != eKResult.RESULT_SUCCESS)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", positionResult.ToString(), options.OutputPath, $"OnSetPosition result={positionResult} object={positionUpdate.ObjectName}"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
completion.TrySetResult(new SaveSceneImageProbeResult(true, "SUCCESS", "FAILED", options.OutputPath, "Image file was not created."));
|
||||
foreach (var positionKeyUpdate in options.PostPositionKeys)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[SAVE-IMAGE] Setting post-chart position key object={positionKeyUpdate.ObjectName} index={positionKeyUpdate.KeyIndex} " +
|
||||
$"value=({positionKeyUpdate.X},{positionKeyUpdate.Y},{positionKeyUpdate.Z}) vector={positionKeyUpdate.VectorType}...");
|
||||
var sceneObject = scene.GetObject(positionKeyUpdate.ObjectName);
|
||||
if (sceneObject is null)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "FAILED", options.OutputPath, $"Object '{positionKeyUpdate.ObjectName}' was not found."));
|
||||
return;
|
||||
}
|
||||
|
||||
handler.ResetPositionKeyTask();
|
||||
sceneObject.SetPositionKey(
|
||||
positionKeyUpdate.KeyIndex,
|
||||
positionKeyUpdate.X,
|
||||
positionKeyUpdate.Y,
|
||||
positionKeyUpdate.Z,
|
||||
positionKeyUpdate.VectorType);
|
||||
|
||||
if (!WaitForTaskWithMessagePump(handler.PositionKeyTask, options.Connection.Timeout))
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", "TIMEOUT", options.OutputPath, $"OnSetPositionKey timed out for '{positionKeyUpdate.ObjectName}'." ));
|
||||
return;
|
||||
}
|
||||
|
||||
var positionKeyResult = handler.PositionKeyTask.Result;
|
||||
if (positionKeyResult != eKResult.RESULT_SUCCESS)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", positionKeyResult.ToString(), options.OutputPath, $"OnSetPositionKey result={positionKeyResult} object={positionKeyUpdate.ObjectName}"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var captures = new List<(string OutputPath, int Frame)>();
|
||||
if (options.Frames.Count > 0)
|
||||
{
|
||||
var captureDirectory = options.OutputDirectory ?? options.OutputPath;
|
||||
foreach (var captureFrame in options.Frames)
|
||||
{
|
||||
captures.Add((
|
||||
Path.GetFullPath(Path.Combine(
|
||||
captureDirectory,
|
||||
string.Format(CultureInfo.InvariantCulture, options.OutputPattern, captureFrame))),
|
||||
captureFrame));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
captures.Add((options.OutputPath, options.Frame));
|
||||
}
|
||||
|
||||
long totalBytes = 0;
|
||||
foreach (var capture in captures)
|
||||
{
|
||||
var outputDirectory = Path.GetDirectoryName(capture.OutputPath);
|
||||
if (!string.IsNullOrWhiteSpace(outputDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
}
|
||||
|
||||
if (File.Exists(capture.OutputPath))
|
||||
{
|
||||
File.Delete(capture.OutputPath);
|
||||
}
|
||||
|
||||
Console.WriteLine($"[SAVE-IMAGE] Calling SaveSceneImage() frame={capture.Frame} output={capture.OutputPath}...");
|
||||
handler.ResetSaveSceneImageTask();
|
||||
scene.SaveSceneImage(capture.OutputPath, options.Width, options.Height, capture.Frame);
|
||||
|
||||
if (!WaitForTaskWithMessagePump(handler.SaveSceneImageTask, options.Connection.Timeout))
|
||||
{
|
||||
completion.TrySetResult(new SaveSceneImageProbeResult(true, "SUCCESS", "TIMEOUT", capture.OutputPath, "OnSaveSceneImage timed out."));
|
||||
return;
|
||||
}
|
||||
|
||||
var saveResult = handler.SaveSceneImageTask.Result;
|
||||
if (saveResult != eKResult.RESULT_SUCCESS)
|
||||
{
|
||||
completion.TrySetResult(
|
||||
new SaveSceneImageProbeResult(true, "SUCCESS", saveResult.ToString(), capture.OutputPath, $"OnSaveSceneImage result={saveResult}"));
|
||||
return;
|
||||
}
|
||||
|
||||
var savedThisFrame = false;
|
||||
var fileWaitDeadline = DateTime.UtcNow + options.Connection.Timeout;
|
||||
while (DateTime.UtcNow < fileWaitDeadline)
|
||||
{
|
||||
if (File.Exists(capture.OutputPath))
|
||||
{
|
||||
var info = new FileInfo(capture.OutputPath);
|
||||
if (info.Length > 0)
|
||||
{
|
||||
totalBytes += info.Length;
|
||||
savedThisFrame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
if (!savedThisFrame)
|
||||
{
|
||||
completion.TrySetResult(new SaveSceneImageProbeResult(true, "SUCCESS", "FAILED", capture.OutputPath, "Image file was not created."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var resultOutput = options.Frames.Count > 0
|
||||
? options.OutputDirectory ?? options.OutputPath
|
||||
: options.OutputPath;
|
||||
var detail = captures.Count == 1
|
||||
? $"Saved {totalBytes} bytes."
|
||||
: $"Saved {captures.Count} frames ({totalBytes} bytes).";
|
||||
completion.TrySetResult(new SaveSceneImageProbeResult(true, "SUCCESS", "SUCCESS", resultOutput, detail));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -3430,6 +3561,9 @@ internal sealed record SaveSceneImageOptions(
|
||||
int Width,
|
||||
int Height,
|
||||
int Frame,
|
||||
IReadOnlyList<int> Frames,
|
||||
string? OutputDirectory,
|
||||
string OutputPattern,
|
||||
string? SetObjectName,
|
||||
string? SetObjectValue,
|
||||
string? VisibleObjectName,
|
||||
@@ -3442,6 +3576,9 @@ internal sealed record SaveSceneImageOptions(
|
||||
PositionUpdate? Position,
|
||||
IReadOnlyList<PositionUpdate> Positions,
|
||||
PositionKeyUpdate? PositionKey,
|
||||
IReadOnlyList<PositionKeyUpdate> PositionKeys,
|
||||
IReadOnlyList<PositionUpdate> PostPositions,
|
||||
IReadOnlyList<PositionKeyUpdate> PostPositionKeys,
|
||||
string? ChartObjectName,
|
||||
string? ChartCsvPath,
|
||||
IReadOnlyList<ChartCellUpdate> ChartCells,
|
||||
@@ -3455,6 +3592,9 @@ internal sealed record SaveSceneImageOptions(
|
||||
string? scenePath = null;
|
||||
string? sceneAlias = null;
|
||||
string? outputPath = null;
|
||||
string? outputDirectory = null;
|
||||
string outputPattern = "frame_{0:D4}.png";
|
||||
IReadOnlyList<int> frames = Array.Empty<int>();
|
||||
string? setObjectName = null;
|
||||
string? setObjectValue = null;
|
||||
string? visibleObjectName = null;
|
||||
@@ -3474,6 +3614,9 @@ internal sealed record SaveSceneImageOptions(
|
||||
string? positionKeyObjectName = null;
|
||||
int positionKeyIndex = 1;
|
||||
string? positionKeyRaw = null;
|
||||
string? positionKeysRaw = null;
|
||||
string? postPositionsRaw = null;
|
||||
string? postPositionKeysRaw = null;
|
||||
string? chartObjectName = null;
|
||||
string? chartCsvPath = null;
|
||||
string? chartCellsRaw = null;
|
||||
@@ -3497,6 +3640,12 @@ internal sealed record SaveSceneImageOptions(
|
||||
case "--output" when index + 1 < args.Length:
|
||||
outputPath = args[++index];
|
||||
break;
|
||||
case "--output-dir" when index + 1 < args.Length:
|
||||
outputDirectory = args[++index];
|
||||
break;
|
||||
case "--output-pattern" when index + 1 < args.Length:
|
||||
outputPattern = args[++index];
|
||||
break;
|
||||
case "--set-object" when index + 1 < args.Length:
|
||||
setObjectName = args[++index];
|
||||
break;
|
||||
@@ -3561,6 +3710,15 @@ internal sealed record SaveSceneImageOptions(
|
||||
case "--position-key" when index + 1 < args.Length:
|
||||
positionKeyRaw = args[++index];
|
||||
break;
|
||||
case "--position-keys" when index + 1 < args.Length:
|
||||
positionKeysRaw = args[++index];
|
||||
break;
|
||||
case "--post-positions" when index + 1 < args.Length:
|
||||
postPositionsRaw = args[++index];
|
||||
break;
|
||||
case "--post-position-keys" when index + 1 < args.Length:
|
||||
postPositionKeysRaw = args[++index];
|
||||
break;
|
||||
case "--chart-object" when index + 1 < args.Length:
|
||||
chartObjectName = args[++index];
|
||||
break;
|
||||
@@ -3591,6 +3749,9 @@ internal sealed record SaveSceneImageOptions(
|
||||
frame = parsedFrame;
|
||||
index++;
|
||||
break;
|
||||
case "--frames" when index + 1 < args.Length:
|
||||
frames = ParseFrameSequence(args[++index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3599,7 +3760,18 @@ internal sealed record SaveSceneImageOptions(
|
||||
throw new ArgumentException("--scene is required.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(outputPath))
|
||||
if (frames.Count > 0)
|
||||
{
|
||||
outputDirectory ??= outputPath;
|
||||
if (string.IsNullOrWhiteSpace(outputDirectory))
|
||||
{
|
||||
throw new ArgumentException("--output-dir is required when --frames is provided.");
|
||||
}
|
||||
|
||||
outputDirectory = Path.GetFullPath(outputDirectory);
|
||||
outputPath ??= outputDirectory;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(outputPath))
|
||||
{
|
||||
throw new ArgumentException("--output is required.");
|
||||
}
|
||||
@@ -3618,6 +3790,9 @@ internal sealed record SaveSceneImageOptions(
|
||||
width,
|
||||
height,
|
||||
frame,
|
||||
frames,
|
||||
outputDirectory,
|
||||
outputPattern,
|
||||
setObjectName,
|
||||
setObjectValue,
|
||||
visibleObjectName,
|
||||
@@ -3630,6 +3805,9 @@ internal sealed record SaveSceneImageOptions(
|
||||
ParsePosition(positionObjectName, positionRaw),
|
||||
ParsePositions(positionsRaw),
|
||||
ParsePositionKey(positionKeyObjectName, positionKeyIndex, positionKeyRaw),
|
||||
ParsePositionKeys(positionKeysRaw),
|
||||
ParsePositions(postPositionsRaw),
|
||||
ParsePositionKeys(postPositionKeysRaw),
|
||||
chartObjectName,
|
||||
chartCsvPath,
|
||||
ParseChartCells(chartCellsRaw),
|
||||
@@ -3638,6 +3816,53 @@ internal sealed record SaveSceneImageOptions(
|
||||
ParsePathModifications(modifyPathRaw));
|
||||
}
|
||||
|
||||
private static IReadOnlyList<int> ParseFrameSequence(string value)
|
||||
{
|
||||
var frames = new List<int>();
|
||||
foreach (var token in value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
{
|
||||
var rangeMatch = Regex.Match(token, @"^(?<start>-?\d+)-(?<end>-?\d+)(?::(?<step>\d+))?$", RegexOptions.CultureInvariant);
|
||||
if (rangeMatch.Success)
|
||||
{
|
||||
var start = int.Parse(rangeMatch.Groups["start"].Value, CultureInfo.InvariantCulture);
|
||||
var end = int.Parse(rangeMatch.Groups["end"].Value, CultureInfo.InvariantCulture);
|
||||
var step = rangeMatch.Groups["step"].Success
|
||||
? int.Parse(rangeMatch.Groups["step"].Value, CultureInfo.InvariantCulture)
|
||||
: 1;
|
||||
if (step <= 0)
|
||||
{
|
||||
throw new ArgumentException("--frames range step must be greater than zero.");
|
||||
}
|
||||
|
||||
if (start <= end)
|
||||
{
|
||||
for (var frame = start; frame <= end; frame += step)
|
||||
{
|
||||
frames.Add(frame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var frame = start; frame >= end; frame -= step)
|
||||
{
|
||||
frames.Add(frame);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out var singleFrame))
|
||||
{
|
||||
throw new ArgumentException($"Invalid frame token: {token}");
|
||||
}
|
||||
|
||||
frames.Add(singleFrame);
|
||||
}
|
||||
|
||||
return frames.Distinct().ToArray();
|
||||
}
|
||||
|
||||
private static CloneObjectUpdate? ParseCloneObject(string? sourceObjectName, string? variableName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sourceObjectName) || string.IsNullOrWhiteSpace(variableName))
|
||||
@@ -3776,6 +4001,38 @@ internal sealed record SaveSceneImageOptions(
|
||||
return new PositionKeyUpdate(objectName, keyIndex, x, y, z, vectorType);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<PositionKeyUpdate> ParsePositionKeys(string? raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
{
|
||||
return Array.Empty<PositionKeyUpdate>();
|
||||
}
|
||||
|
||||
var updates = new List<PositionKeyUpdate>();
|
||||
foreach (var token in raw.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
{
|
||||
var nameParts = token.Split('=', 2, StringSplitOptions.TrimEntries);
|
||||
if (nameParts.Length != 2)
|
||||
{
|
||||
throw new ArgumentException($"Invalid position key update: {token}");
|
||||
}
|
||||
|
||||
var objectAndKey = nameParts[0].Split('#', 2, StringSplitOptions.TrimEntries);
|
||||
if (objectAndKey.Length != 2 || !int.TryParse(objectAndKey[1], out var keyIndex))
|
||||
{
|
||||
throw new ArgumentException($"Invalid position key object/index: {nameParts[0]}");
|
||||
}
|
||||
|
||||
var update = ParsePositionKey(objectAndKey[0], keyIndex, nameParts[1]);
|
||||
if (update is not null)
|
||||
{
|
||||
updates.Add(update);
|
||||
}
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ChartCellUpdate> ParseChartCells(string? raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
@@ -4410,6 +4667,8 @@ internal sealed class SceneValidationOperation
|
||||
public int A { get; set; } = 255;
|
||||
|
||||
public bool Visible { get; set; }
|
||||
|
||||
public bool ContinueOnFailure { get; set; }
|
||||
}
|
||||
|
||||
internal sealed record SceneOperationValidationResult(string ObjectName, string Method, string Payload, string Result, string Detail);
|
||||
|
||||
Reference in New Issue
Block a user