2026-08-10 14:57:50 +01:00
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2020-12-22 03:13:43 +00:00
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
using GameCult.Caching;
|
2020-12-22 03:13:43 +00:00
|
|
|
|
using System;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using MessagePack;
|
|
|
|
|
|
using Newtonsoft.Json;
|
2026-09-14 17:39:12 +02:00
|
|
|
|
using CultMath;
|
|
|
|
|
|
using static CultMath.math;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
[Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn), RuntimeInspectable]
|
|
|
|
|
|
public class ResourceScannerData : BehaviorData
|
|
|
|
|
|
{
|
|
|
|
|
|
[Inspectable, JsonProperty("range"), Key(1), RuntimeInspectable]
|
|
|
|
|
|
public PerformanceStat Range = new PerformanceStat();
|
|
|
|
|
|
|
|
|
|
|
|
[Inspectable, JsonProperty("minDensity"), Key(2), RuntimeInspectable]
|
|
|
|
|
|
public PerformanceStat MinimumDensity = new PerformanceStat();
|
|
|
|
|
|
|
|
|
|
|
|
[Inspectable, JsonProperty("scanDuration"), Key(3), RuntimeInspectable]
|
|
|
|
|
|
public PerformanceStat ScanDuration = new PerformanceStat();
|
|
|
|
|
|
|
|
|
|
|
|
public override Behavior CreateInstance(EquippedItem item)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ResourceScanner(this, item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override Behavior CreateInstance(ConsumableItemEffect item)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new ResourceScanner(this, item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-05-21 01:53:06 +01:00
|
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
|
public class ResourceScanner : Behavior, IAlwaysUpdatedBehavior
|
2020-05-21 01:53:06 +01:00
|
|
|
|
{
|
2020-05-29 23:06:35 +01:00
|
|
|
|
public int Asteroid = -1;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
private ResourceScannerData _data;
|
2020-05-29 23:06:35 +01:00
|
|
|
|
private float _scanTime;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
private CultRecordKey _scanTarget;
|
2020-05-21 01:53:06 +01:00
|
|
|
|
|
2020-05-29 23:06:35 +01:00
|
|
|
|
public float Range { get; private set; }
|
|
|
|
|
|
public float MinimumDensity { get; private set; }
|
|
|
|
|
|
public float ScanDuration { get; private set; }
|
|
|
|
|
|
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
public CultRecordKey ScanTarget
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
get => _scanTarget;
|
2020-05-29 23:06:35 +01:00
|
|
|
|
set
|
|
|
|
|
|
{
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
if (!value.Equals(_scanTarget))
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_scanTarget = value;
|
2020-05-29 23:06:35 +01:00
|
|
|
|
_scanTime = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-05-21 01:53:06 +01:00
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public ResourceScanner(ResourceScannerData data, EquippedItem item) : base(data, item)
|
2021-04-18 05:13:20 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_data = data;
|
2021-04-18 05:13:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public ResourceScanner(ResourceScannerData data, ConsumableItemEffect item) : base(data, item)
|
2020-05-21 01:53:06 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_data = data;
|
2020-05-21 01:53:06 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-18 05:13:20 +01:00
|
|
|
|
public override bool Execute(float dt)
|
2020-05-21 01:53:06 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
var planetData = Entity.Zone.Planets[ScanTarget];
|
|
|
|
|
|
if (planetData != null)
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
if (planetData is AsteroidBeltData beltData)
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
if(Asteroid > -1 &&
|
|
|
|
|
|
Asteroid < beltData.Asteroids.Length &&
|
Cut 1: belts are evaluated when asked, not simulated every tick
Zone.Update no longer threads a per-belt Task.Run over a double-buffered
Transforms/OrbitPosition pair. AsteroidBelt.Pose/Size are the one place a
chunk pose is computed, a pure function of zone time; Zone.ChunkPose and
Zone.EvaluateBelt read them at the zone's own time. MiningTool,
ResourceScanner and ZoneRenderer (belt matrices and AsteroidBeltUI) all read
through Zone instead of the deleted per-tick buffers; the renderer owns its
own float4[] buffer, filled fresh every frame.
Deleted: System.Threading/.Tasks usings, BeltUpdates, the wait loop, the
per-belt copy+Task.Run, NearestAsteroid (dead/broken reader), the private
UpdateAsteroidTransforms, and AsteroidBelt's four pose fields plus their
allocations. ~65 lines, matching the cut map's estimate.
miningprobe, densest zone of the operator's save (EAC-2265, 809 asteroids,
8 belts), rebuilt against this cut and rerun 10x each side:
before (branch point, 8bd25f6f/e729e6d6):
Zone.Update wall 541-3654 us/tick, CPU 120-391 us/tick (7 clean runs)
3/10 runs crashed: InvalidOperationException from Zone.GetOrbitPosition,
the documented _updatedOrbits/Orbits data race between the main thread's
Clear() and a belt Task.Run
after (this commit):
Zone.Update wall 95-185 us/tick, CPU 99-152 us/tick, in line with the
31-39 us "no belt work" baseline the map recorded -- belts are free per
tick
0/10 races
ChunkPose has no buffer to go stale: ~0.12 u of parent+asteroid motion is
visible across one 17 ms tick instead of being one tick behind
tests/Aetheria.Shared.Tests/MiningCut1Tests.cs (new, 260/260 suite green):
ChunkPoseIsTheOrbitAtZoneTime, ChunkPoseDoesNotDependOnUpdateCadence,
DamagedChunkShrinksAndABrokenOneHasNoSize -- each expected value is the
closed-form orbit/size rule reimplemented in the test, never a call to
AsteroidBelt.Pose/Size.
Negative grep (Zone.cs, "Zone Display", ServerShared/Behaviors) for
Task\.Run|System\.Threading|NewTransforms|\.Transforms\b|BeltUpdates|
NearestAsteroid|\.OrbitPosition\b|NewOrbitPosition: one hit, the commented
line in MineAsteroid that Cut 2 deletes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 01:27:16 +02:00
|
|
|
|
length(Entity.Position.xz - Entity.Zone.ChunkPose(ScanTarget, Asteroid).xy) < Range)
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_scanTime += dt;
|
|
|
|
|
|
if (_scanTime > ScanDuration)
|
|
|
|
|
|
{
|
|
|
|
|
|
// TODO: Implement Scanning!
|
|
|
|
|
|
//Context.ItemData.Get<Corporation>(Entity.Corporation).PlanetSurveyFloor[ScanTarget] = MinimumDensity;
|
|
|
|
|
|
_scanTime = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
2020-05-29 23:06:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-08-10 14:57:50 +01:00
|
|
|
|
else
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
if(length(Entity.Position.xz - Entity.Zone.GetOrbitPosition(planetData.Orbit.Key)) < Range)
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
_scanTime += dt;
|
|
|
|
|
|
if (_scanTime > ScanDuration)
|
|
|
|
|
|
{
|
|
|
|
|
|
//Context.ItemData.Get<Corporation>(Entity.Corporation).PlanetSurveyFloor[ScanTarget] = MinimumDensity;
|
|
|
|
|
|
_scanTime = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
2020-05-29 23:06:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-10 03:59:47 +00:00
|
|
|
|
public void Update(float delta)
|
2020-05-29 23:06:35 +01:00
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
Range = Evaluate(_data.Range);
|
|
|
|
|
|
MinimumDensity = Evaluate(_data.MinimumDensity);
|
|
|
|
|
|
ScanDuration = Evaluate(_data.ScanDuration);
|
2020-05-21 01:53:06 +01:00
|
|
|
|
}
|
2026-08-10 14:57:50 +01:00
|
|
|
|
}
|