1848 lines
81 KiB
C#
1848 lines
81 KiB
C#
using System;
|
|
using System.Net.Sockets;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using KAsyncEngineLib;
|
|
|
|
if (args.Length > 0 && string.Equals(args[0], "--reflect-api", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
PrintApiSurface();
|
|
return;
|
|
}
|
|
|
|
if (args.Length > 0 && string.Equals(args[0], "--test-counter", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var counterOptions = CounterProbeOptions.Parse(args[1..]);
|
|
Console.WriteLine(
|
|
$"Karisma counter probe starting. target={counterOptions.Connection.Host}:{counterOptions.Connection.Port} " +
|
|
$"scene={counterOptions.ScenePath} object={counterOptions.ObjectName} keyIndex={counterOptions.KeyIndex} value={counterOptions.Number:0.###}");
|
|
var counterResult = await ProbeCounterAsync(counterOptions).ConfigureAwait(false);
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Summary");
|
|
Console.WriteLine($"- SDK Connect(): {(counterResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
|
|
Console.WriteLine($"- Scene Load: {counterResult.SceneLoadOutcome}");
|
|
Console.WriteLine($"- Counter Update: {counterResult.CounterOutcome}");
|
|
Console.WriteLine($"- Detail: {counterResult.Detail}");
|
|
Environment.ExitCode = counterResult.ConnectRequestAccepted &&
|
|
counterResult.SceneLoadOutcome == "SUCCESS" &&
|
|
counterResult.CounterOutcome == "SUCCESS"
|
|
? 0
|
|
: 1;
|
|
return;
|
|
}
|
|
|
|
if (args.Length > 0 && string.Equals(args[0], "--catalog-scenes", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var catalogOptions = SceneCatalogOptions.Parse(args[1..]);
|
|
Console.WriteLine(
|
|
$"Karisma scene catalog starting. target={catalogOptions.Connection.Host}:{catalogOptions.Connection.Port} " +
|
|
$"root={catalogOptions.RootPath} output={catalogOptions.OutputPath}");
|
|
var catalogResult = await CatalogScenesAsync(catalogOptions).ConfigureAwait(false);
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Summary");
|
|
Console.WriteLine($"- SDK Connect(): {(catalogResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
|
|
Console.WriteLine($"- Scenes Found: {catalogResult.SceneCount}");
|
|
Console.WriteLine($"- Scenes Cataloged: {catalogResult.CatalogedCount}");
|
|
Console.WriteLine($"- Failures: {catalogResult.Failures.Count}");
|
|
Console.WriteLine($"- Output: {catalogResult.OutputPath}");
|
|
if (catalogResult.Failures.Count > 0)
|
|
{
|
|
Console.WriteLine("- Failure Details:");
|
|
foreach (var failure in catalogResult.Failures.Take(20))
|
|
{
|
|
Console.WriteLine($" - {failure.ScenePath}: {failure.Reason}");
|
|
}
|
|
}
|
|
|
|
Environment.ExitCode = catalogResult.ConnectRequestAccepted && catalogResult.Failures.Count == 0
|
|
? 0
|
|
: 1;
|
|
return;
|
|
}
|
|
|
|
if (args.Length > 0 && string.Equals(args[0], "--validate-scene-values", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var validationOptions = SceneValidationOptions.Parse(args[1..]);
|
|
Console.WriteLine(
|
|
$"Karisma scene validation starting. target={validationOptions.Connection.Host}:{validationOptions.Connection.Port} " +
|
|
$"scene={validationOptions.ScenePath} ops={validationOptions.OperationsPath} output={validationOptions.OutputPath}");
|
|
var validationResult = await ValidateSceneOperationsAsync(validationOptions).ConfigureAwait(false);
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Summary");
|
|
Console.WriteLine($"- SDK Connect(): {(validationResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
|
|
Console.WriteLine($"- Scene Load: {validationResult.SceneLoadOutcome}");
|
|
Console.WriteLine($"- Success Count: {validationResult.SuccessCount}");
|
|
Console.WriteLine($"- Failure Count: {validationResult.FailureCount}");
|
|
Console.WriteLine($"- Output: {validationResult.OutputPath}");
|
|
if (!string.IsNullOrWhiteSpace(validationResult.Detail))
|
|
{
|
|
Console.WriteLine($"- Detail: {validationResult.Detail}");
|
|
}
|
|
|
|
Environment.ExitCode = validationResult.ConnectRequestAccepted &&
|
|
validationResult.SceneLoadOutcome == "SUCCESS" &&
|
|
validationResult.FailureCount == 0
|
|
? 0
|
|
: 1;
|
|
return;
|
|
}
|
|
|
|
if (args.Length > 0 && string.Equals(args[0], "--inspect-tscn-folder", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var inspectionOptions = FolderInspectionOptions.Parse(args[1..]);
|
|
Console.WriteLine(
|
|
$"Karisma tscn inspection starting. target={inspectionOptions.Connection.Host}:{inspectionOptions.Connection.Port} " +
|
|
$"root={inspectionOptions.RootPath} output={inspectionOptions.OutputPath}");
|
|
var inspectionResult = await InspectTscnFolderAsync(inspectionOptions).ConfigureAwait(false);
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Summary");
|
|
Console.WriteLine($"- SDK Connect(): {(inspectionResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
|
|
Console.WriteLine($"- Scenes Found: {inspectionResult.SceneCount}");
|
|
Console.WriteLine($"- Scenes Processed: {inspectionResult.ProcessedSceneCount}");
|
|
Console.WriteLine($"- Variables Found: {inspectionResult.DiscoveredVariableCount}");
|
|
Console.WriteLine($"- Failures: {inspectionResult.FailureCount}");
|
|
Console.WriteLine($"- Output: {inspectionResult.OutputPath}");
|
|
if (!string.IsNullOrWhiteSpace(inspectionResult.Detail))
|
|
{
|
|
Console.WriteLine($"- Detail: {inspectionResult.Detail}");
|
|
}
|
|
|
|
Environment.ExitCode = inspectionResult.ConnectRequestAccepted && inspectionResult.FailureCount == 0
|
|
? 0
|
|
: 1;
|
|
return;
|
|
}
|
|
|
|
var options = ProbeOptions.Parse(args);
|
|
|
|
Console.WriteLine($"Karisma TCP probe starting. target={options.Host}:{options.Port} timeout={options.Timeout.TotalSeconds:0}s");
|
|
|
|
var rawTcpOk = await ProbeRawTcpAsync(options).ConfigureAwait(false);
|
|
var sdkResult = await ProbeSdkAsync(options).ConfigureAwait(false);
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Summary");
|
|
Console.WriteLine($"- Raw TCP: {(rawTcpOk ? "OK" : "FAILED")}");
|
|
Console.WriteLine($"- SDK Connect(): {(sdkResult.ConnectRequestAccepted ? "ACCEPTED" : "REJECTED")}");
|
|
Console.WriteLine($"- SDK OnConnect: {sdkResult.ConnectOutcome}");
|
|
Console.WriteLine($"- SDK Detail: {sdkResult.Detail}");
|
|
|
|
Environment.ExitCode = rawTcpOk && sdkResult.ConnectRequestAccepted && sdkResult.ConnectOutcome == "SUCCESS"
|
|
? 0
|
|
: 1;
|
|
|
|
static void PrintApiSurface()
|
|
{
|
|
PrintTypeMethods(typeof(IKAObject));
|
|
PrintTypeMethods(typeof(KAObject));
|
|
PrintTypeMethods(typeof(IKAScene));
|
|
PrintTypeMethods(typeof(KAScene));
|
|
PrintTypeMethods(typeof(IKAEngine));
|
|
PrintTypeMethods(typeof(KAEngine));
|
|
PrintTypeMethods(typeof(IKACounter));
|
|
PrintTypeMethods(typeof(KACounter));
|
|
PrintTypeMethods(typeof(IKAObjectInfos));
|
|
PrintTypeMethods(typeof(KAObjectInfos));
|
|
PrintStructFields(typeof(sKObjectInfo));
|
|
PrintEnumValues(typeof(eKObjectType));
|
|
}
|
|
|
|
static void PrintTypeMethods(Type type)
|
|
{
|
|
Console.WriteLine(type.FullName);
|
|
foreach (var method in type.GetMethods())
|
|
{
|
|
if (!method.Name.Contains("Counter", StringComparison.OrdinalIgnoreCase) &&
|
|
!string.Equals(method.Name, "SetValue", StringComparison.OrdinalIgnoreCase) &&
|
|
!method.Name.Contains("GetObject", StringComparison.OrdinalIgnoreCase) &&
|
|
!method.Name.Contains("QueryObjectInfos", StringComparison.OrdinalIgnoreCase) &&
|
|
!method.Name.Contains("GetCount", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var parameters = string.Join(
|
|
", ",
|
|
method.GetParameters().Select(parameter => $"{parameter.ParameterType.Name} {parameter.Name}"));
|
|
Console.WriteLine($"- {method.ReturnType.Name} {method.Name}({parameters})");
|
|
}
|
|
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static void PrintStructFields(Type type)
|
|
{
|
|
Console.WriteLine(type.FullName);
|
|
foreach (var field in type.GetFields())
|
|
{
|
|
Console.WriteLine($"- {field.FieldType.Name} {field.Name}");
|
|
}
|
|
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static void PrintEnumValues(Type type)
|
|
{
|
|
Console.WriteLine(type.FullName);
|
|
foreach (var name in Enum.GetNames(type))
|
|
{
|
|
Console.WriteLine($"- {name}");
|
|
}
|
|
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static async Task<bool> ProbeRawTcpAsync(ProbeOptions options)
|
|
{
|
|
try
|
|
{
|
|
using var client = new TcpClient();
|
|
using var cts = new CancellationTokenSource(options.Timeout);
|
|
await client.ConnectAsync(options.Host, options.Port, cts.Token).ConfigureAwait(false);
|
|
Console.WriteLine($"[RAW] Connected local={client.Client.LocalEndPoint} remote={client.Client.RemoteEndPoint}");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[RAW] Failed: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static Task<CounterProbeResult> ProbeCounterAsync(CounterProbeOptions options)
|
|
{
|
|
var completion = new TaskCompletionSource<CounterProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
var thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
var handler = new ProbeEventHandler();
|
|
var engine = (IKAEngine)new KAEngineClass();
|
|
|
|
Console.WriteLine("[COUNTER] Calling Connect()...");
|
|
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
|
|
Console.WriteLine($"[COUNTER] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
|
|
if (connectRequested == 0)
|
|
{
|
|
completion.TrySetResult(new CounterProbeResult(false, "NOT_RUN", "NOT_RUN", "Connect() returned 0."));
|
|
return;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
|
|
{
|
|
completion.TrySetResult(new CounterProbeResult(true, "NOT_RUN", "TIMEOUT", "OnConnect timed out."));
|
|
return;
|
|
}
|
|
|
|
if (handler.ConnectTask.Result != 0)
|
|
{
|
|
completion.TrySetResult(
|
|
new CounterProbeResult(true, "NOT_RUN", "FAILED", $"OnConnect errorCode={handler.ConnectTask.Result}"));
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine("[COUNTER] Loading scene...");
|
|
var scene = engine.LoadScene(options.ScenePath, options.SceneAlias);
|
|
if (scene is null)
|
|
{
|
|
completion.TrySetResult(new CounterProbeResult(true, "FAILED", "NOT_RUN", "LoadScene returned null."));
|
|
return;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
|
|
{
|
|
completion.TrySetResult(new CounterProbeResult(true, "TIMEOUT", "NOT_RUN", "OnLoadScene timed out."));
|
|
return;
|
|
}
|
|
|
|
var loadSceneResult = handler.LoadSceneTask.Result;
|
|
if (loadSceneResult != eKResult.RESULT_SUCCESS)
|
|
{
|
|
completion.TrySetResult(
|
|
new CounterProbeResult(true, loadSceneResult.ToString(), "NOT_RUN", $"OnLoadScene result={loadSceneResult}"));
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine("[COUNTER] Resolving object...");
|
|
var sceneObject = scene.GetObject(options.ObjectName);
|
|
if (sceneObject is not IKACounter counter)
|
|
{
|
|
completion.TrySetResult(
|
|
new CounterProbeResult(true, "SUCCESS", "FAILED", $"Object '{options.ObjectName}' is not an IKACounter."));
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine("[COUNTER] Calling SetCounterNumberKey()...");
|
|
counter.SetCounterNumberKey(options.KeyIndex, options.Number);
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, options.Connection.Timeout))
|
|
{
|
|
completion.TrySetResult(
|
|
new CounterProbeResult(true, "SUCCESS", "TIMEOUT", "OnSetCounterNumberKey timed out."));
|
|
return;
|
|
}
|
|
|
|
var counterResult = handler.CounterNumberKeyTask.Result;
|
|
completion.TrySetResult(
|
|
new CounterProbeResult(
|
|
true,
|
|
"SUCCESS",
|
|
counterResult == eKResult.RESULT_SUCCESS ? "SUCCESS" : counterResult.ToString(),
|
|
$"OnSetCounterNumberKey result={counterResult}"));
|
|
|
|
try
|
|
{
|
|
engine.Disconnect();
|
|
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
completion.TrySetResult(new CounterProbeResult(false, "EXCEPTION", "EXCEPTION", ex.ToString()));
|
|
}
|
|
})
|
|
{
|
|
IsBackground = true,
|
|
Name = "KarismaCounterProbe"
|
|
};
|
|
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
thread.Start();
|
|
return completion.Task;
|
|
}
|
|
|
|
static Task<SceneCatalogProbeResult> CatalogScenesAsync(SceneCatalogOptions options)
|
|
{
|
|
var completion = new TaskCompletionSource<SceneCatalogProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
var thread = new Thread(() =>
|
|
{
|
|
var entries = new List<SceneCatalogEntry>();
|
|
var failures = new List<SceneCatalogFailure>();
|
|
|
|
try
|
|
{
|
|
var handler = new ProbeEventHandler();
|
|
var engine = (IKAEngine)new KAEngineClass();
|
|
|
|
Console.WriteLine("[CATALOG] Calling Connect()...");
|
|
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
|
|
Console.WriteLine($"[CATALOG] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
|
|
if (connectRequested == 0)
|
|
{
|
|
completion.TrySetResult(new SceneCatalogProbeResult(false, 0, 0, options.OutputPath, failures));
|
|
return;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
|
|
{
|
|
failures.Add(new SceneCatalogFailure("(connect)", "OnConnect timed out."));
|
|
completion.TrySetResult(new SceneCatalogProbeResult(true, 0, 0, options.OutputPath, failures));
|
|
return;
|
|
}
|
|
|
|
if (handler.ConnectTask.Result != 0)
|
|
{
|
|
failures.Add(new SceneCatalogFailure("(connect)", $"OnConnect errorCode={handler.ConnectTask.Result}"));
|
|
completion.TrySetResult(new SceneCatalogProbeResult(true, 0, 0, options.OutputPath, failures));
|
|
return;
|
|
}
|
|
|
|
var scenePaths = Directory
|
|
.EnumerateFiles(options.RootPath, "*.tscn", SearchOption.AllDirectories)
|
|
.Where(path => string.IsNullOrWhiteSpace(options.SceneFilter) ||
|
|
path.Contains(options.SceneFilter, StringComparison.OrdinalIgnoreCase))
|
|
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
|
|
.Take(options.MaxScenes ?? int.MaxValue)
|
|
.ToArray();
|
|
|
|
for (var index = 0; index < scenePaths.Length; index++)
|
|
{
|
|
var scenePath = scenePaths[index];
|
|
var sceneAlias = $"catalog_{index:D4}";
|
|
Console.WriteLine($"[CATALOG] ({index + 1}/{scenePaths.Length}) {scenePath}");
|
|
|
|
try
|
|
{
|
|
handler.ResetLoadSceneTask();
|
|
handler.ResetObjectInfosTask();
|
|
var scene = engine.LoadScene(scenePath, sceneAlias);
|
|
if (scene is null)
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, "LoadScene returned null."));
|
|
continue;
|
|
}
|
|
|
|
handler.ConfigureQueryObjectInfosOnLoad(scene);
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, "OnLoadScene timed out."));
|
|
continue;
|
|
}
|
|
|
|
if (handler.LoadSceneTask.Result != eKResult.RESULT_SUCCESS)
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, $"OnLoadScene result={handler.LoadSceneTask.Result}"));
|
|
TryUnloadScene(handler, scene, options.Connection.Timeout);
|
|
continue;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.ObjectInfosTask, options.Connection.Timeout))
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, "OnQueryObjectInfos timed out."));
|
|
TryUnloadScene(handler, scene, options.Connection.Timeout);
|
|
continue;
|
|
}
|
|
|
|
var objectInfosResult = handler.ObjectInfosTask.Result;
|
|
if (objectInfosResult.Result != eKResult.RESULT_SUCCESS)
|
|
{
|
|
var reason = string.IsNullOrWhiteSpace(objectInfosResult.Detail)
|
|
? $"OnQueryObjectInfos result={objectInfosResult.Result}"
|
|
: $"OnQueryObjectInfos result={objectInfosResult.Result} detail={objectInfosResult.Detail}";
|
|
failures.Add(new SceneCatalogFailure(scenePath, reason));
|
|
TryUnloadScene(handler, scene, options.Connection.Timeout);
|
|
continue;
|
|
}
|
|
|
|
entries.Add(new SceneCatalogEntry(
|
|
scenePath,
|
|
Path.GetRelativePath(options.RootPath, scenePath),
|
|
objectInfosResult.Objects));
|
|
|
|
TryUnloadScene(handler, scene, options.Connection.Timeout);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, ex.Message));
|
|
}
|
|
}
|
|
|
|
WriteCatalogMarkdown(options, entries, failures);
|
|
completion.TrySetResult(new SceneCatalogProbeResult(true, scenePaths.Length, entries.Count, options.OutputPath, failures));
|
|
|
|
try
|
|
{
|
|
engine.Disconnect();
|
|
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
failures.Add(new SceneCatalogFailure("(exception)", ex.ToString()));
|
|
completion.TrySetResult(new SceneCatalogProbeResult(false, entries.Count + failures.Count, entries.Count, options.OutputPath, failures));
|
|
}
|
|
})
|
|
{
|
|
IsBackground = true,
|
|
Name = "KarismaSceneCatalog"
|
|
};
|
|
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
thread.Start();
|
|
return completion.Task;
|
|
}
|
|
|
|
static Task<SceneValidationProbeResult> ValidateSceneOperationsAsync(SceneValidationOptions options)
|
|
{
|
|
var completion = new TaskCompletionSource<SceneValidationProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
var operations = LoadValidationOperations(options);
|
|
|
|
var thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
var handler = new ProbeEventHandler();
|
|
var engine = (IKAEngine)new KAEngineClass();
|
|
var results = new List<SceneOperationValidationResult>();
|
|
|
|
Console.WriteLine("[VALIDATE] Calling Connect()...");
|
|
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
|
|
Console.WriteLine($"[VALIDATE] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
|
|
if (connectRequested == 0)
|
|
{
|
|
completion.TrySetResult(new SceneValidationProbeResult(false, "NOT_RUN", 0, operations.Count, options.OutputPath, "Connect() returned 0."));
|
|
return;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
|
|
{
|
|
completion.TrySetResult(new SceneValidationProbeResult(true, "NOT_RUN", 0, operations.Count, options.OutputPath, "OnConnect timed out."));
|
|
return;
|
|
}
|
|
|
|
if (handler.ConnectTask.Result != 0)
|
|
{
|
|
completion.TrySetResult(new SceneValidationProbeResult(true, "NOT_RUN", 0, operations.Count, options.OutputPath, $"OnConnect errorCode={handler.ConnectTask.Result}"));
|
|
return;
|
|
}
|
|
|
|
handler.ResetLoadSceneTask();
|
|
var scene = engine.LoadScene(options.ScenePath, options.SceneAlias);
|
|
if (scene is null)
|
|
{
|
|
completion.TrySetResult(new SceneValidationProbeResult(true, "FAILED", 0, operations.Count, options.OutputPath, "LoadScene returned null."));
|
|
return;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
|
|
{
|
|
completion.TrySetResult(new SceneValidationProbeResult(true, "TIMEOUT", 0, operations.Count, options.OutputPath, "OnLoadScene timed out."));
|
|
return;
|
|
}
|
|
|
|
var loadSceneResult = handler.LoadSceneTask.Result;
|
|
if (loadSceneResult != eKResult.RESULT_SUCCESS)
|
|
{
|
|
completion.TrySetResult(
|
|
new SceneValidationProbeResult(true, loadSceneResult.ToString(), 0, operations.Count, options.OutputPath, $"OnLoadScene result={loadSceneResult}"));
|
|
return;
|
|
}
|
|
|
|
foreach (var operation in operations)
|
|
{
|
|
Console.WriteLine($"[VALIDATE] {operation.Method} object={operation.ObjectName}");
|
|
var sceneObject = scene.GetObject(operation.ObjectName);
|
|
if (sceneObject is null)
|
|
{
|
|
results.Add(new SceneOperationValidationResult(
|
|
operation.ObjectName,
|
|
operation.Method,
|
|
DescribeOperationPayload(operation),
|
|
"OBJECT_NOT_FOUND",
|
|
"scene.GetObject returned null."));
|
|
continue;
|
|
}
|
|
|
|
if (string.Equals(operation.Method, "SetCounterNumberKey", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (sceneObject is not IKACounter counter)
|
|
{
|
|
results.Add(new SceneOperationValidationResult(
|
|
operation.ObjectName,
|
|
operation.Method,
|
|
DescribeOperationPayload(operation),
|
|
"NOT_A_COUNTER",
|
|
"Object does not implement IKACounter."));
|
|
continue;
|
|
}
|
|
|
|
handler.ResetCounterNumberKeyTask();
|
|
counter.SetCounterNumberKey(operation.KeyIndex, operation.Number);
|
|
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, options.Connection.Timeout))
|
|
{
|
|
results.Add(new SceneOperationValidationResult(
|
|
operation.ObjectName,
|
|
operation.Method,
|
|
DescribeOperationPayload(operation),
|
|
"TIMEOUT",
|
|
"OnSetCounterNumberKey timed out."));
|
|
continue;
|
|
}
|
|
|
|
var callbackResult = handler.CounterNumberKeyTask.Result;
|
|
results.Add(new SceneOperationValidationResult(
|
|
operation.ObjectName,
|
|
operation.Method,
|
|
DescribeOperationPayload(operation),
|
|
callbackResult.ToString(),
|
|
string.Empty));
|
|
continue;
|
|
}
|
|
|
|
handler.ResetSetValueTask();
|
|
sceneObject.SetValue(operation.Value ?? string.Empty);
|
|
if (!WaitForTaskWithMessagePump(handler.SetValueTask, options.Connection.Timeout))
|
|
{
|
|
results.Add(new SceneOperationValidationResult(
|
|
operation.ObjectName,
|
|
operation.Method,
|
|
DescribeOperationPayload(operation),
|
|
"TIMEOUT",
|
|
"OnSetValue timed out."));
|
|
continue;
|
|
}
|
|
|
|
var setValueResult = handler.SetValueTask.Result;
|
|
results.Add(new SceneOperationValidationResult(
|
|
operation.ObjectName,
|
|
operation.Method,
|
|
DescribeOperationPayload(operation),
|
|
setValueResult.ToString(),
|
|
string.Empty));
|
|
}
|
|
|
|
WriteSceneValidationMarkdown(options, results);
|
|
|
|
try
|
|
{
|
|
scene.UnloadScene();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
try
|
|
{
|
|
engine.Disconnect();
|
|
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
completion.TrySetResult(new SceneValidationProbeResult(
|
|
true,
|
|
"SUCCESS",
|
|
results.Count(result => string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal)),
|
|
results.Count(result => !string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal)),
|
|
options.OutputPath,
|
|
string.Empty));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
completion.TrySetResult(new SceneValidationProbeResult(false, "EXCEPTION", 0, operations.Count, options.OutputPath, ex.ToString()));
|
|
}
|
|
})
|
|
{
|
|
IsBackground = true,
|
|
Name = "KarismaSceneValidator"
|
|
};
|
|
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
thread.Start();
|
|
return completion.Task;
|
|
}
|
|
|
|
static Task<FolderInspectionProbeResult> InspectTscnFolderAsync(FolderInspectionOptions options)
|
|
{
|
|
var completion = new TaskCompletionSource<FolderInspectionProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
var thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
var handler = new ProbeEventHandler();
|
|
var engine = (IKAEngine)new KAEngineClass();
|
|
var sceneResults = new List<SceneInspectionResult>();
|
|
var failures = new List<SceneCatalogFailure>();
|
|
|
|
var imagePngPath = FindFirstFile(options.RootPath, "*.png");
|
|
var imageVrvPath = FindFirstFile(options.RootPath, "*.vrv");
|
|
Console.WriteLine($"[INSPECT] Sample PNG={(imagePngPath ?? "(none)")}");
|
|
Console.WriteLine($"[INSPECT] Sample VRV={(imageVrvPath ?? "(none)")}");
|
|
|
|
Console.WriteLine("[INSPECT] Calling Connect()...");
|
|
var connectRequested = engine.Connect(options.Connection.Host, options.Connection.Port, handler);
|
|
Console.WriteLine($"[INSPECT] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
|
|
if (connectRequested == 0)
|
|
{
|
|
completion.TrySetResult(new FolderInspectionProbeResult(false, 0, 0, 0, 1, options.OutputPath, "Connect() returned 0."));
|
|
return;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Connection.Timeout))
|
|
{
|
|
completion.TrySetResult(new FolderInspectionProbeResult(true, 0, 0, 0, 1, options.OutputPath, "OnConnect timed out."));
|
|
return;
|
|
}
|
|
|
|
if (handler.ConnectTask.Result != 0)
|
|
{
|
|
completion.TrySetResult(new FolderInspectionProbeResult(true, 0, 0, 0, 1, options.OutputPath, $"OnConnect errorCode={handler.ConnectTask.Result}"));
|
|
return;
|
|
}
|
|
|
|
var scenePaths = Directory
|
|
.EnumerateFiles(options.RootPath, "*.tscn", SearchOption.AllDirectories)
|
|
.Where(path => string.IsNullOrWhiteSpace(options.SceneFilter) ||
|
|
path.Contains(options.SceneFilter, StringComparison.OrdinalIgnoreCase))
|
|
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
|
|
.Take(options.MaxScenes ?? int.MaxValue)
|
|
.ToArray();
|
|
|
|
for (var sceneIndex = 0; sceneIndex < scenePaths.Length; sceneIndex++)
|
|
{
|
|
var scenePath = scenePaths[sceneIndex];
|
|
var sceneAlias = $"inspect_{sceneIndex:D4}";
|
|
Console.WriteLine($"[INSPECT] ({sceneIndex + 1}/{scenePaths.Length}) {scenePath}");
|
|
|
|
try
|
|
{
|
|
handler.ResetLoadSceneTask();
|
|
var scene = engine.LoadScene(scenePath, sceneAlias);
|
|
if (scene is null)
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, "LoadScene returned null."));
|
|
continue;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.LoadSceneTask, options.Connection.Timeout))
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, "OnLoadScene timed out."));
|
|
TryUnloadScene(handler, scene, options.Connection.Timeout);
|
|
continue;
|
|
}
|
|
|
|
if (handler.LoadSceneTask.Result != eKResult.RESULT_SUCCESS)
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, $"OnLoadScene result={handler.LoadSceneTask.Result}"));
|
|
TryUnloadScene(handler, scene, options.Connection.Timeout);
|
|
continue;
|
|
}
|
|
|
|
var candidates = ExtractCandidateNames(scenePath);
|
|
var discoveries = new List<SceneVariableDiscovery>();
|
|
|
|
foreach (var candidate in candidates)
|
|
{
|
|
KAObject? sceneObject;
|
|
try
|
|
{
|
|
sceneObject = scene.GetObject(candidate);
|
|
}
|
|
catch
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (sceneObject is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var textAttempt = TrySetValueOnObject(handler, sceneObject, "__TCP_VALIDATE__", options.Connection.Timeout);
|
|
if (textAttempt == eKResult.RESULT_SUCCESS)
|
|
{
|
|
discoveries.Add(new SceneVariableDiscovery(candidate, "SetValue", "__TCP_VALIDATE__", textAttempt.ToString()));
|
|
continue;
|
|
}
|
|
|
|
if (textAttempt == eKResult.RESULT_ERROR_NO_VARIABLE_OBJECT)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(imagePngPath))
|
|
{
|
|
var pngAttempt = TrySetValueOnObject(handler, sceneObject, imagePngPath, options.Connection.Timeout);
|
|
if (pngAttempt == eKResult.RESULT_SUCCESS)
|
|
{
|
|
discoveries.Add(new SceneVariableDiscovery(candidate, "SetValue", imagePngPath, pngAttempt.ToString()));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(imageVrvPath))
|
|
{
|
|
var vrvAttempt = TrySetValueOnObject(handler, sceneObject, imageVrvPath, options.Connection.Timeout);
|
|
if (vrvAttempt == eKResult.RESULT_SUCCESS)
|
|
{
|
|
discoveries.Add(new SceneVariableDiscovery(candidate, "SetValue", imageVrvPath, vrvAttempt.ToString()));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (sceneObject is IKACounter counter)
|
|
{
|
|
handler.ResetCounterNumberKeyTask();
|
|
counter.SetCounterNumberKey(1, 1d);
|
|
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, options.Connection.Timeout))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var counterResult = handler.CounterNumberKeyTask.Result;
|
|
if (counterResult == eKResult.RESULT_SUCCESS)
|
|
{
|
|
discoveries.Add(new SceneVariableDiscovery(
|
|
candidate,
|
|
"SetCounterNumberKey",
|
|
"keyIndex=1, number=1",
|
|
counterResult.ToString()));
|
|
}
|
|
}
|
|
}
|
|
|
|
sceneResults.Add(new SceneInspectionResult(
|
|
scenePath,
|
|
Path.GetRelativePath(options.RootPath, scenePath),
|
|
candidates.Count,
|
|
discoveries.OrderBy(discovery => discovery.VariableName, StringComparer.Ordinal).ToArray()));
|
|
|
|
TryUnloadScene(handler, scene, options.Connection.Timeout);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
failures.Add(new SceneCatalogFailure(scenePath, ex.Message));
|
|
}
|
|
}
|
|
|
|
WriteFolderInspectionMarkdown(options, sceneResults, failures);
|
|
|
|
try
|
|
{
|
|
engine.Disconnect();
|
|
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
completion.TrySetResult(new FolderInspectionProbeResult(
|
|
true,
|
|
scenePaths.Length,
|
|
sceneResults.Count,
|
|
sceneResults.Sum(result => result.Discoveries.Count),
|
|
failures.Count,
|
|
options.OutputPath,
|
|
string.Empty));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
completion.TrySetResult(new FolderInspectionProbeResult(false, 0, 0, 0, 1, options.OutputPath, ex.ToString()));
|
|
}
|
|
})
|
|
{
|
|
IsBackground = true,
|
|
Name = "KarismaTscnInspector"
|
|
};
|
|
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
thread.Start();
|
|
return completion.Task;
|
|
}
|
|
|
|
static eKResult TrySetValueOnObject(ProbeEventHandler handler, KAObject sceneObject, string payload, TimeSpan timeout)
|
|
{
|
|
handler.ResetSetValueTask();
|
|
sceneObject.SetValue(payload);
|
|
return WaitForTaskWithMessagePump(handler.SetValueTask, timeout)
|
|
? handler.SetValueTask.Result
|
|
: eKResult.RESULT_FAILURE;
|
|
}
|
|
|
|
static string? FindFirstFile(string rootPath, string pattern)
|
|
{
|
|
try
|
|
{
|
|
return Directory.EnumerateFiles(rootPath, pattern, SearchOption.AllDirectories)
|
|
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
|
|
.FirstOrDefault();
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static IReadOnlyList<string> ExtractCandidateNames(string scenePath)
|
|
{
|
|
var data = File.ReadAllBytes(scenePath);
|
|
var candidates = new HashSet<string>(StringComparer.Ordinal);
|
|
for (var index = 0; index + 8 <= data.Length; index++)
|
|
{
|
|
var length = BitConverter.ToInt32(data, index);
|
|
if (length < 2 || length > 48)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var byteLength = length * 2;
|
|
var start = index + 4;
|
|
var end = start + byteLength;
|
|
if (end > data.Length)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string value;
|
|
try
|
|
{
|
|
value = System.Text.Encoding.Unicode.GetString(data, start, byteLength);
|
|
}
|
|
catch
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (IsCandidateName(value))
|
|
{
|
|
candidates.Add(value);
|
|
}
|
|
}
|
|
|
|
return candidates.OrderBy(value => value, StringComparer.Ordinal).ToArray();
|
|
}
|
|
|
|
static bool IsCandidateName(string value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var trimmed = value.Trim();
|
|
if (trimmed.Length < 2 || trimmed.Length > 48)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (trimmed.Contains('\\') ||
|
|
trimmed.Contains('/') ||
|
|
trimmed.Contains(':') ||
|
|
trimmed.Contains('.') ||
|
|
trimmed.Contains(' '))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (trimmed.All(char.IsDigit))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (trimmed.StartsWith("OBJECT_TYPE_", StringComparison.Ordinal) ||
|
|
trimmed.StartsWith("RESULT_", StringComparison.Ordinal) ||
|
|
trimmed.StartsWith("EFFECT_", StringComparison.Ordinal))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (trimmed is "Tornado3" or "Out" or "Default" or "Image" or "Text" or "Counter" or
|
|
"Rect" or "Paragraph" or "Char" or "Arial" or "Path" or "Group" or "Change")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return trimmed.All(ch =>
|
|
ch is >= '0' and <= '9' ||
|
|
ch is >= 'A' and <= 'Z' ||
|
|
ch is >= 'a' and <= 'z' ||
|
|
ch is >= '\uAC00' and <= '\uD7A3' ||
|
|
ch is '_' or '-');
|
|
}
|
|
|
|
static void WriteFolderInspectionMarkdown(
|
|
FolderInspectionOptions options,
|
|
IReadOnlyList<SceneInspectionResult> sceneResults,
|
|
IReadOnlyList<SceneCatalogFailure> failures)
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(options.OutputPath)!);
|
|
|
|
using var writer = new StreamWriter(options.OutputPath, false, new System.Text.UTF8Encoding(false));
|
|
writer.WriteLine("# TSCN Variable Discovery");
|
|
writer.WriteLine();
|
|
writer.WriteLine($"- Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
|
writer.WriteLine($"- Root: `{options.RootPath}`");
|
|
writer.WriteLine($"- Scene Count: {sceneResults.Count}");
|
|
writer.WriteLine($"- Discovered Variable Count: {sceneResults.Sum(result => result.Discoveries.Count)}");
|
|
writer.WriteLine($"- Failure Count: {failures.Count}");
|
|
writer.WriteLine();
|
|
writer.WriteLine("## Method");
|
|
writer.WriteLine();
|
|
writer.WriteLine("- Candidate names are extracted from each `.tscn` as UTF-16LE strings.");
|
|
writer.WriteLine("- Each candidate is verified through Karisma TCP callbacks.");
|
|
writer.WriteLine("- `SetValue(__TCP_VALIDATE__)`, valid `.png`, valid `.vrv`, and `SetCounterNumberKey(1, 1)` are tried as applicable.");
|
|
writer.WriteLine("- Only callbacks that returned `RESULT_SUCCESS` are listed as discovered variables.");
|
|
writer.WriteLine();
|
|
|
|
if (failures.Count > 0)
|
|
{
|
|
writer.WriteLine("## Failures");
|
|
writer.WriteLine();
|
|
foreach (var failure in failures)
|
|
{
|
|
writer.WriteLine($"- `{failure.ScenePath}`: {EscapeInline(failure.Reason)}");
|
|
}
|
|
|
|
writer.WriteLine();
|
|
}
|
|
|
|
writer.WriteLine("## Scenes");
|
|
writer.WriteLine();
|
|
foreach (var sceneResult in sceneResults)
|
|
{
|
|
writer.WriteLine($"### `{sceneResult.RelativePath}`");
|
|
writer.WriteLine();
|
|
writer.WriteLine($"- Candidate Count: {sceneResult.CandidateCount}");
|
|
writer.WriteLine($"- Discovered Variables: {sceneResult.Discoveries.Count}");
|
|
writer.WriteLine();
|
|
|
|
if (sceneResult.Discoveries.Count == 0)
|
|
{
|
|
writer.WriteLine("- No variables discovered with the current TCP validation heuristics.");
|
|
writer.WriteLine();
|
|
continue;
|
|
}
|
|
|
|
writer.WriteLine("| Variable | Method | Payload | Result |");
|
|
writer.WriteLine("| --- | --- | --- | --- |");
|
|
foreach (var discovery in sceneResult.Discoveries)
|
|
{
|
|
writer.WriteLine(
|
|
$"| {EscapeCell(discovery.VariableName)} | {EscapeCell(discovery.Method)} | {EscapeCell(discovery.Payload)} | {EscapeCell(discovery.Result)} |");
|
|
}
|
|
|
|
writer.WriteLine();
|
|
}
|
|
}
|
|
|
|
static List<SceneValidationOperation> LoadValidationOperations(SceneValidationOptions options)
|
|
{
|
|
var json = File.ReadAllText(options.OperationsPath);
|
|
var operations = JsonSerializer.Deserialize<List<SceneValidationOperation>>(json, new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true
|
|
}) ?? new List<SceneValidationOperation>();
|
|
|
|
foreach (var operation in operations)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(operation.Value))
|
|
{
|
|
operation.Value = operation.Value.Replace("${SCENE_DIR}", Path.GetDirectoryName(options.ScenePath) ?? string.Empty, StringComparison.Ordinal);
|
|
}
|
|
}
|
|
|
|
return operations;
|
|
}
|
|
|
|
static string DescribeOperationPayload(SceneValidationOperation operation)
|
|
{
|
|
return string.Equals(operation.Method, "SetCounterNumberKey", StringComparison.OrdinalIgnoreCase)
|
|
? $"keyIndex={operation.KeyIndex}, number={operation.Number:0.###}"
|
|
: operation.Value ?? string.Empty;
|
|
}
|
|
|
|
static void WriteSceneValidationMarkdown(SceneValidationOptions options, IReadOnlyList<SceneOperationValidationResult> results)
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(options.OutputPath)!);
|
|
|
|
using var writer = new StreamWriter(options.OutputPath, false, new System.Text.UTF8Encoding(false));
|
|
writer.WriteLine("# Scene Variable Validation");
|
|
writer.WriteLine();
|
|
writer.WriteLine($"- Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
|
writer.WriteLine($"- Scene: `{options.ScenePath}`");
|
|
writer.WriteLine($"- Operations: `{options.OperationsPath}`");
|
|
writer.WriteLine($"- Success Count: {results.Count(result => string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal))}");
|
|
writer.WriteLine($"- Failure Count: {results.Count(result => !string.Equals(result.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal))}");
|
|
writer.WriteLine();
|
|
writer.WriteLine("| Object | Method | Payload | Result | Detail |");
|
|
writer.WriteLine("| --- | --- | --- | --- | --- |");
|
|
foreach (var result in results)
|
|
{
|
|
writer.WriteLine(
|
|
$"| {EscapeCell(result.ObjectName)} | {EscapeCell(result.Method)} | {EscapeCell(result.Payload)} | {EscapeCell(result.Result)} | {EscapeCell(result.Detail)} |");
|
|
}
|
|
}
|
|
|
|
static void TryUnloadScene(ProbeEventHandler handler, IKAScene scene, TimeSpan timeout)
|
|
{
|
|
try
|
|
{
|
|
handler.ResetUnloadSceneTask();
|
|
scene.UnloadScene();
|
|
WaitForTaskWithMessagePump(handler.UnloadSceneTask, timeout);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
static void WriteCatalogMarkdown(
|
|
SceneCatalogOptions options,
|
|
IReadOnlyList<SceneCatalogEntry> entries,
|
|
IReadOnlyList<SceneCatalogFailure> failures)
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(options.OutputPath)!);
|
|
|
|
using var writer = new StreamWriter(options.OutputPath, false, new System.Text.UTF8Encoding(false));
|
|
writer.WriteLine("# Scene Object Catalog");
|
|
writer.WriteLine();
|
|
writer.WriteLine($"- Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
|
writer.WriteLine($"- Root: `{options.RootPath}`");
|
|
writer.WriteLine($"- Scene Count: {entries.Count}");
|
|
writer.WriteLine($"- Failure Count: {failures.Count}");
|
|
writer.WriteLine();
|
|
|
|
if (failures.Count > 0)
|
|
{
|
|
writer.WriteLine("## Failures");
|
|
writer.WriteLine();
|
|
foreach (var failure in failures)
|
|
{
|
|
writer.WriteLine($"- `{failure.ScenePath}`: {EscapeInline(failure.Reason)}");
|
|
}
|
|
|
|
writer.WriteLine();
|
|
}
|
|
|
|
writer.WriteLine("## Scenes");
|
|
writer.WriteLine();
|
|
foreach (var entry in entries)
|
|
{
|
|
writer.WriteLine($"### `{entry.RelativePath}`");
|
|
writer.WriteLine();
|
|
writer.WriteLine($"- Object Count: {entry.Objects.Count}");
|
|
writer.WriteLine();
|
|
writer.WriteLine("| Name | Type | Value | Visible |");
|
|
writer.WriteLine("| --- | --- | --- | --- |");
|
|
foreach (var obj in entry.Objects)
|
|
{
|
|
writer.WriteLine(
|
|
$"| {EscapeCell(obj.Name)} | {obj.ObjectType} | {EscapeCell(obj.Value)} | {(obj.Visible ? "true" : "false")} |");
|
|
}
|
|
|
|
writer.WriteLine();
|
|
}
|
|
}
|
|
|
|
static string EscapeCell(string? value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return value.Replace("\\", "\\\\", StringComparison.Ordinal)
|
|
.Replace("|", "\\|", StringComparison.Ordinal)
|
|
.Replace("\r", " ", StringComparison.Ordinal)
|
|
.Replace("\n", "<br/>", StringComparison.Ordinal);
|
|
}
|
|
|
|
static string EscapeInline(string? value) => EscapeCell(value);
|
|
|
|
static Task<SdkProbeResult> ProbeSdkAsync(ProbeOptions options)
|
|
{
|
|
var completion = new TaskCompletionSource<SdkProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
var thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
var handler = new ProbeEventHandler();
|
|
var engine = (IKAEngine)new KAEngineClass();
|
|
|
|
Console.WriteLine("[SDK] Calling Connect()...");
|
|
var connectRequested = engine.Connect(options.Host, options.Port, handler);
|
|
Console.WriteLine($"[SDK] Connect() returned {(connectRequested != 0 ? "TRUE" : "FALSE")} raw={connectRequested}");
|
|
|
|
if (connectRequested == 0)
|
|
{
|
|
completion.TrySetResult(new SdkProbeResult(false, "FAILED", "Connect() returned 0."));
|
|
return;
|
|
}
|
|
|
|
if (!WaitForTaskWithMessagePump(handler.ConnectTask, options.Timeout))
|
|
{
|
|
completion.TrySetResult(new SdkProbeResult(true, "TIMEOUT", $"OnConnect was not received within {options.Timeout.TotalSeconds:0} seconds."));
|
|
return;
|
|
}
|
|
|
|
var errorCode = handler.ConnectTask.Result;
|
|
var outcome = errorCode == 0 ? "SUCCESS" : "FAILED";
|
|
completion.TrySetResult(new SdkProbeResult(true, outcome, $"OnConnect errorCode={errorCode}"));
|
|
|
|
try
|
|
{
|
|
engine.Disconnect();
|
|
handler.CloseTask.Wait(TimeSpan.FromSeconds(2));
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
completion.TrySetResult(new SdkProbeResult(false, "EXCEPTION", ex.ToString()));
|
|
}
|
|
})
|
|
{
|
|
IsBackground = true,
|
|
Name = "KarismaTcpProbe"
|
|
};
|
|
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
thread.Start();
|
|
return completion.Task;
|
|
}
|
|
|
|
static bool WaitForTaskWithMessagePump(Task task, TimeSpan timeout)
|
|
{
|
|
User32.PeekMessage(out _, IntPtr.Zero, 0, 0, 0);
|
|
|
|
var deadline = DateTime.UtcNow + timeout;
|
|
while (!task.IsCompleted)
|
|
{
|
|
while (User32.PeekMessage(out var message, IntPtr.Zero, 0, 0, 1))
|
|
{
|
|
User32.TranslateMessage(ref message);
|
|
User32.DispatchMessage(ref message);
|
|
}
|
|
|
|
if (DateTime.UtcNow >= deadline)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Thread.Sleep(10);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal sealed record ProbeOptions(string Host, int Port, TimeSpan Timeout)
|
|
{
|
|
public static ProbeOptions Parse(string[] args)
|
|
{
|
|
var host = "127.0.0.1";
|
|
var port = 30001;
|
|
var timeout = TimeSpan.FromSeconds(5);
|
|
|
|
for (var index = 0; index < args.Length; index++)
|
|
{
|
|
switch (args[index])
|
|
{
|
|
case "--host" when index + 1 < args.Length:
|
|
host = args[++index];
|
|
break;
|
|
case "--port" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedPort):
|
|
port = parsedPort;
|
|
index++;
|
|
break;
|
|
case "--timeout" when index + 1 < args.Length && double.TryParse(args[index + 1], out var parsedTimeoutSeconds):
|
|
timeout = TimeSpan.FromSeconds(parsedTimeoutSeconds);
|
|
index++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return new ProbeOptions(host, port, timeout);
|
|
}
|
|
}
|
|
|
|
internal sealed record CounterProbeOptions(
|
|
ProbeOptions Connection,
|
|
string ScenePath,
|
|
string SceneAlias,
|
|
string ObjectName,
|
|
int KeyIndex,
|
|
double Number)
|
|
{
|
|
public static CounterProbeOptions Parse(string[] args)
|
|
{
|
|
var connection = ProbeOptions.Parse(args);
|
|
string? scenePath = null;
|
|
string? sceneAlias = null;
|
|
string? objectName = null;
|
|
var keyIndex = 1;
|
|
var number = 0d;
|
|
|
|
for (var index = 0; index < args.Length; index++)
|
|
{
|
|
switch (args[index])
|
|
{
|
|
case "--scene" when index + 1 < args.Length:
|
|
scenePath = args[++index];
|
|
break;
|
|
case "--alias" when index + 1 < args.Length:
|
|
sceneAlias = args[++index];
|
|
break;
|
|
case "--object" when index + 1 < args.Length:
|
|
objectName = args[++index];
|
|
break;
|
|
case "--key-index" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedKeyIndex):
|
|
keyIndex = parsedKeyIndex;
|
|
index++;
|
|
break;
|
|
case "--number" when index + 1 < args.Length && double.TryParse(args[index + 1], out var parsedNumber):
|
|
number = parsedNumber;
|
|
index++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(scenePath))
|
|
{
|
|
throw new ArgumentException("--scene is required.");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(objectName))
|
|
{
|
|
throw new ArgumentException("--object is required.");
|
|
}
|
|
|
|
sceneAlias ??= Path.GetFileNameWithoutExtension(scenePath);
|
|
return new CounterProbeOptions(connection, scenePath, sceneAlias, objectName, keyIndex, number);
|
|
}
|
|
}
|
|
|
|
internal sealed record SceneCatalogOptions(
|
|
ProbeOptions Connection,
|
|
string RootPath,
|
|
string OutputPath,
|
|
string? SceneFilter,
|
|
int? MaxScenes)
|
|
{
|
|
public static SceneCatalogOptions Parse(string[] args)
|
|
{
|
|
var connection = ProbeOptions.Parse(args);
|
|
string? rootPath = null;
|
|
string? outputPath = null;
|
|
string? sceneFilter = null;
|
|
int? maxScenes = null;
|
|
|
|
for (var index = 0; index < args.Length; index++)
|
|
{
|
|
switch (args[index])
|
|
{
|
|
case "--root" when index + 1 < args.Length:
|
|
rootPath = args[++index];
|
|
break;
|
|
case "--output" when index + 1 < args.Length:
|
|
outputPath = args[++index];
|
|
break;
|
|
case "--filter" when index + 1 < args.Length:
|
|
sceneFilter = args[++index];
|
|
break;
|
|
case "--max-scenes" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedMaxScenes):
|
|
maxScenes = parsedMaxScenes;
|
|
index++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rootPath ??= Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
|
|
"Tornado3 Data",
|
|
"T3_Cut",
|
|
"T3_Cut");
|
|
|
|
if (!Directory.Exists(rootPath))
|
|
{
|
|
throw new DirectoryNotFoundException($"Catalog root path does not exist: {rootPath}");
|
|
}
|
|
|
|
outputPath ??= Path.Combine(Environment.CurrentDirectory, "SCENE_OBJECT_CATALOG.md");
|
|
outputPath = Path.GetFullPath(outputPath);
|
|
return new SceneCatalogOptions(connection, Path.GetFullPath(rootPath), outputPath, sceneFilter, maxScenes);
|
|
}
|
|
}
|
|
|
|
internal sealed record SceneValidationOptions(
|
|
ProbeOptions Connection,
|
|
string ScenePath,
|
|
string SceneAlias,
|
|
string OperationsPath,
|
|
string OutputPath)
|
|
{
|
|
public static SceneValidationOptions Parse(string[] args)
|
|
{
|
|
var connection = ProbeOptions.Parse(args);
|
|
string? scenePath = null;
|
|
string? sceneAlias = null;
|
|
string? operationsPath = null;
|
|
string? outputPath = null;
|
|
|
|
for (var index = 0; index < args.Length; index++)
|
|
{
|
|
switch (args[index])
|
|
{
|
|
case "--scene" when index + 1 < args.Length:
|
|
scenePath = args[++index];
|
|
break;
|
|
case "--alias" when index + 1 < args.Length:
|
|
sceneAlias = args[++index];
|
|
break;
|
|
case "--operations" when index + 1 < args.Length:
|
|
operationsPath = args[++index];
|
|
break;
|
|
case "--output" when index + 1 < args.Length:
|
|
outputPath = args[++index];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(scenePath))
|
|
{
|
|
throw new ArgumentException("--scene is required.");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(operationsPath))
|
|
{
|
|
throw new ArgumentException("--operations is required.");
|
|
}
|
|
|
|
scenePath = Path.GetFullPath(scenePath);
|
|
operationsPath = Path.GetFullPath(operationsPath);
|
|
sceneAlias ??= Path.GetFileNameWithoutExtension(scenePath);
|
|
outputPath ??= Path.Combine(Environment.CurrentDirectory, "SCENE_VARIABLE_VALIDATION.md");
|
|
outputPath = Path.GetFullPath(outputPath);
|
|
|
|
return new SceneValidationOptions(connection, scenePath, sceneAlias, operationsPath, outputPath);
|
|
}
|
|
}
|
|
|
|
internal sealed record FolderInspectionOptions(ProbeOptions Connection, string RootPath, string OutputPath, string? SceneFilter, int? MaxScenes)
|
|
{
|
|
public static FolderInspectionOptions Parse(string[] args)
|
|
{
|
|
var connection = ProbeOptions.Parse(args);
|
|
string? rootPath = null;
|
|
string? outputPath = null;
|
|
string? sceneFilter = null;
|
|
int? maxScenes = null;
|
|
|
|
for (var index = 0; index < args.Length; index++)
|
|
{
|
|
switch (args[index])
|
|
{
|
|
case "--root" when index + 1 < args.Length:
|
|
rootPath = args[++index];
|
|
break;
|
|
case "--output" when index + 1 < args.Length:
|
|
outputPath = args[++index];
|
|
break;
|
|
case "--filter" when index + 1 < args.Length:
|
|
sceneFilter = args[++index];
|
|
break;
|
|
case "--max-scenes" when index + 1 < args.Length && int.TryParse(args[index + 1], out var parsedMaxScenes):
|
|
maxScenes = parsedMaxScenes;
|
|
index++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(rootPath))
|
|
{
|
|
throw new ArgumentException("--root is required.");
|
|
}
|
|
|
|
rootPath = Path.GetFullPath(rootPath);
|
|
outputPath ??= Path.Combine(Environment.CurrentDirectory, "TSCN_VARIABLE_DISCOVERY.md");
|
|
outputPath = Path.GetFullPath(outputPath);
|
|
return new FolderInspectionOptions(connection, rootPath, outputPath, sceneFilter, maxScenes);
|
|
}
|
|
}
|
|
|
|
internal sealed record SdkProbeResult(bool ConnectRequestAccepted, string ConnectOutcome, string Detail);
|
|
internal sealed record CounterProbeResult(bool ConnectRequestAccepted, string SceneLoadOutcome, string CounterOutcome, string Detail);
|
|
internal sealed record SceneCatalogProbeResult(
|
|
bool ConnectRequestAccepted,
|
|
int SceneCount,
|
|
int CatalogedCount,
|
|
string OutputPath,
|
|
IReadOnlyList<SceneCatalogFailure> Failures);
|
|
internal sealed record SceneCatalogFailure(string ScenePath, string Reason);
|
|
internal sealed record SceneCatalogEntry(string ScenePath, string RelativePath, IReadOnlyList<SceneObjectCatalogItem> Objects);
|
|
internal sealed record SceneObjectCatalogItem(string Name, eKObjectType ObjectType, string Value, bool Visible);
|
|
internal sealed record ObjectInfosProbeResult(eKResult Result, string SceneName, IReadOnlyList<SceneObjectCatalogItem> Objects, string Detail);
|
|
internal sealed record SceneValidationProbeResult(
|
|
bool ConnectRequestAccepted,
|
|
string SceneLoadOutcome,
|
|
int SuccessCount,
|
|
int FailureCount,
|
|
string OutputPath,
|
|
string Detail);
|
|
internal sealed record FolderInspectionProbeResult(
|
|
bool ConnectRequestAccepted,
|
|
int SceneCount,
|
|
int ProcessedSceneCount,
|
|
int DiscoveredVariableCount,
|
|
int FailureCount,
|
|
string OutputPath,
|
|
string Detail);
|
|
internal sealed class SceneValidationOperation
|
|
{
|
|
public string ObjectName { get; set; } = string.Empty;
|
|
|
|
public string Method { get; set; } = "SetValue";
|
|
|
|
public string? Value { get; set; }
|
|
|
|
public int KeyIndex { get; set; }
|
|
|
|
public double Number { get; set; }
|
|
}
|
|
|
|
internal sealed record SceneOperationValidationResult(string ObjectName, string Method, string Payload, string Result, string Detail);
|
|
internal sealed record SceneInspectionResult(string ScenePath, string RelativePath, int CandidateCount, IReadOnlyList<SceneVariableDiscovery> Discoveries);
|
|
internal sealed record SceneVariableDiscovery(string VariableName, string Method, string Payload, string Result);
|
|
|
|
internal sealed class ProbeEventHandler : KAEventHandler
|
|
{
|
|
private readonly TaskCompletionSource<int> _connectTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private readonly TaskCompletionSource<int> _closeTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private TaskCompletionSource<eKResult> _loadSceneTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private TaskCompletionSource<eKResult> _unloadSceneTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private TaskCompletionSource<eKResult> _counterNumberKeyTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private TaskCompletionSource<eKResult> _setValueTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private TaskCompletionSource<ObjectInfosProbeResult> _objectInfosTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private IKAScene? _sceneToQueryOnLoad;
|
|
|
|
public Task<int> ConnectTask => _connectTask.Task;
|
|
|
|
public Task<int> CloseTask => _closeTask.Task;
|
|
|
|
public Task<eKResult> LoadSceneTask => _loadSceneTask.Task;
|
|
|
|
public Task<eKResult> UnloadSceneTask => _unloadSceneTask.Task;
|
|
|
|
public Task<eKResult> CounterNumberKeyTask => _counterNumberKeyTask.Task;
|
|
|
|
public Task<eKResult> SetValueTask => _setValueTask.Task;
|
|
|
|
public Task<ObjectInfosProbeResult> ObjectInfosTask => _objectInfosTask.Task;
|
|
|
|
public void ResetLoadSceneTask() => _loadSceneTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
public void ResetUnloadSceneTask() => _unloadSceneTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
public void ResetCounterNumberKeyTask() => _counterNumberKeyTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
public void ResetSetValueTask() => _setValueTask = new TaskCompletionSource<eKResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
public void ResetObjectInfosTask() => _objectInfosTask = new TaskCompletionSource<ObjectInfosProbeResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
public void ConfigureQueryObjectInfosOnLoad(IKAScene scene) => _sceneToQueryOnLoad = scene;
|
|
|
|
public void OnConnect(int ErrorCode)
|
|
{
|
|
Console.WriteLine($"[SDK] OnConnect errorCode={ErrorCode}");
|
|
_connectTask.TrySetResult(ErrorCode);
|
|
}
|
|
|
|
public void OnClose(int ErrorCode)
|
|
{
|
|
Console.WriteLine($"[SDK] OnClose errorCode={ErrorCode}");
|
|
_closeTask.TrySetResult(ErrorCode);
|
|
}
|
|
|
|
public void OnLoadScene(eKResult Result, string SceneName)
|
|
{
|
|
Console.WriteLine($"[SDK] OnLoadScene result={Result} scene={SceneName}");
|
|
_loadSceneTask.TrySetResult(Result);
|
|
|
|
if (Result == eKResult.RESULT_SUCCESS && _sceneToQueryOnLoad is not null)
|
|
{
|
|
try
|
|
{
|
|
_sceneToQueryOnLoad.QueryObjectInfos();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_objectInfosTask.TrySetResult(new ObjectInfosProbeResult(
|
|
eKResult.RESULT_FAILURE,
|
|
SceneName,
|
|
Array.Empty<SceneObjectCatalogItem>(),
|
|
ex.Message));
|
|
}
|
|
finally
|
|
{
|
|
_sceneToQueryOnLoad = null;
|
|
}
|
|
}
|
|
}
|
|
public void OnLoadSceneForce(eKResult Result, string SceneName) { }
|
|
public void OnLogMessage(string LogMessage) { }
|
|
public void OnMessageNo(uint MessageNo) { }
|
|
public void OnBeginTransaction(eKResult Result) { }
|
|
public void OnEndTransaction(eKResult Result) { }
|
|
public void OnHeartBeat(eKResult Result) { }
|
|
public void OnUnloadAll(eKResult Result) { }
|
|
public void OnSetTrialPlayoutMode(eKResult Result) { }
|
|
public void OnCheckVersion(eKResult Result, string ServerVersion, string SDKVersion) { }
|
|
public void OnSetAudioOutput(eKResult Result) { }
|
|
public void OnScenePrepare(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnScenePrepareEx(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnPlay(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnPlayOut(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnStop(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnStopAll(eKResult Result) { }
|
|
public void OnPause(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnResume(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnQueryIsOnAir(eKResult Result, int OutputChannelIndex, int LayerNo, int bOnAir) { }
|
|
public void OnTrigger(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnScenePlayingStarted(string SceneName, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnScenePlayed(string SceneName, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnSceneAnimationPlayed(string SceneName, int OutputChannelIndex, int LayerNo, string AnimationName) { }
|
|
public void OnScenePaused(string SceneName, int OutputChannelIndex, int LayerNo, int bLastPause) { }
|
|
public void OnSceneSaved(eKResult Result, string FileName) { }
|
|
public void OnTriggerObject(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnResumeBackground(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnSaveMixedPreviewImage(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnPlayDirect(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnCutIn(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnCutOut(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnClearNextPreview(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnPlayRange(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnQueryPlaybackRangeCount(eKResult Result, string SceneName, int PlaybackRangeCount) { }
|
|
public void OnQueryPlaybackRange(eKResult Result, string SceneName, int PlaybackRangeNo, int Start, int End) { }
|
|
public void OnQueryOutputChannelIndex(eKResult Result, string SceneName, int OutputChannelIndex) { }
|
|
public void OnPlayInNextPreview(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnPlayOutNextPreview(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnSetBackgroundFill(eKResult Result, string SceneName) { }
|
|
public void OnSetBackgroundTexture(eKResult Result, string SceneName) { }
|
|
public void OnSetBackgroundVideo(eKResult Result, string SceneName) { }
|
|
public void OnSetBackgroundLiveIn(eKResult Result, string SceneName) { }
|
|
public void OnUseBackground(eKResult Result, string SceneName) { }
|
|
public void OnSetBackgroundVideoPlayInfo(eKResult Result, string SceneName) { }
|
|
public void OnQueryBackgroundVideoPlayInfo(eKResult Result, string SceneName, ref sKVideoPlayInfo pVideoPlayInfo) { }
|
|
public void OnSetSceneEffectType(eKResult Result, string SceneName) { }
|
|
public void OnSaveSceneImage(eKResult Result, string SceneName) { }
|
|
public void OnSaveScene(eKResult Result, string SceneName) { }
|
|
public void OnUnloadScene(eKResult Result, string SceneName)
|
|
{
|
|
_unloadSceneTask.TrySetResult(Result);
|
|
}
|
|
public void OnReloadScene(eKResult Result, string SceneName) { }
|
|
public void OnUpdateTextures(eKResult Result, string SceneName) { }
|
|
public void OnSetSceneAudioFile(eKResult Result, string SceneName) { }
|
|
public void OnEnableSceneAudio(eKResult Result, string SceneName) { }
|
|
public void OnSetSceneDuration(eKResult Result, string SceneName) { }
|
|
public void OnSetBackgroundPauseType(eKResult Result, string SceneName) { }
|
|
public void OnSetBackgroundChangeType(eKResult Result, string SceneName) { }
|
|
public void OnSetBackgroundPauseAtZeroFrameAsStandBy(eKResult Result, string SceneName) { }
|
|
public void OnResetDuration(eKResult Result, string SceneName) { }
|
|
public void OnSetDuration(eKResult Result, string SceneName) { }
|
|
public void OnAddObject(eKResult Result, string SceneName) { }
|
|
public void OnAddCloneObject(eKResult Result, string SceneName) { }
|
|
public void OnUpdateThumbnail(eKResult Result, string SceneName) { }
|
|
public void OnExportVideo(eKResult Result, string SceneName) { }
|
|
public void OnStopVideoExporting(eKResult Result) { }
|
|
public void OnQueryVideoExportingProgress(eKResult Result, string TargetName, int CurrentFrame, int TotalFrame) { }
|
|
public void OnFinishedVideoExporting(eKResult Result, string FileName) { }
|
|
public void OnAddPause(eKResult Result, string SceneName) { }
|
|
public void OnDeletePause(eKResult Result, string SceneName) { }
|
|
public void OnSetPause(eKResult Result, string SceneName) { }
|
|
public void OnSetPauseWithIndex(eKResult Result, string SceneName) { }
|
|
public void OnDeletePauseWithIndex(eKResult Result, string SceneName) { }
|
|
public void OnQueryPauseCount(eKResult Result, string SceneName, int PauseCount) { }
|
|
public void OnQueryObjectInfos(eKResult Result, string SceneName, KAObjectInfos pObjectInfos)
|
|
{
|
|
var objects = new List<SceneObjectCatalogItem>();
|
|
if (Result == eKResult.RESULT_SUCCESS)
|
|
{
|
|
var count = pObjectInfos.GetCount();
|
|
for (var index = 0; index < count; index++)
|
|
{
|
|
var objectInfo = pObjectInfos.GetObjectInfo(index);
|
|
objects.Add(new SceneObjectCatalogItem(
|
|
objectInfo.Name ?? string.Empty,
|
|
objectInfo.ObjectType,
|
|
objectInfo.Value ?? string.Empty,
|
|
objectInfo.bVisible != 0));
|
|
}
|
|
}
|
|
|
|
_objectInfosTask.TrySetResult(new ObjectInfosProbeResult(Result, SceneName, objects, string.Empty));
|
|
}
|
|
public void OnQueryAnimationNames(eKResult Result, string SceneName, KAStrings pAnimationNames) { }
|
|
public void OnQueryAnimationCount(eKResult Result, string SceneName, int AnimationCount) { }
|
|
public void OnQueryObjectInfosByScreenPoint(eKResult Result, KAObjectInfos pObjectInfos) { }
|
|
public void OnQuerySceneEffectType(eKResult Result, string SceneName, int bInEffect, eKEffectType EffectType, int Duration) { }
|
|
public void OnQueryDuration(eKResult Result, string SceneName, string AnimationName, int Duration) { }
|
|
public void OnQueryContentsOfTextObjects(eKResult Result, string SceneName, KAStrings pTexts) { }
|
|
public void OnSetStyleColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetStyleTexture(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetFaceTextColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetEdgeTextColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetShadowTextColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetVisible(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetValue(eKResult Result, string SceneName, string ObjectName)
|
|
{
|
|
if (Result != eKResult.RESULT_ERROR_NO_VARIABLE_OBJECT)
|
|
{
|
|
Console.WriteLine($"[SDK] OnSetValue result={Result} scene={SceneName} object={ObjectName}");
|
|
}
|
|
|
|
_setValueTask.TrySetResult(Result);
|
|
}
|
|
public void OnAddText(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnStoreTextStyle(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetTextStyle(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnEditText(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetFont(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetTextRange(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnResetTextRange(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryObjectType(eKResult Result, string SceneName, string ObjectName, eKObjectType ObjectType) { }
|
|
public void OnSetChartCSVFile(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetChartCellData(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryChartDataTable(eKResult Result, string SceneName, string ObjectName, KAChartDataTable Table) { }
|
|
public void OnQuerySize(eKResult Result, string SceneName, string ObjectName, float Width, float Height) { }
|
|
public void OnSetSize(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCounterNumberKey(eKResult Result, string SceneName, string ObjectName)
|
|
{
|
|
if (Result != eKResult.RESULT_ERROR_NO_VARIABLE_OBJECT)
|
|
{
|
|
Console.WriteLine($"[SDK] OnSetCounterNumberKey result={Result} scene={SceneName} object={ObjectName}");
|
|
}
|
|
|
|
_counterNumberKeyTask.TrySetResult(Result);
|
|
}
|
|
public void OnSetPositionKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetRotationKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetScaleKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCylinderAngleKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetSphereAngleKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCircleAngleKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCropKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCountDown(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetPosition(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetRotation(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetScale(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnAddPathPoint(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnClearPathPoints(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnAddPathShapePoint(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnClearPathShapePoints(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryScrollRemainingDistance(eKResult Result, string SceneName, string ObjectName, int ScrollRemainingDistance) { }
|
|
public void OnQueryScrollChildRemainingDistance(eKResult Result, string SceneName, string ObjectName, string ChildName, int ScrollRemainingDistance) { }
|
|
public void OnAddScrollObject(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnAdjustScrollSpeed(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetScrollSpeed(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetVariableName(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetLoftPositionKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetChangeOut(eKResult Result, string SceneName) { }
|
|
public void OnModifyPathPoint(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnInitScrollObject(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCounterInfo(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCounterNumber(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCounterRange(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCounterRemainingTime(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetCounterElapsedTime(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSaveObjectImage(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetPositionOfPathAnimation(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetPositionKeyOfPathAnimation(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetStartFrame(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetObjectEffectType(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetObjectOutEffectDelay(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetColorKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetEmissiveColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetEmissiveColorKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetTransparencyOpacity(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetTransparencyOpacityKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetExposure(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetExposureKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureType(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureFile(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureOffset(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureOffsetKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureTiling(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureTilingKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureRotation(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureRotationKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureOpacity(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureOpacityKey(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryGroupType(eKResult Result, string SceneName, string ObjectName, eKGroupType GroupType) { }
|
|
public void OnQueryImageType(eKResult Result, string SceneName, string ObjectName, eKImageType ImageType) { }
|
|
public void OnSetVideoPlayInfo(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryVideoPlayInfo(eKResult Result, string SceneName, string ObjectName, ref sKVideoPlayInfo pVideoPlayInfo) { }
|
|
public void OnQueryIs3D(eKResult Result, string SceneName, string ObjectName, int b3D) { }
|
|
public void OnQueryPosition(eKResult Result, string SceneName, string ObjectName, float X, float Y, float Z) { }
|
|
public void OnSetImageType(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMemo(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryMemo(eKResult Result, string SceneName, string ObjectName, string Memo) { }
|
|
public void OnQueryFont(eKResult Result, string SceneName, string ObjectName, ref sKFont Param) { }
|
|
public void OnSetImageOriginalSize(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnApplyChangeEffectLibrary(eKResult Result, string SceneName) { }
|
|
public void OnApplyObjectLibrary(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnApplyTextureEffectLibrary(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetTableValue(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetTableColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryTableValue(eKResult Result, string SceneName, string ObjectName, int Row, int Column, string Value) { }
|
|
public void OnQueryTableValues(eKResult Result, string SceneName, string ObjectName, KATableValues pValues) { }
|
|
public void OnSetPathShapeOutlineThickness(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnEnablePathShapeOutline(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetPlaybackCamera(eKResult Result, string SceneName) { }
|
|
public void OnSetMaterialTextureVideoPlayInfo(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnQueryMaterialTextureVideoPlayInfo(eKResult Result, string SceneName, string ObjectName, ref sKVideoPlayInfo VideoPlayInfo) { }
|
|
public void OnQueryVideoFormat(eKResult Result, ref sKVideoFormat VideoFormat) { }
|
|
public void OnQueryLiveStreamingStatus(eKResult Result, string StreamingURI, eKLiveStreamingStatus Status) { }
|
|
public void OnPreloadLiveStreaming(eKResult Result, string StreamingURI) { }
|
|
public void OnReleaseLiveStreaming(eKResult Result, string StreamingURI) { }
|
|
public void OnUpdateImageResource(eKResult Result) { }
|
|
public void OnQueryLayerCount(eKResult Result, int LayerCount) { }
|
|
public void OnSetLayerViewportRate(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnSetLayerViewportRateEx(eKResult Result, int OutputChannelIndex, int LayerNo) { }
|
|
public void OnSetFitting(eKResult Result, string SceneName) { }
|
|
public void OnSetFittingOffset(eKResult Result, string SceneName) { }
|
|
public void OnSetFittingScale(eKResult Result, string SceneName) { }
|
|
public void OnSetLightColor(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnEnableLight(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetDirectionalLight(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetPointLight(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetSpotLight(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetInfinitePointLight(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetMaterialTextureLiveStreamingURI(eKResult Result, string SceneName, string ObjectName) { }
|
|
public void OnSetBackgroundLiveStreamingURI(eKResult Result, string SceneName) { }
|
|
public void OnLoadProject(eKResult Result, string FilePath, string AliasName) { }
|
|
public void OnNewProject(eKResult Result, string AliasName) { }
|
|
public void OnUnloadAllProject(eKResult Result) { }
|
|
public void OnSaveProject(eKResult Result, string AliasName) { }
|
|
public void OnQuerySceneItemCount(eKResult Result, string AliasName, int SceneItemCount) { }
|
|
public void OnQuerySceneItemInfos(eKResult Result, string AliasName, KASceneItemInfos SceneItemInfos) { }
|
|
public void OnAddSceneItem(eKResult Result, string AliasName, int Index) { }
|
|
public void OnInsertSceneItem(eKResult Result, string AliasName) { }
|
|
public void OnDeleteSceneItem(eKResult Result, string AliasNAme) { }
|
|
public void OnQueryProjectFormat(eKResult Result, ref sKVideoFormat ProjectFormat) { }
|
|
public void OnSetTimecode(eKResult Result, string AliasName) { }
|
|
public void OnSetTimecodeInOut(eKResult Result, string AliasName) { }
|
|
public void OnSetTimecodeTrack(eKResult Result, string AliasName) { }
|
|
public void OnSetTimecodeInOutType(eKResult Result, string AliasName) { }
|
|
public void OnDeleteTimecode(eKResult Result, string AliasName) { }
|
|
public void OnQueryTimecode(eKResult Result, int TrackNo, int In, int Out, int bOnTrack) { }
|
|
public void OnUnloadProject(eKResult Result, string AliasName) { }
|
|
public void OnEnableSyncWithSceneEffect(eKResult Result, string AliasName) { }
|
|
public void OnExportProjectVideo(eKResult Result, string AliasName) { }
|
|
public void OnExportSceneImage(eKResult Result, string SceneName) { }
|
|
public void OnStartVideoCapture(eKResult Result) { }
|
|
public void OnStopVideoCapture(eKResult Result) { }
|
|
public void OnCaptureImage(eKResult Result) { }
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
internal struct NativeMessage
|
|
{
|
|
public IntPtr hwnd;
|
|
public uint message;
|
|
public UIntPtr wParam;
|
|
public IntPtr lParam;
|
|
public uint time;
|
|
public NativePoint pt;
|
|
public uint lPrivate;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
internal struct NativePoint
|
|
{
|
|
public int x;
|
|
public int y;
|
|
}
|
|
|
|
internal static class User32
|
|
{
|
|
[DllImport("user32.dll")]
|
|
internal static extern bool PeekMessage(out NativeMessage lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern bool TranslateMessage([In] ref NativeMessage lpMsg);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern IntPtr DispatchMessage([In] ref NativeMessage lpMsg);
|
|
}
|