Prove contact-gated cargo rejection
This commit is contained in:
parent
190f5871ed
commit
e73da9faf5
4 changed files with 238 additions and 67 deletions
|
|
@ -210,7 +210,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
float tractorReleasedPower = 1f;
|
||||
int tractorBeamParticleSystemCount = 0;
|
||||
EveUnityFeedbackEvent pickupCollection = null;
|
||||
EveUnityFeedbackEvent pickupRejection = null;
|
||||
int pickupCollectionEventCount = 0;
|
||||
int pickupRejectionEventCount = 0;
|
||||
var pickupRejectionEventIds = new HashSet<string>(StringComparer.Ordinal);
|
||||
int initialPickupEntityCount = 0;
|
||||
int finalPickupEntityCount = -1;
|
||||
bool destructionPickupObserved = false;
|
||||
|
|
@ -225,6 +228,15 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
witnessProfile,
|
||||
"full-session-gameplay",
|
||||
StringComparison.Ordinal);
|
||||
var gameplayScenario = Environment.GetEnvironmentVariable("EVEUNITY_WITNESS_GAMEPLAY_SCENARIO") ??
|
||||
"released-client-proof";
|
||||
Assert.That(gameplayScenario,
|
||||
Is.EqualTo("released-client-proof").Or.EqualTo("cargo-capacity-rejection-proof"),
|
||||
"The live witness must select an explicit daemon-owned gameplay scenario.");
|
||||
var expectCargoRejection = string.Equals(
|
||||
gameplayScenario,
|
||||
"cargo-capacity-rejection-proof",
|
||||
StringComparison.Ordinal);
|
||||
EveUnityAimPresentationRenderer aimRenderer = null;
|
||||
EveUnityBeamPresentation beamPresentation = null;
|
||||
EveUnityBeamPresentationRenderer beamRenderer = null;
|
||||
|
|
@ -311,16 +323,25 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
if (requireSessionGameplay)
|
||||
Assert.That(initialPickupEntityCount, Is.Zero,
|
||||
"The witness scenario must obtain salvage from daemon-owned destruction, not a boot-seeded pickup.");
|
||||
runtime.FeedbackAvailable += feedback =>
|
||||
{
|
||||
if (!string.Equals(feedback.Kind, "pickup.collected", StringComparison.Ordinal)) return;
|
||||
pickupCollectionEventCount++;
|
||||
pickupCollection = feedback;
|
||||
};
|
||||
|
||||
var playerId = runtime.ActiveWorld.PlayerEntityId;
|
||||
var playerFact = host.PresentedEntities.CurrentGeneration.Entities
|
||||
.Single(entity => entity.EntityId == playerId);
|
||||
runtime.FeedbackAvailable += feedback =>
|
||||
{
|
||||
if (feedback.TargetEntityIndex != playerFact.SourceIndex) return;
|
||||
if (string.Equals(feedback.Kind, "pickup.collected", StringComparison.Ordinal))
|
||||
{
|
||||
pickupCollectionEventCount++;
|
||||
pickupCollection = feedback;
|
||||
}
|
||||
else if (string.Equals(feedback.Kind, "pickup.rejected", StringComparison.Ordinal))
|
||||
{
|
||||
pickupRejectionEventCount++;
|
||||
pickupRejectionEventIds.Add(feedback.EventId);
|
||||
pickupRejection = feedback;
|
||||
}
|
||||
};
|
||||
|
||||
var playerPrefab = provider.ResolvePrefab(new EveUnityPlayableWorldAssetBinding(
|
||||
playerFact.AssetRef,
|
||||
playerFact.EntityKind,
|
||||
|
|
@ -529,7 +550,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
(observedShot == null ||
|
||||
host.PresentedEntities.CurrentGeneration.Entities.Any(entity =>
|
||||
entity.EntityId == targetEntityId) ||
|
||||
(!destructionPickupObserved && pickupCollection == null)))
|
||||
(!destructionPickupObserved && pickupCollection == null && pickupRejection == null)))
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.05f);
|
||||
runtime.Refresh();
|
||||
|
|
@ -544,11 +565,12 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
Assert.That(host.PresentedEntities.CurrentGeneration.Entities.Any(entity =>
|
||||
entity.EntityId == targetEntityId), Is.False,
|
||||
"The low-hull witness raider was not destroyed by daemon combat.");
|
||||
Assert.That(destructionPickupObserved || pickupCollection != null, Is.True,
|
||||
Assert.That(destructionPickupObserved || pickupCollection != null || pickupRejection != null, Is.True,
|
||||
"Daemon destruction did not publish its guaranteed cargo as a pickup.");
|
||||
var collectionDeadline = Time.realtimeSinceStartup + 15f;
|
||||
var nextTractorAimAt = 0f;
|
||||
while (Time.realtimeSinceStartup < collectionDeadline && pickupCollection == null)
|
||||
while (Time.realtimeSinceStartup < collectionDeadline &&
|
||||
(expectCargoRejection ? pickupRejection == null : pickupCollection == null))
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.05f);
|
||||
runtime.Refresh();
|
||||
|
|
@ -571,19 +593,44 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
}
|
||||
finalPickupEntityCount = host.PresentedEntities.CurrentGeneration.Entities.Count(entity =>
|
||||
string.Equals(entity.EntityKind, "pickup", StringComparison.Ordinal));
|
||||
Assert.That(pickupCollection, Is.Not.Null,
|
||||
"Destruction-created salvage never reached cargo through a Ymir contact fact.");
|
||||
Assert.That(pickupCollectionEventCount, Is.EqualTo(1),
|
||||
"One destruction-loot Ymir contact transaction must emit one collection event.");
|
||||
Assert.That(pickupCollection.ItemKey, Is.Not.Empty,
|
||||
"Destruction loot must retain its canonical typed item identity.");
|
||||
Assert.That(pickupCollection.EventId, Does.StartWith("ymir-fact:"),
|
||||
"Collection feedback must retain the consumed Ymir fact identity.");
|
||||
Assert.That(pickupCollection.ScalarValue, Is.EqualTo(1));
|
||||
Assert.That(pickupCollection.CargoQuantityBefore, Is.Zero);
|
||||
Assert.That(pickupCollection.CargoQuantityAfter, Is.EqualTo(1));
|
||||
Assert.That(finalPickupEntityCount, Is.Zero,
|
||||
"Collected destruction loot remained in the provider-authored entity generation.");
|
||||
if (expectCargoRejection)
|
||||
{
|
||||
Assert.That(pickupRejection, Is.Not.Null,
|
||||
"Full cargo did not reject destruction salvage from its Ymir contact fact.");
|
||||
Assert.That(pickupRejectionEventCount, Is.EqualTo(1),
|
||||
"The first destruction-loot Ymir contact transaction must emit one rejection event.");
|
||||
Assert.That(pickupRejectionEventIds.Count, Is.EqualTo(pickupRejectionEventCount),
|
||||
"One Ymir fact identity emitted duplicate player rejection feedback.");
|
||||
Assert.That(pickupCollectionEventCount, Is.Zero,
|
||||
"Capacity-rejected salvage was also collected.");
|
||||
Assert.That(pickupRejection.ItemKey, Is.Not.Empty);
|
||||
Assert.That(pickupRejection.EventId, Does.StartWith("ymir-fact:"),
|
||||
"Rejection feedback must retain the consumed Ymir fact identity.");
|
||||
Assert.That(pickupRejection.Reason, Is.EqualTo("cargo-capacity"));
|
||||
Assert.That(pickupRejection.ScalarValue, Is.EqualTo(1));
|
||||
Assert.That(pickupRejection.CargoQuantityAfter,
|
||||
Is.EqualTo(pickupRejection.CargoQuantityBefore));
|
||||
Assert.That(finalPickupEntityCount, Is.EqualTo(1),
|
||||
"Capacity-rejected destruction loot disappeared from provider state.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.That(pickupCollection, Is.Not.Null,
|
||||
"Destruction-created salvage never reached cargo through a Ymir contact fact.");
|
||||
Assert.That(pickupCollectionEventCount, Is.EqualTo(1),
|
||||
"One destruction-loot Ymir contact transaction must emit one collection event.");
|
||||
Assert.That(pickupRejectionEventCount, Is.Zero,
|
||||
"Collectible destruction salvage was also rejected.");
|
||||
Assert.That(pickupCollection.ItemKey, Is.Not.Empty,
|
||||
"Destruction loot must retain its canonical typed item identity.");
|
||||
Assert.That(pickupCollection.EventId, Does.StartWith("ymir-fact:"),
|
||||
"Collection feedback must retain the consumed Ymir fact identity.");
|
||||
Assert.That(pickupCollection.ScalarValue, Is.EqualTo(1));
|
||||
Assert.That(pickupCollection.CargoQuantityBefore, Is.Zero);
|
||||
Assert.That(pickupCollection.CargoQuantityAfter, Is.EqualTo(1));
|
||||
Assert.That(finalPickupEntityCount, Is.Zero,
|
||||
"Collected destruction loot remained in the provider-authored entity generation.");
|
||||
}
|
||||
observedCombat = EveUnityCombatPresentation.Find(runtime.ActiveProjection);
|
||||
Assert.That(observedShot.TargetEntityIndex, Is.EqualTo(targetEntityIndex));
|
||||
Assert.That(observedShot.LockQuality, Is.GreaterThanOrEqualTo(0.99));
|
||||
|
|
@ -869,6 +916,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
Assert.That(fieldSplats, Is.Not.Null);
|
||||
WriteWitnessFacts(
|
||||
witnessProfile,
|
||||
gameplayScenario,
|
||||
provider.CurrentAssetBodyTransportKind?.ToString() ?? "",
|
||||
initialVersion,
|
||||
runtime.ActiveVersion,
|
||||
|
|
@ -915,8 +963,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
initialPickupEntityCount,
|
||||
finalPickupEntityCount,
|
||||
pickupCollectionEventCount,
|
||||
pickupRejectionEventCount,
|
||||
destructionPickupObserved,
|
||||
pickupCollection,
|
||||
pickupRejection,
|
||||
camera.transform.position,
|
||||
camera.transform.forward,
|
||||
playerMarker.transform.position,
|
||||
|
|
@ -949,6 +999,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
|
||||
private static void WriteWitnessFacts(
|
||||
string witnessProfile,
|
||||
string gameplayScenario,
|
||||
string assetBodyTransport,
|
||||
long initialVersion,
|
||||
long finalVersion,
|
||||
|
|
@ -995,8 +1046,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
int initialPickupEntityCount,
|
||||
int finalPickupEntityCount,
|
||||
int pickupCollectionEventCount,
|
||||
int pickupRejectionEventCount,
|
||||
bool destructionPickupObserved,
|
||||
EveUnityFeedbackEvent pickupCollection,
|
||||
EveUnityFeedbackEvent pickupRejection,
|
||||
Vector3 cameraPosition,
|
||||
Vector3 cameraForward,
|
||||
Vector3 playerPosition,
|
||||
|
|
@ -1011,9 +1064,11 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
{
|
||||
var path = Environment.GetEnvironmentVariable("EVEUNITY_WITNESS_FACTS_PATH");
|
||||
if (string.IsNullOrWhiteSpace(path)) return;
|
||||
var pickupEvent = pickupCollection ?? pickupRejection;
|
||||
var document = new WitnessFacts
|
||||
{
|
||||
witnessProfile = witnessProfile,
|
||||
gameplayScenario = gameplayScenario,
|
||||
providerAdvertisement = true,
|
||||
providerAssets = true,
|
||||
assetBodyTransport = assetBodyTransport,
|
||||
|
|
@ -1068,16 +1123,25 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
tractorBeamParticleSystemCount = tractorBeamParticleSystemCount,
|
||||
pickupCollection = pickupCollection != null && pickupCollectionEventCount == 1 &&
|
||||
pickupCollection.CargoQuantityAfter - pickupCollection.CargoQuantityBefore == pickupCollection.ScalarValue,
|
||||
destructionLoot = pickupCollection != null &&
|
||||
pickupCollection.EventId.StartsWith("ymir-fact:", StringComparison.Ordinal),
|
||||
pickupRejection = pickupRejection != null && pickupRejectionEventCount >= 1 &&
|
||||
pickupCollectionEventCount == 0 &&
|
||||
string.Equals(pickupRejection.Reason, "cargo-capacity", StringComparison.Ordinal) &&
|
||||
pickupRejection.CargoQuantityAfter == pickupRejection.CargoQuantityBefore,
|
||||
destructionLoot = (pickupCollection?.EventId ?? pickupRejection?.EventId ?? "")
|
||||
.StartsWith("ymir-fact:", StringComparison.Ordinal),
|
||||
initialPickupEntityCount = initialPickupEntityCount,
|
||||
finalPickupEntityCount = finalPickupEntityCount,
|
||||
pickupCollectionEventCount = pickupCollectionEventCount,
|
||||
pickupEventId = pickupCollection?.EventId ?? "",
|
||||
pickupItemKey = pickupCollection?.ItemKey ?? "",
|
||||
pickupQuantity = (float)(pickupCollection?.ScalarValue ?? 0),
|
||||
cargoQuantityBeforePickup = (float)(pickupCollection?.CargoQuantityBefore ?? 0),
|
||||
cargoQuantityAfterPickup = (float)(pickupCollection?.CargoQuantityAfter ?? 0),
|
||||
pickupRejectionEventCount = pickupRejectionEventCount,
|
||||
pickupRejectionEventId = pickupRejection?.EventId ?? "",
|
||||
pickupRejectionReason = pickupRejection?.Reason ?? "",
|
||||
cargoQuantityBeforeRejection = (float)(pickupRejection?.CargoQuantityBefore ?? 0),
|
||||
cargoQuantityAfterRejection = (float)(pickupRejection?.CargoQuantityAfter ?? 0),
|
||||
pickupEventId = pickupEvent?.EventId ?? "",
|
||||
pickupItemKey = pickupEvent?.ItemKey ?? "",
|
||||
pickupQuantity = (float)(pickupEvent?.ScalarValue ?? 0),
|
||||
cargoQuantityBeforePickup = (float)(pickupEvent?.CargoQuantityBefore ?? 0),
|
||||
cargoQuantityAfterPickup = (float)(pickupEvent?.CargoQuantityAfter ?? 0),
|
||||
cameraPosition = cameraPosition,
|
||||
cameraForward = cameraForward,
|
||||
playerPosition = playerPosition,
|
||||
|
|
@ -1103,6 +1167,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
private sealed class WitnessFacts
|
||||
{
|
||||
public string witnessProfile;
|
||||
public string gameplayScenario;
|
||||
public bool providerAdvertisement;
|
||||
public bool providerAssets;
|
||||
public string assetBodyTransport;
|
||||
|
|
@ -1154,10 +1219,16 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
public float tractorReleasedPower;
|
||||
public int tractorBeamParticleSystemCount;
|
||||
public bool pickupCollection;
|
||||
public bool pickupRejection;
|
||||
public bool destructionLoot;
|
||||
public int initialPickupEntityCount;
|
||||
public int finalPickupEntityCount;
|
||||
public int pickupCollectionEventCount;
|
||||
public int pickupRejectionEventCount;
|
||||
public string pickupRejectionEventId;
|
||||
public string pickupRejectionReason;
|
||||
public float cargoQuantityBeforeRejection;
|
||||
public float cargoQuantityAfterRejection;
|
||||
public string pickupEventId;
|
||||
public string pickupItemKey;
|
||||
public float pickupQuantity;
|
||||
|
|
|
|||
|
|
@ -210,7 +210,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
float tractorReleasedPower = 1f;
|
||||
int tractorBeamParticleSystemCount = 0;
|
||||
EveUnityFeedbackEvent pickupCollection = null;
|
||||
EveUnityFeedbackEvent pickupRejection = null;
|
||||
int pickupCollectionEventCount = 0;
|
||||
int pickupRejectionEventCount = 0;
|
||||
var pickupRejectionEventIds = new HashSet<string>(StringComparer.Ordinal);
|
||||
int initialPickupEntityCount = 0;
|
||||
int finalPickupEntityCount = -1;
|
||||
bool destructionPickupObserved = false;
|
||||
|
|
@ -225,6 +228,15 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
witnessProfile,
|
||||
"full-session-gameplay",
|
||||
StringComparison.Ordinal);
|
||||
var gameplayScenario = Environment.GetEnvironmentVariable("EVEUNITY_WITNESS_GAMEPLAY_SCENARIO") ??
|
||||
"released-client-proof";
|
||||
Assert.That(gameplayScenario,
|
||||
Is.EqualTo("released-client-proof").Or.EqualTo("cargo-capacity-rejection-proof"),
|
||||
"The live witness must select an explicit daemon-owned gameplay scenario.");
|
||||
var expectCargoRejection = string.Equals(
|
||||
gameplayScenario,
|
||||
"cargo-capacity-rejection-proof",
|
||||
StringComparison.Ordinal);
|
||||
EveUnityAimPresentationRenderer aimRenderer = null;
|
||||
EveUnityBeamPresentation beamPresentation = null;
|
||||
EveUnityBeamPresentationRenderer beamRenderer = null;
|
||||
|
|
@ -311,16 +323,25 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
if (requireSessionGameplay)
|
||||
Assert.That(initialPickupEntityCount, Is.Zero,
|
||||
"The witness scenario must obtain salvage from daemon-owned destruction, not a boot-seeded pickup.");
|
||||
runtime.FeedbackAvailable += feedback =>
|
||||
{
|
||||
if (!string.Equals(feedback.Kind, "pickup.collected", StringComparison.Ordinal)) return;
|
||||
pickupCollectionEventCount++;
|
||||
pickupCollection = feedback;
|
||||
};
|
||||
|
||||
var playerId = runtime.ActiveWorld.PlayerEntityId;
|
||||
var playerFact = host.PresentedEntities.CurrentGeneration.Entities
|
||||
.Single(entity => entity.EntityId == playerId);
|
||||
runtime.FeedbackAvailable += feedback =>
|
||||
{
|
||||
if (feedback.TargetEntityIndex != playerFact.SourceIndex) return;
|
||||
if (string.Equals(feedback.Kind, "pickup.collected", StringComparison.Ordinal))
|
||||
{
|
||||
pickupCollectionEventCount++;
|
||||
pickupCollection = feedback;
|
||||
}
|
||||
else if (string.Equals(feedback.Kind, "pickup.rejected", StringComparison.Ordinal))
|
||||
{
|
||||
pickupRejectionEventCount++;
|
||||
pickupRejectionEventIds.Add(feedback.EventId);
|
||||
pickupRejection = feedback;
|
||||
}
|
||||
};
|
||||
|
||||
var playerPrefab = provider.ResolvePrefab(new EveUnityPlayableWorldAssetBinding(
|
||||
playerFact.AssetRef,
|
||||
playerFact.EntityKind,
|
||||
|
|
@ -529,7 +550,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
(observedShot == null ||
|
||||
host.PresentedEntities.CurrentGeneration.Entities.Any(entity =>
|
||||
entity.EntityId == targetEntityId) ||
|
||||
(!destructionPickupObserved && pickupCollection == null)))
|
||||
(!destructionPickupObserved && pickupCollection == null && pickupRejection == null)))
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.05f);
|
||||
runtime.Refresh();
|
||||
|
|
@ -544,11 +565,12 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
Assert.That(host.PresentedEntities.CurrentGeneration.Entities.Any(entity =>
|
||||
entity.EntityId == targetEntityId), Is.False,
|
||||
"The low-hull witness raider was not destroyed by daemon combat.");
|
||||
Assert.That(destructionPickupObserved || pickupCollection != null, Is.True,
|
||||
Assert.That(destructionPickupObserved || pickupCollection != null || pickupRejection != null, Is.True,
|
||||
"Daemon destruction did not publish its guaranteed cargo as a pickup.");
|
||||
var collectionDeadline = Time.realtimeSinceStartup + 15f;
|
||||
var nextTractorAimAt = 0f;
|
||||
while (Time.realtimeSinceStartup < collectionDeadline && pickupCollection == null)
|
||||
while (Time.realtimeSinceStartup < collectionDeadline &&
|
||||
(expectCargoRejection ? pickupRejection == null : pickupCollection == null))
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.05f);
|
||||
runtime.Refresh();
|
||||
|
|
@ -571,19 +593,44 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
}
|
||||
finalPickupEntityCount = host.PresentedEntities.CurrentGeneration.Entities.Count(entity =>
|
||||
string.Equals(entity.EntityKind, "pickup", StringComparison.Ordinal));
|
||||
Assert.That(pickupCollection, Is.Not.Null,
|
||||
"Destruction-created salvage never reached cargo through a Ymir contact fact.");
|
||||
Assert.That(pickupCollectionEventCount, Is.EqualTo(1),
|
||||
"One destruction-loot Ymir contact transaction must emit one collection event.");
|
||||
Assert.That(pickupCollection.ItemKey, Is.Not.Empty,
|
||||
"Destruction loot must retain its canonical typed item identity.");
|
||||
Assert.That(pickupCollection.EventId, Does.StartWith("ymir-fact:"),
|
||||
"Collection feedback must retain the consumed Ymir fact identity.");
|
||||
Assert.That(pickupCollection.ScalarValue, Is.EqualTo(1));
|
||||
Assert.That(pickupCollection.CargoQuantityBefore, Is.Zero);
|
||||
Assert.That(pickupCollection.CargoQuantityAfter, Is.EqualTo(1));
|
||||
Assert.That(finalPickupEntityCount, Is.Zero,
|
||||
"Collected destruction loot remained in the provider-authored entity generation.");
|
||||
if (expectCargoRejection)
|
||||
{
|
||||
Assert.That(pickupRejection, Is.Not.Null,
|
||||
"Full cargo did not reject destruction salvage from its Ymir contact fact.");
|
||||
Assert.That(pickupRejectionEventCount, Is.EqualTo(1),
|
||||
"The first destruction-loot Ymir contact transaction must emit one rejection event.");
|
||||
Assert.That(pickupRejectionEventIds.Count, Is.EqualTo(pickupRejectionEventCount),
|
||||
"One Ymir fact identity emitted duplicate player rejection feedback.");
|
||||
Assert.That(pickupCollectionEventCount, Is.Zero,
|
||||
"Capacity-rejected salvage was also collected.");
|
||||
Assert.That(pickupRejection.ItemKey, Is.Not.Empty);
|
||||
Assert.That(pickupRejection.EventId, Does.StartWith("ymir-fact:"),
|
||||
"Rejection feedback must retain the consumed Ymir fact identity.");
|
||||
Assert.That(pickupRejection.Reason, Is.EqualTo("cargo-capacity"));
|
||||
Assert.That(pickupRejection.ScalarValue, Is.EqualTo(1));
|
||||
Assert.That(pickupRejection.CargoQuantityAfter,
|
||||
Is.EqualTo(pickupRejection.CargoQuantityBefore));
|
||||
Assert.That(finalPickupEntityCount, Is.EqualTo(1),
|
||||
"Capacity-rejected destruction loot disappeared from provider state.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.That(pickupCollection, Is.Not.Null,
|
||||
"Destruction-created salvage never reached cargo through a Ymir contact fact.");
|
||||
Assert.That(pickupCollectionEventCount, Is.EqualTo(1),
|
||||
"One destruction-loot Ymir contact transaction must emit one collection event.");
|
||||
Assert.That(pickupRejectionEventCount, Is.Zero,
|
||||
"Collectible destruction salvage was also rejected.");
|
||||
Assert.That(pickupCollection.ItemKey, Is.Not.Empty,
|
||||
"Destruction loot must retain its canonical typed item identity.");
|
||||
Assert.That(pickupCollection.EventId, Does.StartWith("ymir-fact:"),
|
||||
"Collection feedback must retain the consumed Ymir fact identity.");
|
||||
Assert.That(pickupCollection.ScalarValue, Is.EqualTo(1));
|
||||
Assert.That(pickupCollection.CargoQuantityBefore, Is.Zero);
|
||||
Assert.That(pickupCollection.CargoQuantityAfter, Is.EqualTo(1));
|
||||
Assert.That(finalPickupEntityCount, Is.Zero,
|
||||
"Collected destruction loot remained in the provider-authored entity generation.");
|
||||
}
|
||||
observedCombat = EveUnityCombatPresentation.Find(runtime.ActiveProjection);
|
||||
Assert.That(observedShot.TargetEntityIndex, Is.EqualTo(targetEntityIndex));
|
||||
Assert.That(observedShot.LockQuality, Is.GreaterThanOrEqualTo(0.99));
|
||||
|
|
@ -869,6 +916,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
Assert.That(fieldSplats, Is.Not.Null);
|
||||
WriteWitnessFacts(
|
||||
witnessProfile,
|
||||
gameplayScenario,
|
||||
provider.CurrentAssetBodyTransportKind?.ToString() ?? "",
|
||||
initialVersion,
|
||||
runtime.ActiveVersion,
|
||||
|
|
@ -915,8 +963,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
initialPickupEntityCount,
|
||||
finalPickupEntityCount,
|
||||
pickupCollectionEventCount,
|
||||
pickupRejectionEventCount,
|
||||
destructionPickupObserved,
|
||||
pickupCollection,
|
||||
pickupRejection,
|
||||
camera.transform.position,
|
||||
camera.transform.forward,
|
||||
playerMarker.transform.position,
|
||||
|
|
@ -949,6 +999,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
|
||||
private static void WriteWitnessFacts(
|
||||
string witnessProfile,
|
||||
string gameplayScenario,
|
||||
string assetBodyTransport,
|
||||
long initialVersion,
|
||||
long finalVersion,
|
||||
|
|
@ -995,8 +1046,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
int initialPickupEntityCount,
|
||||
int finalPickupEntityCount,
|
||||
int pickupCollectionEventCount,
|
||||
int pickupRejectionEventCount,
|
||||
bool destructionPickupObserved,
|
||||
EveUnityFeedbackEvent pickupCollection,
|
||||
EveUnityFeedbackEvent pickupRejection,
|
||||
Vector3 cameraPosition,
|
||||
Vector3 cameraForward,
|
||||
Vector3 playerPosition,
|
||||
|
|
@ -1011,9 +1064,11 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
{
|
||||
var path = Environment.GetEnvironmentVariable("EVEUNITY_WITNESS_FACTS_PATH");
|
||||
if (string.IsNullOrWhiteSpace(path)) return;
|
||||
var pickupEvent = pickupCollection ?? pickupRejection;
|
||||
var document = new WitnessFacts
|
||||
{
|
||||
witnessProfile = witnessProfile,
|
||||
gameplayScenario = gameplayScenario,
|
||||
providerAdvertisement = true,
|
||||
providerAssets = true,
|
||||
assetBodyTransport = assetBodyTransport,
|
||||
|
|
@ -1068,16 +1123,25 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
tractorBeamParticleSystemCount = tractorBeamParticleSystemCount,
|
||||
pickupCollection = pickupCollection != null && pickupCollectionEventCount == 1 &&
|
||||
pickupCollection.CargoQuantityAfter - pickupCollection.CargoQuantityBefore == pickupCollection.ScalarValue,
|
||||
destructionLoot = pickupCollection != null &&
|
||||
pickupCollection.EventId.StartsWith("ymir-fact:", StringComparison.Ordinal),
|
||||
pickupRejection = pickupRejection != null && pickupRejectionEventCount >= 1 &&
|
||||
pickupCollectionEventCount == 0 &&
|
||||
string.Equals(pickupRejection.Reason, "cargo-capacity", StringComparison.Ordinal) &&
|
||||
pickupRejection.CargoQuantityAfter == pickupRejection.CargoQuantityBefore,
|
||||
destructionLoot = (pickupCollection?.EventId ?? pickupRejection?.EventId ?? "")
|
||||
.StartsWith("ymir-fact:", StringComparison.Ordinal),
|
||||
initialPickupEntityCount = initialPickupEntityCount,
|
||||
finalPickupEntityCount = finalPickupEntityCount,
|
||||
pickupCollectionEventCount = pickupCollectionEventCount,
|
||||
pickupEventId = pickupCollection?.EventId ?? "",
|
||||
pickupItemKey = pickupCollection?.ItemKey ?? "",
|
||||
pickupQuantity = (float)(pickupCollection?.ScalarValue ?? 0),
|
||||
cargoQuantityBeforePickup = (float)(pickupCollection?.CargoQuantityBefore ?? 0),
|
||||
cargoQuantityAfterPickup = (float)(pickupCollection?.CargoQuantityAfter ?? 0),
|
||||
pickupRejectionEventCount = pickupRejectionEventCount,
|
||||
pickupRejectionEventId = pickupRejection?.EventId ?? "",
|
||||
pickupRejectionReason = pickupRejection?.Reason ?? "",
|
||||
cargoQuantityBeforeRejection = (float)(pickupRejection?.CargoQuantityBefore ?? 0),
|
||||
cargoQuantityAfterRejection = (float)(pickupRejection?.CargoQuantityAfter ?? 0),
|
||||
pickupEventId = pickupEvent?.EventId ?? "",
|
||||
pickupItemKey = pickupEvent?.ItemKey ?? "",
|
||||
pickupQuantity = (float)(pickupEvent?.ScalarValue ?? 0),
|
||||
cargoQuantityBeforePickup = (float)(pickupEvent?.CargoQuantityBefore ?? 0),
|
||||
cargoQuantityAfterPickup = (float)(pickupEvent?.CargoQuantityAfter ?? 0),
|
||||
cameraPosition = cameraPosition,
|
||||
cameraForward = cameraForward,
|
||||
playerPosition = playerPosition,
|
||||
|
|
@ -1103,6 +1167,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
private sealed class WitnessFacts
|
||||
{
|
||||
public string witnessProfile;
|
||||
public string gameplayScenario;
|
||||
public bool providerAdvertisement;
|
||||
public bool providerAssets;
|
||||
public string assetBodyTransport;
|
||||
|
|
@ -1154,10 +1219,16 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
public float tractorReleasedPower;
|
||||
public int tractorBeamParticleSystemCount;
|
||||
public bool pickupCollection;
|
||||
public bool pickupRejection;
|
||||
public bool destructionLoot;
|
||||
public int initialPickupEntityCount;
|
||||
public int finalPickupEntityCount;
|
||||
public int pickupCollectionEventCount;
|
||||
public int pickupRejectionEventCount;
|
||||
public string pickupRejectionEventId;
|
||||
public string pickupRejectionReason;
|
||||
public float cargoQuantityBeforeRejection;
|
||||
public float cargoQuantityAfterRejection;
|
||||
public string pickupEventId;
|
||||
public string pickupItemKey;
|
||||
public float pickupQuantity;
|
||||
|
|
|
|||
|
|
@ -84,6 +84,17 @@ The remaining visual controls (fog parameters, stellar tint, ambient intensity,
|
|||
histogram exposure, grading, and temporal settings) remain provider-owned
|
||||
levers; this proof does not claim a final art grade.
|
||||
|
||||
The released `0.3.63` warm rejection witness in
|
||||
`artifacts/aetheria-daemon-cargo-rejection-0363-warm` selects the daemon-owned
|
||||
`cargo-capacity-rejection-proof` scenario. The controlled ship begins with all
|
||||
generated cargo bays filled from typed item volume and bay capacity. Combat
|
||||
creates the salvage pickup normally; player Ymir Begin contacts emit
|
||||
`pickup.rejected` with `cargo-capacity`, preserve cargo at `5` items, and leave
|
||||
the pickup live. The final run observed two later player recontacts with two
|
||||
distinct `ymir-fact:` identities and no player collection event. The generic
|
||||
witness correlates feedback through advertised SoA `SourceIndex` and feedback
|
||||
`TargetEntityIndex`; events from other ships are not attributed to the pilot.
|
||||
|
||||
Current released-package evidence is in
|
||||
`artifacts/aetheria-daemon-mapped-body-cold`; despite that retained directory
|
||||
name, `runtime-witness.warm.json` is the current passing run. The warm PlayMode test
|
||||
|
|
@ -251,8 +262,8 @@ changes cargo from `0` to `1`, and emits one `pickup.collected` feedback event
|
|||
whose identity begins with `ymir-fact:`. Aetheria's Ymir body
|
||||
mapping excludes stations and non-ship world bodies from pickup collision facts,
|
||||
so ambient station contact cannot become a cargo writer. Capacity rejection and
|
||||
its `cargo-capacity` reason are covered by the daemon smoke, not claimed as a
|
||||
released-client live scenario here.
|
||||
its `cargo-capacity` reason are also proven by the released-client live rejection
|
||||
scenario described above.
|
||||
Effect tuning remains provider art work; the runtime must not compensate with
|
||||
an Aetheria-specific beam.
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ param(
|
|||
[string] $OutputDirectory = "artifacts\aetheria-daemon",
|
||||
[ValidateSet("auto", "cold", "warm")]
|
||||
[string] $CacheState = "auto",
|
||||
[ValidateSet("released-client-proof", "cargo-capacity-rejection-proof")]
|
||||
[string] $GameplayScenario = "released-client-proof",
|
||||
[switch] $SkipAssetBundleBuild
|
||||
)
|
||||
|
||||
|
|
@ -168,6 +170,7 @@ $daemonArguments = @(
|
|||
"--client-cultmesh-port", $Port,
|
||||
"--tick-interval-ms", 20,
|
||||
"--fixed-delta-ms", 20,
|
||||
"--terminus-scenario", $GameplayScenario,
|
||||
"--no-odin-announcements"
|
||||
)
|
||||
$env:AETHERIA_TRACE_EVE_SNAPSHOTS = "1"
|
||||
|
|
@ -183,6 +186,7 @@ $env:EVEUNITY_ASSET_CACHE_PATH = $assetCachePath
|
|||
$env:EVEUNITY_WITNESS_FACTS_PATH = $factsPath
|
||||
$env:EVEUNITY_PROVIDER_READY_PATH = $providerReadyPath
|
||||
$env:EVEUNITY_WITNESS_PROFILE = $witnessProfile
|
||||
$env:EVEUNITY_WITNESS_GAMEPLAY_SCENARIO = $GameplayScenario
|
||||
$arguments = @(
|
||||
"-batchmode", "-projectPath", $projectRoot,
|
||||
"-runTests", "-testPlatform", "PlayMode",
|
||||
|
|
@ -271,11 +275,21 @@ try {
|
|||
@($presentedCelestials | Where-Object { $_.intersectsPilotFrustum }).Count -le 0) {
|
||||
throw "Live witness did not prove provider-owned celestial bodies and asteroids reached the generic scene and intersected the pilot frustum. bodies=$($presentedBodies.Count) asteroids=$($presentedAsteroids.Count) invalid=$($invalidCelestials.Count)"
|
||||
}
|
||||
if ($witnessProfile -eq "full-session-gameplay" -and
|
||||
(-not $facts.combatPresentation -or [string]::IsNullOrWhiteSpace($facts.shotId) -or
|
||||
[double]$facts.lockProgress -le 0.99 -or -not $facts.pickupCollection -or
|
||||
-not $facts.destructionLoot -or [int]$facts.pickupCollectionEventCount -ne 1)) {
|
||||
throw "Full-session witness did not prove combat and exactly-once destruction-loot Ymir contact collection."
|
||||
if ($witnessProfile -eq "full-session-gameplay") {
|
||||
if (-not $facts.combatPresentation -or [string]::IsNullOrWhiteSpace($facts.shotId) -or
|
||||
[double]$facts.lockProgress -le 0.99 -or -not $facts.destructionLoot) {
|
||||
throw "Full-session witness did not prove combat and Ymir-contact destruction loot."
|
||||
}
|
||||
if ($GameplayScenario -eq "cargo-capacity-rejection-proof") {
|
||||
if ($facts.gameplayScenario -ne $GameplayScenario -or -not $facts.pickupRejection -or
|
||||
[int]$facts.pickupRejectionEventCount -lt 1 -or [int]$facts.pickupCollectionEventCount -ne 0 -or
|
||||
$facts.pickupRejectionReason -ne "cargo-capacity" -or
|
||||
[double]$facts.cargoQuantityBeforeRejection -ne [double]$facts.cargoQuantityAfterRejection) {
|
||||
throw "Full-session rejection witness did not prove player cargo-capacity refusal from distinct Ymir contact facts."
|
||||
}
|
||||
} elseif (-not $facts.pickupCollection -or [int]$facts.pickupCollectionEventCount -ne 1) {
|
||||
throw "Full-session collection witness did not prove exactly-once player destruction-loot collection from a Ymir contact fact."
|
||||
}
|
||||
}
|
||||
$releaseLock = Get-Content -LiteralPath (Join-Path $repoRoot "ReleaseConsumerProject\Packages\packages-lock.json") -Raw | ConvertFrom-Json
|
||||
$releasedPackageClient = $projectRoot -eq "ReleaseConsumerProject"
|
||||
|
|
@ -379,7 +393,11 @@ try {
|
|||
}
|
||||
)
|
||||
authority = if ($witnessProfile -eq "full-session-gameplay") {
|
||||
"released-generic-runtime-observes-provider-assets-authoritative-gameplay-receipts-ymir-contact-collection-and-camera-channel-separation"
|
||||
if ($GameplayScenario -eq "cargo-capacity-rejection-proof") {
|
||||
"released-generic-runtime-observes-provider-assets-authoritative-gameplay-receipts-ymir-contact-capacity-rejection-and-camera-channel-separation"
|
||||
} else {
|
||||
"released-generic-runtime-observes-provider-assets-authoritative-gameplay-receipts-ymir-contact-collection-and-camera-channel-separation"
|
||||
}
|
||||
} else {
|
||||
"released-generic-runtime-cold-loads-provider-assets-lowers-playable-world-and-preserves-camera-channel-separation"
|
||||
}
|
||||
|
|
@ -396,6 +414,6 @@ try {
|
|||
finally {
|
||||
if ($null -ne $unity -and -not $unity.HasExited) { Stop-Process -Id $unity.Id -Force -ErrorAction SilentlyContinue }
|
||||
if ($null -ne $daemon -and -not $daemon.HasExited) { Stop-Process -Id $daemon.Id -Force }
|
||||
Remove-Item Env:EVEUNITY_RENDEZVOUS_ENDPOINT, Env:EVEUNITY_PROVIDER_ENDPOINT, Env:EVEUNITY_PROVIDER_ID, Env:EVEUNITY_SURFACE_ID, Env:EVEUNITY_REPLICA_PATH, Env:EVEUNITY_AETHERIA_CAPTURE_PATH, Env:EVEUNITY_AETHERIA_MAP_CAPTURE_PATH, Env:EVEUNITY_DISABLE_AUTO_LAUNCHER, Env:EVEUNITY_ASSET_CACHE_PATH, Env:EVEUNITY_WITNESS_FACTS_PATH, Env:EVEUNITY_PROVIDER_READY_PATH, Env:EVEUNITY_WITNESS_PROFILE -ErrorAction SilentlyContinue
|
||||
Remove-Item Env:EVEUNITY_RENDEZVOUS_ENDPOINT, Env:EVEUNITY_PROVIDER_ENDPOINT, Env:EVEUNITY_PROVIDER_ID, Env:EVEUNITY_SURFACE_ID, Env:EVEUNITY_REPLICA_PATH, Env:EVEUNITY_AETHERIA_CAPTURE_PATH, Env:EVEUNITY_AETHERIA_MAP_CAPTURE_PATH, Env:EVEUNITY_DISABLE_AUTO_LAUNCHER, Env:EVEUNITY_ASSET_CACHE_PATH, Env:EVEUNITY_WITNESS_FACTS_PATH, Env:EVEUNITY_PROVIDER_READY_PATH, Env:EVEUNITY_WITNESS_PROFILE, Env:EVEUNITY_WITNESS_GAMEPLAY_SCENARIO -ErrorAction SilentlyContinue
|
||||
Remove-Item Env:AETHERIA_TRACE_EVE_SNAPSHOTS -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue