Adds raycasting to the ore detector so in-game scripts can query voxel ore composition, which the vanilla Space Engineers API does not expose.
Rays are fired from the ore detector toward a world position and report the name of the first ore they hit, plus the hit position.
This is a fork. The original is Racher/SpaceEngineersOreDetectorRaycast by Péter Himes, MIT licensed, archived read-only in September 2023 with its last code change in April 2021. All credit for the design and implementation is theirs. This fork exists to keep it maintained; the MIT licence and original copyright are retained in LICENSE.
No. It still compiles cleanly against current Space Engineers assemblies, and Space Engineers compiles mods from source at world load, so the stale project file never affected the shipped mod. If you are happily using the original, you do not need this.
This fork fixes genuine latent bugs and adds tuning knobs — see Changes.
The implementation attaches only to the vanilla MyOreDetector. Ore detectors added by
other mods will not have these properties. None of them are visible in the terminal.
var det = GridTerminalSystem.GetBlockWithName("Ore Detector") as IMyOreDetector;
det.SetValue<Vector3D>("RaycastTarget", det.GetPosition() + det.WorldMatrix.Forward * 400);
var hit = det.GetValue<MyDetectedEntityInfo>("RaycastResult");
if (!hit.IsEmpty() && hit.HitPosition.HasValue)
Echo(hit.Name + " at " + hit.HitPosition.Value);| Property | Type | Access | Meaning |
|---|---|---|---|
RaycastTarget |
Vector3D |
get/set | End point of the ray, absolute world coordinates. The ray starts at the detector's position. Setting this does not itself scan |
RaycastResult |
MyDetectedEntityInfo |
get | Performs the scan and returns the first non-blacklisted ore along the ray |
DirectResult |
MyDetectedEntityInfo |
get | Samples only the exact target point. Ignores OreBlacklist by design. Same range cost |
AvailableScanRange |
double |
get | Remaining scan budget in metres |
OreBlacklist |
string |
get/set | Ores the ray passes through. Substring match, so "Ice,Stone" works. Defaults to "Stone" |
RaycastStepConst |
double |
get/set | New. Linear term of the ray sampling step. Default 1.0 |
RaycastStepBase |
double |
get/set | New. Exponential term of the ray sampling step. Default 1.01 |
DebugChat |
bool |
get/set | New. Mirror diagnostics into chat. Off by default; process-wide, not per block |
There is no cone or angle limit — unlike vanilla camera raycast, the detector's
orientation is irrelevant. RaycastTarget is a world position and any direction is valid.
Only some fields of MyDetectedEntityInfo are populated:
| Field | On hit | On miss |
|---|---|---|
EntityId |
1 |
0 |
Name |
MyVoxelMaterialDefinition.MinedOre |
"" |
Type |
Unknown |
None |
HitPosition |
world coordinates | null |
TimeStamp |
GameDateTime.Ticks (100 ns units) |
same |
Ore deposits have no unique entity ID, hence the constant 1.
Charge accrues at 2 km/s of ray length, banked up to 100 km, matching vanilla camera raycast. The requested ray length is consumed even when the hit is closer. If the target is further than the remaining budget, the ray is shortened rather than refused.
Budget does not persist across a world load — a freshly loaded block charges from zero.
The ray is walked in steps that grow with distance:
dist = dist * RaycastStepBase + RaycastStepConst
so it samples finely near the detector and coarsens further out, keeping cost bounded.
The trade-off is that a thin vein can be stepped over at long range — with the defaults
the step is roughly 5 m at 400 m out. Effective hit-position accuracy is about
1 m + distance/100.
Shorter rays are better than longer ones: they sample more finely and cost less
budget. If you need long-range sensitivity, lower RaycastStepConst — but that raises CPU
cost, so it is clamped, and a hard step cap per voxel map protects the server.
Limits: RaycastStepConst ∈ [0.25, 50], RaycastStepBase ∈ [1.0005, 2.0], max 8000 steps
per voxel map.
Fixes
- Storage keys are stable.
StoredPropertygenerated its key withGuid.NewGuid(), producing a fresh key every launch. Stored values were therefore never read back after a reload — the documented "remembers between sessions" behaviour did not work — and every session leaked another orphaned entry into the save file. - Safe storage writes.
Entity.Storage.Add()throws when the key already exists. This only went unnoticed because the random key guaranteed no collision; with a stable key it would throw on every reload. Now guarded withContainsKey/SetValue. - Deterministic registration. Terminal properties registered themselves from static
field initialisers, at whatever moment the first ore detector touched the class, with
failures silently swallowed. The symptom was the mod appearing absent — scripts see no
RaycastTargetand report the mod as not installed. Registration now happens in a session component'sLoadData(), and is idempotent. - No more chat spam. Every caught exception called
ShowMessage, so one misbehaving script could spam every player on the server once per raycast. Diagnostics go to the SE log, de-duplicated; chat output is opt-in viaDebugChat. - Culture-invariant serialisation. Numeric values are written with
CultureInfo.InvariantCultureso a comma decimal separator cannot corrupt stored state. - Removed a dead read of
OreBlacklistinDirectResult. Its blacklist-ignoring behaviour is intentional and documented, so it is unchanged.
Additions
RaycastStepConst/RaycastStepBaseexpose the sampling trade-off, with clamps and a per-voxel-map step cap so a script cannot stall the server thread.DebugChattoggle.- Project file retargeted to .NET Framework 4.8 with an explicit
netstandardreference — SE's assemblies now reference netstandard 2.0, which v4.6.1 cannot consume. The Bin64 path is overridable via$(SpaceEngineersBin64).
Behaviour is otherwise unchanged, and every original property keeps its name, type and semantics, so existing scripts such as OreFinderPlus keep working.
The project file is a development convenience only — Space Engineers compiles the mod from source at load time, so you never ship a DLL.
msbuild Data/Scripts/OreDetectorRaycastScript/mod.csproj /p:SpaceEngineersBin64="C:\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64"Copy or clone this repository into your local mods folder:
%AppData%\Roaming\SpaceEngineers\Mods\SpaceEngineersOreDetectorRaycast\
so that Data\Scripts\OreDetectorRaycastScript\*.cs sits directly underneath. The mod then
appears in the world's mod list as a local mod. Check Alt+F11 for load errors.
From the world's mod selection window, use Publish. A modinfo.sbmi is generated on
first publish and identifies the item thereafter — keep it if you intend to update rather
than create a second listing.
If you publish this fork, please keep the attribution to Péter Himes and a link back to the original repository. The MIT licence requires the copyright notice to travel with the code.
MIT — see LICENSE. Copyright (c) 2020 Péter Himes, and contributors to this fork.