Restore fossil environment material ownership

This commit is contained in:
Meta Vens 2026-07-16 11:35:14 +01:00
commit 90a6c8705b
6 changed files with 74 additions and 23 deletions

View file

@ -734,10 +734,10 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
world.Props["postProcessProfileAssetRef"] == "profile.environment.flight" &&
world.Props["reflectionIntensity"] == "1" &&
world.Props["ambientLightIntensity"] == "1.46" &&
world.Props["keyLightDirection"] == "0.4,-1,0.25" &&
world.Props["keyLightColor"] == "1,0.95,0.9" &&
world.Props["keyLightIntensity"] == "0.75",
"playable world must advertise the provider-owned fossil skybox, reflection, and key-light response");
!world.Props.ContainsKey("keyLightDirection") &&
!world.Props.ContainsKey("keyLightColor") &&
!world.Props.ContainsKey("keyLightIntensity"),
"playable world must advertise the provider-owned fossil skybox and reflection without inventing a scene light");
Require(aim.Props["controlledEntityIndex"] == "0" &&
aim.Props["convergenceTargetEntityId"] == run.EntityRecordKey(0, 1) &&
aim.Props["minimumConvergenceDistance"] == "50",

View file

@ -791,10 +791,10 @@ static void RequireSharedEvePackagesImportedFromEveRepo(string root)
var requiredManifestSymbols = new[]
{
"\"org.gamecult.cultlib\": \"https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.13\"",
"\"org.gamecult.cultlib\": \"https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.14\"",
"\"org.gamecult.eve.plugin-fields\": \"https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.3\"",
"\"org.gamecult.eve.surface\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.2\"",
"\"org.gamecult.eve.unity-scene\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.49\""
"\"org.gamecult.eve.unity-scene\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.51\""
};
var missingManifestSymbols = requiredManifestSymbols
@ -810,10 +810,10 @@ static void RequireSharedEvePackagesImportedFromEveRepo(string root)
var requiredLockSymbols = new[]
{
"cultlib-unity-v1.0.13",
"cultlib-unity-v1.0.14",
"eve-plugin-fields-unity-v0.2.3",
"eveunity-surface-v0.2.2",
"eveunity-scene-v0.3.49",
"eveunity-scene-v0.3.51",
"\"source\": \"git\""
};
@ -12179,11 +12179,11 @@ static void RequireDaemonVersePublication(string root)
}
if (daemonZoneGenerator.Contains("fallbackCargo", StringComparison.Ordinal) ||
!daemonLoadoutGenerator.Contains("IReadOnlyList<string> scenarioCargo)", StringComparison.Ordinal) ||
!daemonLoadoutGenerator.Contains("foreach (var key in scenarioCargo ?? Array.Empty<string>())", StringComparison.Ordinal))
daemonZoneGenerator.Contains("scenarioCargo", StringComparison.Ordinal) ||
daemonLoadoutGenerator.Contains("scenarioCargo", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Daemon zone generation must treat declared scenario cargo as authored state, not as a catalog-generation fallback.");
"Daemon zone generation must not restore invented scenario cargo as a second item-identity authority.");
}
var requiredDaemonHostSymbols = new[]
@ -12197,7 +12197,7 @@ static void RequireDaemonVersePublication(string root)
"AetheriaDaemonZoneGenerator.WritePlayableRunAsync(",
"node.MutableDocument<AetheriaRuntimeVerseAuthorityPolicyDocument>(AetheriaRuntimeVerseRecordKeys.VerseAuthorityPolicy)",
"node.MutableDocument<AetheriaRuntimeStarbridgeScenarioDocument>(AetheriaRuntimeVerseRecordKeys.StarbridgeScenarioLatest)",
"node.MutableDocument<AetheriaRuntimeStarbridgeSessionDocument>(AetheriaRuntimeVerseRecordKeys.StarbridgeSessionLatest)",
"MutableDocument<AetheriaRuntimeStarbridgeSessionDocument>(AetheriaRuntimeVerseRecordKeys.StarbridgeSessionLatest)",
"StarbridgeScenario = starbridgeScenario",
"StarbridgeSession = starbridgeSession",
"BuildPublications = buildPublications",

View file

