Migrate Aetheria geometry ownership

This commit is contained in:
Meta Vens 2026-07-22 04:08:57 +01:00
commit 26948f81b6
6 changed files with 119 additions and 49 deletions

View file

@ -9,7 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\Aetheria.State\Aetheria.State.csproj" />
<ProjectReference Include="..\..\CultLib\src\GameCult.Geometry\GameCult.Geometry.csproj" />
<ProjectReference Include="..\..\CultMath\src\CultMath\CultMath.csproj" />
<Compile Include="..\Assets\Scripts\ServerShared\YmirPhysicsContracts.cs" Link="YmirPhysicsContracts.cs" />
</ItemGroup>
</Project>

View file

@ -2,11 +2,18 @@ using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using Aetheria.State;
using GameCult.Geometry;
using CultMath;
using GameCult.Aetheria.State.Verse;
using MessagePack;
using MessagePack.Formatters;
using MessagePack.Resolvers;
MessagePackSerializer.DefaultOptions = MessagePackSerializerOptions.Standard.WithResolver(
CompositeResolver.Create(
new IMessagePackFormatter[] { new CultMathFloat2Formatter(), new CultMathFloat3Formatter() },
new IFormatterResolver[] { StandardResolver.Instance }));
var options = FreezeOptions.Parse(args);
VerifyCultMathVectorSerializationCompatibility();
Directory.CreateDirectory(options.OutputDirectory);
Directory.CreateDirectory(Path.GetDirectoryName(options.StatePath) ?? ".");
@ -841,14 +848,27 @@ static YmirFixtureSphereCastHit[] ToFixtureSphereCastHits(IReadOnlyList<YmirSphe
.ToArray();
}
static CultVec2 ToFixtureVec2(YmirVec2 value)
static float2 ToFixtureVec2(YmirVec2 value)
{
return new CultVec2(value.x, value.y);
return new float2(value.x, value.y);
}
static CultVec3 ToFixtureVec3(YmirVec3 value)
static float3 ToFixtureVec3(YmirVec3 value)
{
return new CultVec3(value.x, value.y, value.z);
return new float3(value.x, value.y, value.z);
}
static void VerifyCultMathVectorSerializationCompatibility()
{
var old2 = MessagePackSerializer.Serialize(new LegacyFixtureVec2(1.25f, -2.5f));
var current2 = MessagePackSerializer.Serialize(new float2(1.25f, -2.5f));
if (!old2.AsSpan().SequenceEqual(current2))
throw new InvalidOperationException("CultMath.float2 changed the frozen MessagePack vector layout.");
var old3 = MessagePackSerializer.Serialize(new LegacyFixtureVec3(1.25f, -2.5f, 4.75f));
var current3 = MessagePackSerializer.Serialize(new float3(1.25f, -2.5f, 4.75f));
if (!old3.AsSpan().SequenceEqual(current3))
throw new InvalidOperationException("CultMath.float3 changed the frozen MessagePack vector layout.");
}
static YmirWorld YmirWorldOf(params YmirPhysicsBody[] bodies)
@ -1143,6 +1163,52 @@ internal sealed class FreezeArtifact
public string MessagePack { get; set; } = "";
}
[MessagePackObject]
internal readonly record struct LegacyFixtureVec2(
[property: Key(0)] float x,
[property: Key(1)] float y);
[MessagePackObject]
internal readonly record struct LegacyFixtureVec3(
[property: Key(0)] float x,
[property: Key(1)] float y,
[property: Key(2)] float z);
internal sealed class CultMathFloat2Formatter : IMessagePackFormatter<float2>
{
public void Serialize(ref MessagePackWriter writer, float2 value, MessagePackSerializerOptions options)
{
writer.WriteArrayHeader(2);
writer.Write(value.x);
writer.Write(value.y);
}
public float2 Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
var count = reader.ReadArrayHeader();
if (count != 2) throw new MessagePackSerializationException($"Expected float2 array length 2, got {count}.");
return new float2(reader.ReadSingle(), reader.ReadSingle());
}
}
internal sealed class CultMathFloat3Formatter : IMessagePackFormatter<float3>
{
public void Serialize(ref MessagePackWriter writer, float3 value, MessagePackSerializerOptions options)
{
writer.WriteArrayHeader(3);
writer.Write(value.x);
writer.Write(value.y);
writer.Write(value.z);
}
public float3 Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
var count = reader.ReadArrayHeader();
if (count != 3) throw new MessagePackSerializationException($"Expected float3 array length 3, got {count}.");
return new float3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
}
[MessagePackObject]
internal sealed class YmirQueryFixtureSet
{
@ -1167,12 +1233,12 @@ internal sealed class YmirQueryFixturePayload
[Key(0)] public float? DeltaTime { get; set; }
[Key(1)] public float? Radius { get; set; }
[Key(2)] public float? Distance { get; set; }
[Key(3)] public CultVec2? Center2 { get; set; }
[Key(4)] public CultVec3? Center3 { get; set; }
[Key(5)] public CultVec2? Origin2 { get; set; }
[Key(6)] public CultVec3? Origin3 { get; set; }
[Key(7)] public CultVec2? Direction2 { get; set; }
[Key(8)] public CultVec3? Direction3 { get; set; }
[Key(3)] public float2? Center2 { get; set; }
[Key(4)] public float3? Center3 { get; set; }
[Key(5)] public float2? Origin2 { get; set; }
[Key(6)] public float3? Origin3 { get; set; }
[Key(7)] public float2? Direction2 { get; set; }
[Key(8)] public float3? Direction3 { get; set; }
[Key(9)] public YmirFixtureWorld? World { get; set; }
[Key(10)] public YmirFixtureSphereBody[] SphereBodies { get; set; } = Array.Empty<YmirFixtureSphereBody>();
[Key(11)] public YmirFixtureCircleOverlapHit[] CircleOverlapHits { get; set; } = Array.Empty<YmirFixtureCircleOverlapHit>();
@ -1194,9 +1260,9 @@ internal sealed class YmirFixtureWorld
internal sealed class YmirFixtureBody
{
[Key(0)] public string Id { get; set; } = "";
[Key(1)] public CultVec2 Position { get; set; }
[Key(2)] public CultVec2 Velocity { get; set; }
[Key(3)] public CultVec2 Direction { get; set; }
[Key(1)] public float2 Position { get; set; }
[Key(2)] public float2 Velocity { get; set; }
[Key(3)] public float2 Direction { get; set; }
[Key(4)] public float AngularVelocity { get; set; }
[Key(5)] public float Torque { get; set; }
[Key(6)] public float MomentOfInertia { get; set; }
@ -1210,7 +1276,7 @@ internal sealed class YmirFixtureBody
internal sealed class YmirFixtureSphereBody
{
[Key(0)] public string Id { get; set; } = "";
[Key(1)] public CultVec3 Position { get; set; }
[Key(1)] public float3 Position { get; set; }
[Key(2)] public float Radius { get; set; }
}
@ -1218,7 +1284,7 @@ internal sealed class YmirFixtureSphereBody
internal sealed class YmirFixtureField
{
[Key(0)] public string Id { get; set; } = "";
[Key(1)] public CultVec2 Position { get; set; }
[Key(1)] public float2 Position { get; set; }
[Key(2)] public float Strength { get; set; }
[Key(3)] public float Radius { get; set; }
}
@ -1228,8 +1294,8 @@ internal sealed class YmirFixtureContact
{
[Key(0)] public string BodyA { get; set; } = "";
[Key(1)] public string BodyB { get; set; } = "";
[Key(2)] public CultVec2 Point { get; set; }
[Key(3)] public CultVec2 Normal { get; set; }
[Key(2)] public float2 Point { get; set; }
[Key(3)] public float2 Normal { get; set; }
[Key(4)] public float Penetration { get; set; }
[Key(5)] public float RelativeSpeed { get; set; }
}
@ -1238,8 +1304,8 @@ internal sealed class YmirFixtureContact
internal sealed class YmirFixtureCircleOverlapHit
{
[Key(0)] public string BodyId { get; set; } = "";
[Key(1)] public CultVec2 Point { get; set; }
[Key(2)] public CultVec2 Normal { get; set; }
[Key(1)] public float2 Point { get; set; }
[Key(2)] public float2 Normal { get; set; }
[Key(3)] public float Penetration { get; set; }
[Key(4)] public float Distance { get; set; }
}
@ -1248,8 +1314,8 @@ internal sealed class YmirFixtureCircleOverlapHit
internal sealed class YmirFixtureSphereOverlapHit
{
[Key(0)] public string BodyId { get; set; } = "";
[Key(1)] public CultVec3 Point { get; set; }
[Key(2)] public CultVec3 Normal { get; set; }
[Key(1)] public float3 Point { get; set; }
[Key(2)] public float3 Normal { get; set; }
[Key(3)] public float Penetration { get; set; }
[Key(4)] public float Distance { get; set; }
}
@ -1258,8 +1324,8 @@ internal sealed class YmirFixtureSphereOverlapHit
internal sealed class YmirFixtureCircleCastHit
{
[Key(0)] public string BodyId { get; set; } = "";
[Key(1)] public CultVec2 Point { get; set; }
[Key(2)] public CultVec2 Normal { get; set; }
[Key(1)] public float2 Point { get; set; }
[Key(2)] public float2 Normal { get; set; }
[Key(3)] public float Distance { get; set; }
}
@ -1267,8 +1333,8 @@ internal sealed class YmirFixtureCircleCastHit
internal sealed class YmirFixtureSphereCastHit
{
[Key(0)] public string BodyId { get; set; } = "";
[Key(1)] public CultVec3 Point { get; set; }
[Key(2)] public CultVec3 Normal { get; set; }
[Key(1)] public float3 Point { get; set; }
[Key(2)] public float3 Normal { get; set; }
[Key(3)] public float Distance { get; set; }
}

View file

@ -19,6 +19,7 @@
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Mesh\GameCult.Mesh.csproj" />
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Networking\GameCult.Networking.csproj" />
<ProjectReference Include="..\..\CultMath\src\CultMath\CultMath.csproj" />
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Geometry\GameCult.Geometry.csproj" />
<ProjectReference Include="$(EveUnityRoot)\packages\org.gamecult.eve.surface\GameCult.Eve.Surface.csproj"
AdditionalProperties="CultLibRoot=$(CultLibRoot)" />
<ProjectReference Include="..\..\EvePlugins\plugins\eve-plugin-fields\GameCult.Eve.PluginFields.csproj"

View file

@ -9,6 +9,7 @@
"com.unity.modules.uielements": "1.0.0",
"com.unity.render-pipelines.universal": "17.4.0",
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.43",
"org.gamecult.geometry": "file:../../CultLib/unity/org.gamecult.geometry",
"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.4",
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.103",

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using CultMath;
using GameCult.Geometry;
using MessagePack;
#nullable enable
@ -33,9 +34,9 @@ namespace GameCult.Aetheria.State.Verse
public double MaxX { get; }
public double MaxZ { get; }
public rect ToAetheriaXyRect()
public CultRect ToAetheriaXyRect()
{
return new rect(
return new CultRect(
(float)MinX,
(float)MinZ,
(float)MaxX,
@ -790,7 +791,7 @@ namespace GameCult.Aetheria.State.Verse
public static AetheriaRuntimeGravityInfluenceBrush[] QueryGravityInfluences(
AetheriaRuntimeZoneSnapshotCommit? zone,
rect viewport)
CultRect viewport)
{
var brushes = new List<AetheriaRuntimeGravityInfluenceBrush>();
QueryGravityInfluences(zone, viewport, brushes);
@ -807,7 +808,7 @@ namespace GameCult.Aetheria.State.Verse
public static int QueryGravityInfluences(
AetheriaRuntimeZoneSnapshotCommit? zone,
rect viewport,
CultRect viewport,
List<AetheriaRuntimeGravityInfluenceBrush> brushes)
{
if (brushes == null) throw new ArgumentNullException(nameof(brushes));
@ -906,7 +907,7 @@ namespace GameCult.Aetheria.State.Verse
public static AetheriaRuntimeDaemonBodyView[] QueryBodyViews(
AetheriaRuntimeZoneSnapshotCommit? zone,
rect viewport)
CultRect viewport)
{
var views = new List<AetheriaRuntimeDaemonBodyView>();
QueryBodyViews(zone, viewport, views);
@ -930,15 +931,15 @@ namespace GameCult.Aetheria.State.Verse
public static int QueryBodyViews(
AetheriaRuntimeZoneSnapshotCommit? zone,
rect viewport,
CultRect viewport,
List<AetheriaRuntimeDaemonBodyView> views)
{
return QueryBodyViews(zone, (rect?)viewport, views);
return QueryBodyViews(zone, (CultRect?)viewport, views);
}
private static int QueryBodyViews(
AetheriaRuntimeZoneSnapshotCommit? zone,
rect? viewport,
CultRect? viewport,
List<AetheriaRuntimeDaemonBodyView> views)
{
if (views == null) throw new ArgumentNullException(nameof(views));
@ -1196,7 +1197,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
int observerEntityIndex,
double minimumInfoGathered,
rect viewport)
CultRect viewport)
{
var currentEntityIndex = TryParseEntityIndex(run?.CurrentEntityKey);
return QueryPresentationEntityIndices(
@ -1223,7 +1224,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
int observerEntityIndex,
double minimumInfoGathered,
rect viewport,
CultRect viewport,
List<int> entityIndices)
{
var currentEntityIndex = TryParseEntityIndex(run?.CurrentEntityKey);
@ -1251,7 +1252,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
int observerEntityIndex,
double minimumInfoGathered,
rect viewport)
CultRect viewport)
{
var entityIndices = new List<int>();
var currentEntityIndex = TryParseEntityIndex(currentEntityKey);
@ -1270,7 +1271,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
int observerEntityIndex,
double minimumInfoGathered,
rect viewport)
CultRect viewport)
{
var entityIndices = new List<int>();
QueryPresentationEntityIndices(
@ -1299,7 +1300,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
int observerEntityIndex,
double minimumInfoGathered,
rect viewport,
CultRect viewport,
List<int> entityIndices)
{
var currentEntityIndex = TryParseEntityIndex(currentEntityKey);
@ -1317,7 +1318,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
int observerEntityIndex,
double minimumInfoGathered,
rect viewport,
CultRect viewport,
List<int> entityIndices)
{
if (entityIndices == null) throw new ArgumentNullException(nameof(entityIndices));
@ -1368,7 +1369,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
IReadOnlyList<int>? controlledEntityIndices,
double minimumInfoGathered,
rect viewport)
CultRect viewport)
{
var entityIndices = new List<int>();
QueryObjectsViewportEntityIndices(zone, controlledEntityIndices, minimumInfoGathered, viewport, entityIndices);
@ -1379,7 +1380,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
IReadOnlyList<int>? controlledEntityIndices,
double minimumInfoGathered,
rect viewport,
CultRect viewport,
List<int> entityIndices)
{
if (entityIndices == null) throw new ArgumentNullException(nameof(entityIndices));
@ -1454,7 +1455,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
IReadOnlyList<int>? controlledEntityIndices,
double minimumInfoGathered,
rect viewport)
CultRect viewport)
{
var entities = new List<AetheriaRuntimeDaemonObjectViewportEntity>();
QueryObjectsViewport(zone, controlledEntityIndices, minimumInfoGathered, viewport, entities);
@ -1465,7 +1466,7 @@ namespace GameCult.Aetheria.State.Verse
AetheriaRuntimeZoneSnapshotCommit? zone,
IReadOnlyList<int>? controlledEntityIndices,
double minimumInfoGathered,
rect viewport,
CultRect viewport,
List<AetheriaRuntimeDaemonObjectViewportEntity> entities)
{
if (entities == null) throw new ArgumentNullException(nameof(entities));
@ -1761,7 +1762,7 @@ namespace GameCult.Aetheria.State.Verse
foreach (var brush in QueryGravityInfluences(
zone,
new rect((float)positionX, (float)positionZ, (float)positionX, (float)positionZ)))
new CultRect((float)positionX, (float)positionZ, (float)positionX, (float)positionZ)))
{
var dx = positionX - brush.CenterX;
var dz = positionZ - brush.CenterZ;
@ -1896,7 +1897,7 @@ namespace GameCult.Aetheria.State.Verse
return source;
}
private static bool IntersectsCircle(rect viewport, double centerX, double centerY, double radius)
private static bool IntersectsCircle(CultRect viewport, double centerX, double centerY, double radius)
{
if (radius <= 0)
return false;
@ -1918,12 +1919,12 @@ namespace GameCult.Aetheria.State.Verse
return ContainsPoint(rect.ToAetheriaXyRect(), x, z);
}
private static bool ContainsPoint(rect viewport, double x, double y)
private static bool ContainsPoint(CultRect viewport, double x, double y)
{
return viewport.Contains(new float2((float)x, (float)y));
}
private static rect ToRuntimeRect(AetheriaRuntimeXzRect unityViewport)
private static CultRect ToRuntimeRect(AetheriaRuntimeXzRect unityViewport)
{
return unityViewport.ToAetheriaXyRect();
}

View file

@ -5,6 +5,7 @@
"GameCult.Eve.Surface",
"GameCult.Eve.PluginFields",
"GameCult.Mesh",
"GameCult.Geometry",
"CultMath",
"MessagePack"
],