Prove generic Unity movement and fire

This commit is contained in:
Meta Vens 2026-07-10 06:37:33 +01:00
commit 38dcdc6d3a
3 changed files with 80 additions and 16 deletions

View file

@ -162,6 +162,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
}
[UnityTest]
[Timeout(300000)]
public IEnumerator GenericCultMeshClientLowersAndMovesAdvertisedWorld()
{
var endpoint = Environment.GetEnvironmentVariable("EVEUNITY_PROVIDER_ENDPOINT");
@ -244,6 +245,40 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Assert.That(runtime.ActiveVersion, Is.GreaterThan(initialVersion));
Assert.That(Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition), Is.GreaterThan(0.01f));
var movementVersion = runtime.ActiveVersion;
var focus = runtime.SubmitFocusIntent(playerId);
var focusDeadline = Time.realtimeSinceStartup + 12f;
while (Time.realtimeSinceStartup < focusDeadline &&
(runtime.LastReceipt == null || runtime.LastReceipt.CommandId != focus.CommandId ||
runtime.ActiveVersion < runtime.LastReceipt.SourceVersion))
{
yield return new WaitForSecondsRealtime(0.1f);
runtime.Refresh();
}
Assert.That(runtime.LastReceipt, Is.Not.Null);
Assert.That(runtime.LastReceipt.CommandId, Is.EqualTo(focus.CommandId));
Assert.That(runtime.LastReceipt.State, Is.EqualTo("accepted").Or.EqualTo("reconciled"));
Assert.That(runtime.LastReceipt.SourceVersion, Is.GreaterThan(movementVersion));
var focusVersion = runtime.ActiveVersion;
var action = runtime.SubmitActionIntent(playerId, "0");
var actionDeadline = Time.realtimeSinceStartup + 12f;
while (Time.realtimeSinceStartup < actionDeadline &&
(runtime.LastReceipt == null || runtime.LastReceipt.CommandId != action.CommandId ||
runtime.ActiveVersion < runtime.LastReceipt.SourceVersion))
{
yield return new WaitForSecondsRealtime(0.1f);
runtime.Refresh();
}
Assert.That(runtime.LastReceipt, Is.Not.Null);
Assert.That(runtime.LastReceipt.CommandId, Is.EqualTo(action.CommandId));
Assert.That(
runtime.LastReceipt.State,
Is.EqualTo("accepted").Or.EqualTo("reconciled"),
runtime.LastReceipt.Message);
Assert.That(runtime.LastReceipt.SourceVersion, Is.GreaterThan(focusVersion));
Assert.That(runtime.ActiveVersion, Is.GreaterThanOrEqualTo(runtime.LastReceipt.SourceVersion));
var light = lightObject.AddComponent<Light>();
light.type = LightType.Directional;
light.intensity = 1.4f;
@ -263,11 +298,16 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
}
}
var playerMarker = FindMarker(root, playerId);
var playerRenderer = playerMarker.GetComponentInChildren<Renderer>();
Assert.That(playerRenderer, Is.Not.Null, "The provider-authored player prefab has no renderable visual.");
var playerBounds = playerRenderer.bounds;
var cameraDistance = Mathf.Max(8f, playerBounds.extents.magnitude * 4f);
camera.transform.position = playerBounds.center + new Vector3(1f, 0.8f, -1.2f).normalized * cameraDistance;
var playerRenderers = playerMarker.GetComponentsInChildren<Renderer>();
Assert.That(playerRenderers, Is.Not.Empty, "The provider-authored player prefab has no renderable visual.");
var playerBounds = playerRenderers[0].bounds;
foreach (var renderer in playerRenderers)
playerBounds.Encapsulate(renderer.bounds);
var halfFovRadians = camera.fieldOfView * 0.5f * Mathf.Deg2Rad;
var cameraDistance = Mathf.Max(8f, playerBounds.extents.magnitude / Mathf.Tan(halfFovRadians) * 1.35f);
camera.nearClipPlane = Mathf.Max(0.01f, cameraDistance * 0.001f);
camera.farClipPlane = Mathf.Max(1000f, cameraDistance + playerBounds.size.magnitude * 4f);
camera.transform.position = playerBounds.center + new Vector3(1f, 0.65f, -1.2f).normalized * cameraDistance;
camera.transform.LookAt(playerBounds.center);
target = new RenderTexture(640, 360, 24, RenderTextureFormat.ARGB32);
camera.targetTexture = target;

View file