@ -149,6 +149,22 @@ namespace Aetheria.Editor
foreach (var effect in tractorEffects)
UnityEngine.Object.DestroyImmediate(effect, true);
var embeddedShieldVisuals = root.GetComponentsInChildren<Renderer>(true)
.Where(renderer => renderer != null && renderer.sharedMaterials.Any(material =>
material != null && string.Equals(
material.shader?.name,
"Aetheria/Shield",
StringComparison.Ordinal)))
.Select(renderer => renderer.gameObject)
.Distinct()
.ToArray();
if (embeddedShieldVisuals.Length == 0)
throw new InvalidOperationException(
$"Ship presentation source '{assetKey}' has no embedded shield visual to externalize.");
foreach (var visual in embeddedShieldVisuals)
UnityEngine.Object.DestroyImmediate(visual, true);
}
private static void StripNonPresentationScripts(GameObject root)
@ -203,10 +219,11 @@ namespace Aetheria.Editor
if (IsUniversalMaterial(source))
continue;
var key = PresentationMaterialKey(source, particle);
var unlit = particle || IsUnlitMaterial(source);
var key = PresentationMaterialKey(source, particle, unlit);
if (!generated.TryGetValue(key, out var replacement))
{
replacement = CreatePresentationMaterial(source, particle, key);
replacement = CreatePresentationMaterial(source, particle, unlit, key);
generated.Add(key, replacement);
}
materials[index] = replacement;
@ -252,11 +269,13 @@ namespace Aetheria.Editor
return material;
}
private static Material CreatePresentationMaterial(Material source, bool particle, string key)
private static Material CreatePresentationMaterial(Material source, bool particle, bool unlit, string key)
{
var shaderName = particle
? "Universal Render Pipeline/Particles/Unlit"
: "Universal Render Pipeline/Lit";
: unlit
? "Universal Render Pipeline/Unlit"
: "Universal Render Pipeline/Lit";
var shader = Shader.Find(shaderName);
if (shader == null || !shader.isSupported)
throw new InvalidOperationException($"Aetheria provider presentation shader is unavailable: {shaderName}");
@ -361,11 +380,18 @@ namespace Aetheria.Editor
(material.shader.name.StartsWith("Universal Render Pipeline/", StringComparison.Ordinal) ||
string.Equals(material.GetTag("RenderPipeline", true, ""), "UniversalPipeline", StringComparison.Ordinal));
private static string PresentationMaterialKey(Material material, bool particle)
private static bool IsUnlitMaterial(Material material)
{
var shaderName = material.shader?.name ?? "";
return shaderName.IndexOf("Unlit", StringComparison.OrdinalIgnoreCase) >= 0 ||
shaderName.IndexOf("Sprite", StringComparison.OrdinalIgnoreCase) >= 0;
}
private static string PresentationMaterialKey(Material material, bool particle, bool unlit)
{
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(material, out string guid, out long localId))
throw new InvalidOperationException($"Provider material has no stable asset identity: {material.name}");
return $"{guid}-{localId}-{(particle ? "particle" : "lit")}";
return $"{guid}-{localId}-{(particle ? "particle" : unlit ? "unlit" : "lit")}";
}
private static void CopyTexture(Material source, Material target, string targetProperty, params string[] sourceProperties)

View file

@ -624,9 +624,6 @@ namespace GameCult.Aetheria.State.Verse
["reflectionAssetRef"] = "texture.environment.reflection",
["reflectionIntensity"] = "1",
["postProcessProfileAssetRef"] = "profile.environment.flight",
["keyLightDirection"] = "0.4,-1,0.25",
["keyLightColor"] = "1,0.95,0.9",
["keyLightIntensity"] = "0.75",
["viewId"] = "pilot",
["excludedRenderChannels"] = "map",
["playerEntityId"] = playerEntityId,

View file

@ -947,6 +947,34 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
with enabled provider renderers; seven celestial instances intersected the
pilot frustum. This proves projection and lowering, not final art parity.
### Native Environment Lighting Contract
- Owner: Aetheria owns the fossil scene's skybox, ambient intensity, custom
reflection cubemap, and each provider material's lit/unlit semantics. EveUnity
owns only the reversible application of those advertised environment facts.
- Inputs: `ARPG.unity` render settings, `Assets/Materials/Skybox.mat`,
`Assets/Textures/studio2.hdr`, and source shader lighting models. The fossil
scene contains no Unity `Light` component.
- Outputs: the world surface advertises skybox ambient intensity `1.46`, custom
reflection intensity `1`, and no key light. Provider material conversion maps
particle sources to URP Particles/Unlit, unlit or sprite sources to URP/Unlit,
and genuinely lit sources to URP/Lit.
- Derived state: Unity ambient probes, custom-reflection state, runtime material
instances, and shader keywords are client presentation state.
- Forbidden writers: the daemon cannot invent a directional light to compensate
for broken material conversion. EveUnity cannot add product lighting or infer
shader models from Aetheria asset names.
- Cut line: the invented `0.4,-1,0.25` warm key light is deleted. The embedded
fossil shield renderer is removed from ship presentation prefabs because its
dithered collision response is already externalized as the provider-owned,
receipt-driven shield-impact effect; it cannot remain permanently visible
after generic material conversion.
- Verification layer: daemon smoke negatively proves that no key-light fields
are published. The released cold witness records zero directional intensity,
no player `Shield Visual`, twelve remaining player renderers, and a map-channel
material using `Universal Render Pipeline/Unlit`; visual inspection shows ship
hulls instead of opaque shield ellipsoids and readable map glyphs.
### Gravity Fog Presentation Contract
- Owner: Aetheria owns gravity/fog brush placement and values, authored volume