@ -43,6 +43,7 @@ namespace GameCult.Eve.UnityScene
private readonly List<AssetBundle> _assetBundles = new List<AssetBundle>();
private CultMeshNode? _node;
private CultMeshSnapshotEndpoint? _snapshot;
private CultMeshSnapshotSession? _assetSession;
private CultNetDocumentRegistry? _networkRegistry;
private CultNetShardDescriptor? _replicaShard;
private EveProviderAdvertisementDocument? _advertisement;
@ -148,8 +149,10 @@ namespace GameCult.Eve.UnityScene
_assetBundles.Clear();
_prefabs.Clear();
_node?.Dispose();
_assetSession?.Dispose();
_node = null;
_snapshot = null;
_assetSession = null;
_networkRegistry = null;
_replicaShard = null;
_pendingCommandIds.Clear();
@ -215,6 +218,19 @@ namespace GameCult.Eve.UnityScene
RudpMaxFragmentBytes = 2048
}
});
_assetSession = CultMesh.SnapshotSession(
_endpoint,
new CultMeshSnapshotRequestOptions
{
ShardId = RemoteShardId,
ShardEpoch = 1,
ConnectTimeout = TimeSpan.FromSeconds(5),
ResponseTimeout = TimeSpan.FromSeconds(10),
MessageIdPrefix = "eve-unity-assets",
RudpRuntimeId = $"{_runtimeId}.assets",
RudpMaxFragmentBytes = 2048
},
_networkRegistry);
}
private void ResolveAdvertisement()
@ -294,7 +310,7 @@ namespace GameCult.Eve.UnityScene
foreach (var group in selected.GroupBy(selection => selection.Variant!.Uri, StringComparer.Ordinal))
{
var variant = group.First().Variant!;
var manifest = _snapshot.FetchDocumentsAsync<CultMeshCdnArtifactManifest>(
var manifest = _assetSession!.FetchDocumentsAsync<CultMeshCdnArtifactManifest>(
recordKeys: new[] { variant.Uri },
schemaIds: new[] { CultMeshCdnSchemaVersions.ArtifactManifest })
.GetAwaiter()
@ -332,20 +348,26 @@ namespace GameCult.Eve.UnityScene
private byte[] ReadBundle(CultMeshCdnArtifactManifest manifest)
{
var bytes = new byte[checked((int)manifest.SizeBytes)];
foreach (var reference in manifest.Chunks.OrderBy(chunk => chunk.Offset))
var references = manifest.Chunks.OrderBy(chunk => chunk.Offset).ToArray();
for (var index = 0; index < references.Length; index += 2)
{
var chunk = _snapshot!
var batch = references.Skip(index).Take(2).ToArray();
var chunks = _assetSession!
.FetchDocumentsAsync<CultMeshCdnArtifactChunk>(
recordKeys: new[] { reference.RecordKey },
recordKeys: batch.Select(reference => reference.RecordKey).ToArray(),
schemaIds: new[] { CultMeshCdnSchemaVersions.ArtifactChunk })
.GetAwaiter()
.GetResult()
.FirstOrDefault();
if (chunk == null)
throw new InvalidOperationException($"Provider asset chunk '{reference.RecordKey}' was not available.");
if (chunk.SizeBytes != reference.SizeBytes || chunk.Payload.Length != reference.SizeBytes)
throw new InvalidDataException($"Provider asset chunk '{reference.RecordKey}' size did not match its manifest.");
Buffer.BlockCopy(chunk.Payload, 0, bytes, checked((int)reference.Offset), reference.SizeBytes);
.GetResult();
foreach (var reference in batch)
{
var chunk = chunks.FirstOrDefault(candidate =>
string.Equals(candidate.ChunkHash, reference.ChunkHash, StringComparison.Ordinal));
if (chunk == null)
throw new InvalidOperationException($"Provider asset chunk '{reference.RecordKey}' was not available.");
if (chunk.SizeBytes != reference.SizeBytes || chunk.Payload.Length != reference.SizeBytes)
throw new InvalidDataException($"Provider asset chunk '{reference.RecordKey}' size did not match its manifest.");
Buffer.BlockCopy(chunk.Payload, 0, bytes, checked((int)reference.Offset), reference.SizeBytes);
}
}
return bytes;
}

View file

@ -69,6 +69,8 @@ $daemonArguments = @(
"--client-cultmesh-host", "127.0.0.1",
"--client-cultmesh-advertise-host", "127.0.0.1",
"--client-cultmesh-port", $Port,
"--tick-interval-ms", 250,
"--fixed-delta-ms", 20,
"--no-odin-announcements"
)
$env:AETHERIA_TRACE_EVE_SNAPSHOTS = "1"