View file

@ -32,13 +32,13 @@ validation proves contract shape, not wiring, exclusivity, lowering, or parity.
| Trade | trade UI/economy | daemon | stock, quote, purchase commands | generic UI | daemon derives station, stock row, catalog item, current price, quantity, credits, target capacity, and hull-vs-cargo result; Eve advertises only currently available stock actions; generic Unity live witness receives an authoritative purchase receipt while paused | sell flow, quote expiry, contested stock, and full trade surface parity |
| Action bar/input | input shell and rebinding UI | daemon advertises actions; client owns bindings | input capability and action descriptors | universal binding UI | contracts wired; cargo actions are catalog-filtered to consumables the current actor can actually activate, including suppression of active non-stackable items | dynamic equipment behavior validity and runtime action-bar quantity/fill proof |
| Pilot camera | ARPG follow/dock cameras | Eve view semantics + runtime | camera rig and semantic view id | EveUnity camera rig | daemon dock state selects distinct contracts instead of collapsing both fossil cameras: undocked flight advertises the entity-forward `Third Person Rig` framing (distance 30, screen 0.64/0.19, FOV 60, lens 1-4096) and the shared `aim.presentation` convergence point as its look-at; Aetheria translates the fossil Cinemachine top-origin screen Y of `0.81` to Eve's bottom-origin viewport Y of `0.19`; EveUnity solves native pose while preserving both optical aim and advertised entity framing, without provider types; the retained dock projection remains separate | reproduce the dock camera's separate follow/look-at subjects, blend timeline, noise, and reconnect state |
| Render channels | Unity layers/culling masks | provider surface policy + runtime asset variant | semantic channel exclusions | EveUnity native layer mapping | wired, exclusive, and lowered: the pilot surface excludes the semantic `map` channel; the Unity asset variant maps `map` to its authored native layer; EveUnity subtracts that layer from the configured camera mask; portable state contains no Unity layer or mask; the released warm witness proves the pilot camera excludes map glyphs while the map camera retains them | preserve the channel proof while world presentation and transport continue to evolve |
| Render channels | Unity layers/culling masks | provider surface policy + runtime asset variant | semantic channel exclusions | EveUnity native layer mapping | wired, exclusive, and lowered: the pilot surface excludes the semantic `map` channel; the Unity asset variant maps `map` to its authored native layer; EveUnity subtracts that layer from the configured camera mask; portable state contains no Unity layer or mask; provider conversion preserves the source icon shader's unlit lighting model; the released witness proves the pilot camera excludes map glyphs while the map camera retains readable white glyphs without a scene light | preserve the channel proof while world presentation and transport continue to evolve |
| Minimap/tactical map | `ZoneRenderer`, map cameras | daemon world facts | explicit map projections | Unity and Electron | facts fragmented; generic lowerers absent | same state in both runtimes |
| Celestial world | `ZoneRenderer`, body/asteroid prefab scripts | daemon render projection plus provider assets | ordinary generic Eve entity SoA rows | EveUnity generic entity/prefab lowerer | wired, exclusive, projected, and lowered: daemon orbit and terrain queries emit stable presentation-only identities for suns, planets, gas giants, and live asteroid instances; provider packaging extracts only each advertised visual child and strips fossil scripts/physics; released EveUnity `0.3.51` applies semantic scale around provider-authored local scale and contains no celestial or Aetheria branch. The released warm witness records eleven bodies and eighteen asteroids with provider refs and enabled renderers, including seven celestial instances intersecting the pilot frustum | body material/light readability, close-range composition matching the fossil, asteroid variation, and Electron lowering |
| Fields/fog/gravity | zone renderer and shaders | daemon/plugin semantics | Fields plugin documents | EveUnity fields lowerer | daemon publishes canonical gravity/fog splat layers through `field.volume3d`; Eve Fields `0.2.3` owns portable `PowerPulse` and `AnimatedRadialCosine` semantics; Aetheria emits the fossil global animated simplex/cellular/ambient stack, body-owned radial waves, and separate negative gameplay `gravity.height` and positive visual `fog.surface_height`; the field viewport matches the fossil splat cameras' 2000-by-2000 orthographic footprint while the larger 3000-scale brush parent remains brush coverage, not sampling extent; daemon entities follow fossil terrain height plus hull grid offset after Ymir XZ motion; released EveUnity `0.3.51` lowers the authored 2048/256/512 layer topology, provider shader ABI, pre-postprocess URP pass, authoritative flow time, explicit camera matrix, and float32 packed-output diagnostics without provider code. Translating the fossil camera's top-origin screen Y into Eve's bottom-origin convention places the camera above the field and restores a live fog horizon with the original `-36.17` noise amplitude; the deterministic Terminus sun now owns a blue fog tint instead of the unrelated brown calibration, and both camera-channel screenshots pass without runtime-specific color rules | restore readable patch structure, gravity waves, surrounding world geometry, and final luminance/composition parity; visual parity remains explicitly open |
| HUD/schematic | ARPG HUD and `SchematicDisplay` | Eve composition from daemon facts | semantic status/components | EveUnity UI | provider-authored transparent cockpit is wired, projected, and lowered through generic UI Toolkit panes, metrics, and progress components for hull, shield, capacitor, temperature, cargo, weapon cooldown/groups, selected target, target lock, target shield, and target hull; the original armor/equipment schematic and transient warning choreography remain reference-only | schematic topology, warning timelines, dynamic action state, and composited live capture |
| Hit/lock/warnings | `ActionGameManager` feedback | daemon event facts | transient feedback stream | VFX/UI/audio | bounded deduplicated game-event ledger and Eve `feedback.stream` wired for pickup outcomes, projectile launch/impact, entity damage, alive-to-dead destruction, authoritative weapon reload chronology, and episode-deduplicated `weapon.fire.refused` facts for insufficient energy or missing ammunition; refusal identity, authored weapon, target, reason, and remaining magazine state are runtime-independent cockpit inputs | add lock transitions, thermal and docking event families, negative transaction fixtures, and runtime effect consumption |
| Assets | `Assets/Resources` | Aetheria provider | asset catalog + CultMesh CDN | runtime cache/load | provider manifests name deterministic bundle-internal paths; the authoring build derives presentation-only prefabs, strips missing and non-Eve `MonoBehaviour` components, verifies both saved prefabs and the loaded bundle, and publishes the immutable bundle through CultMesh; the released cold profile starts empty, receives and verifies the 55,529,775-byte body through `cultmesh.content.v1`, atomically promotes the exact hash with no partial, and lowers rendered provider identity; the warm profile reuses the committed body. Negotiated mapped/zero-copy delivery remains unproven | negotiated mapped/network body selection and same-machine zero-copy proof |
| Assets | `Assets/Resources` | Aetheria provider | asset catalog + CultMesh CDN | runtime cache/load | provider manifests name deterministic bundle-internal paths; the authoring build derives presentation-only prefabs, strips missing and non-Eve `MonoBehaviour` components, externalizes embedded tractor and shield event visuals, preserves particle/unlit/lit source semantics during URP conversion, verifies both saved prefabs and the loaded bundle, and publishes the immutable bundle through CultMesh; the released cold profile starts empty, receives and verifies the 55,533,143-byte body through `cultmesh.content.v1`, atomically promotes the exact hash with no partial, and lowers rendered provider identity; the warm profile reuses the committed body. Negotiated mapped/zero-copy delivery remains unproven | negotiated mapped/network body selection and same-machine zero-copy proof |
| Save/resume | original persistence paths | daemon/CultCache | published current state | clients rehydrate | owner-exists; not parity-proven | mid-loop restart |
| Terminus loop | original game composition | daemon/Ymir/Eve | pilot surface | Aetheria.Unity | live minimum-loop proof: generic Unity discovers the provider, lowers the pilot world/assets, single-steps dock and undock while paused, buys daemon-priced station stock, single-steps Ymir salvage collection, resumes, moves, targets, fires, and presents authoritative receipts/shot outcome; the released full-session profile starts with no pickup, destroys a deterministic target, observes its canonical cargo drop, tractors it into a Ymir Begin contact, and proves exactly one `ymir-fact:` collection receipt plus cargo `0` to `1` | richer trade content, docking transitions, and sustained combat encounters |
| Starbridge | original/shared architecture intent | one daemon session | pilot + commander surfaces | Unity + Electron | commander task board and autonomous explore execution wired; Electron Eve lowering and split-target proof missing | commander orders plus four pilots and autonomous workers |