Skip to content
This repository was archived by the owner on Aug 31, 2026. It is now read-only.

Commit 46cb7de

Browse files
committed
A survey teaches the world it was made from
- report the bodies a look actually named, for a caller that can act - the observatory teaches its own dimension and syncs it like a beacon - only a resolved, unobscured, full-detail look may teach anything
1 parent f37af3a commit 46cb7de

3 files changed

Lines changed: 150 additions & 4 deletions

File tree

src/main/java/zmaster587/advancedRocketry/tile/multiblock/TileObservatory.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package zmaster587.advancedRocketry.tile.multiblock;
22

3+
import java.util.HashSet;
4+
import java.util.Set;
5+
import zmaster587.advancedRocketry.dimension.DimensionManager;
6+
import zmaster587.advancedRocketry.dimension.DimensionProperties;
7+
import zmaster587.advancedRocketry.network.PacketDimInfo;
38
import io.netty.buffer.ByteBuf;
49
import net.minecraft.block.Block;
510
import net.minecraft.block.state.IBlockState;
@@ -901,6 +906,35 @@ public boolean beginPassiveSweep() {
901906
return true;
902907
}
903908

909+
/**
910+
* Teach the world this observatory stands on what the survey just made out.
911+
*
912+
* <p>Server side only, and only what has a dimension. The set is a body's own, additive over the
913+
* global known-set rather than a replacement for it, so a pack that authored its known planets
914+
* keeps them and a world merely adds what it has learned since. Syncing goes through the same
915+
* channel a beacon uses, because this is the same kind of fact.</p>
916+
*/
917+
private void teachThisBody(Set<Integer> discovered) {
918+
if (discovered.isEmpty() || world == null || world.isRemote) {
919+
return;
920+
}
921+
DimensionProperties here = DimensionManager.getInstance()
922+
.getDimensionPropertiesOrNull(world.provider.getDimension());
923+
if (here == null) {
924+
return; // a world the planet layer does not own - nothing here can learn
925+
}
926+
boolean learned = false;
927+
for (int dimId : discovered) {
928+
if (!here.isPlanetKnownHere(dimId)) {
929+
here.discoverPlanet(dimId);
930+
learned = true;
931+
}
932+
}
933+
if (learned) {
934+
PacketHandler.sendToAll(new PacketDimInfo(here.getId(), here));
935+
}
936+
}
937+
904938
/** The observation in flight, or {@code null}. */
905939
@Nullable
906940
public RegionScan getActiveScan() {
@@ -1083,9 +1117,15 @@ private void completeRegionScanIfDue() {
10831117
GalacticCoord origin = scanOrigin();
10841118
UniverseRegistry registry = UniverseRegistry.get(world);
10851119
lastScanObscured += countObscured(registry, origin, activeScan, activeScan.cellsDone(), cells);
1120+
// WHERE the instrument stands is what it teaches. A survey writes the crystal the operator
1121+
// will carry away, and it also teaches the body underneath: a launch pad here may afterwards
1122+
// be aimed at what this telescope made out, while a pad on the next world may not. Only a
1123+
// NAMED body can be taught - a bare address has no world to fly to.
1124+
Set<Integer> taughtHere = new HashSet<>();
10861125
lastScanDiscoveries += TelescopeScan.resolveBatch(registry, activeScan,
10871126
activeScan.cellsDone(), cells, crystal, now, TelescopeScan.dimensionNames(), origin,
1088-
characteriseWholeSystem);
1127+
characteriseWholeSystem, taughtHere::add);
1128+
teachThisBody(taughtHere);
10891129
activeScan = instant ? activeScan.completed(now) : activeScan.advanced(now, cells);
10901130
if (activeScan.isComplete()) {
10911131
activeScan = null;

src/main/java/zmaster587/advancedRocketry/universe/TelescopeScan.java

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Collections;
55
import java.util.List;
66
import java.util.Optional;
7+
import java.util.function.IntConsumer;
78
import java.util.function.IntFunction;
89

910
import net.minecraft.item.ItemStack;
@@ -231,6 +232,20 @@ public static List<Detection> detect(UniverseRegistry registry, GalacticCoord lo
231232
public static int characterise(UniverseRegistry registry, Detection hit, CrystalMemory memory,
232233
long observedTick, IntFunction<String> nameOf,
233234
boolean wholeSystem) {
235+
return characterise(registry, hit, memory, observedTick, nameOf, wholeSystem, null);
236+
}
237+
238+
/**
239+
* The same, telling {@code named} the dimension of every body this look actually made out.
240+
*
241+
* <p>The callback is a fact about the OBSERVATION - "this look named that world" - and nothing
242+
* more; what a caller does with it belongs to the caller. It fires only for a body that has a
243+
* dimension: an unrealized body has no world to fly to, and an address that names no body
244+
* (unresolvable, obscured, or positions-only) reports nothing at all.</p>
245+
*/
246+
public static int characterise(UniverseRegistry registry, Detection hit, CrystalMemory memory,
247+
long observedTick, IntFunction<String> nameOf,
248+
boolean wholeSystem, IntConsumer named) {
234249
if (registry == null || hit == null || memory == null) {
235250
return 0;
236251
}
@@ -244,6 +259,9 @@ public static int characterise(UniverseRegistry registry, Detection hit, Crystal
244259
if (memory.record(entryFor(body, observedTick, nameOf))) {
245260
written++;
246261
}
262+
if (named != null && body.dimId() != Constants.INVALID_PLANET) {
263+
named.accept(body.dimId());
264+
}
247265
}
248266
}
249267
if (!namedSomething) {
@@ -269,12 +287,20 @@ public static int resolveBatch(UniverseRegistry registry, RegionScan scan, int f
269287
public static int resolveBatch(UniverseRegistry registry, RegionScan scan, int from, int count,
270288
ItemStack crystal, long observedTick, IntFunction<String> nameOf,
271289
GalacticCoord observer, boolean wholeSystem) {
290+
return resolveBatch(registry, scan, from, count, crystal, observedTick, nameOf, observer,
291+
wholeSystem, null);
292+
}
293+
294+
/** The same, reporting every body the batch named to {@code named}. */
295+
public static int resolveBatch(UniverseRegistry registry, RegionScan scan, int from, int count,
296+
ItemStack crystal, long observedTick, IntFunction<String> nameOf,
297+
GalacticCoord observer, boolean wholeSystem, IntConsumer named) {
272298
if (!ItemMemoryCrystal.isCrystal(crystal)) {
273299
return 0;
274300
}
275301
CrystalMemory memory = ItemMemoryCrystal.memoryOf(crystal);
276302
int written = resolveBatch(registry, scan, from, count, memory, observedTick, nameOf,
277-
observer, wholeSystem);
303+
observer, wholeSystem, named);
278304
if (written > 0) {
279305
ItemMemoryCrystal.writeMemory(crystal, memory);
280306
}
@@ -294,14 +320,22 @@ public static int resolveBatch(UniverseRegistry registry, RegionScan scan, int f
294320
public static int resolveBatch(UniverseRegistry registry, RegionScan scan, int from, int count,
295321
CrystalMemory memory, long observedTick, IntFunction<String> nameOf,
296322
GalacticCoord observer, boolean wholeSystem) {
323+
return resolveBatch(registry, scan, from, count, memory, observedTick, nameOf, observer,
324+
wholeSystem, null);
325+
}
326+
327+
/** The same, reporting every body the batch named to {@code named}. */
328+
public static int resolveBatch(UniverseRegistry registry, RegionScan scan, int from, int count,
329+
CrystalMemory memory, long observedTick, IntFunction<String> nameOf,
330+
GalacticCoord observer, boolean wholeSystem, IntConsumer named) {
297331
if (registry == null || scan == null || memory == null) {
298332
return 0;
299333
}
300334
double limit = limitMagnitude();
301335
int written = 0;
302336
for (int index = from; index < from + count && index < scan.totalCells(); index++) {
303337
written += resolveLook(registry, scan.cellAt(index), memory, observedTick, nameOf,
304-
observer, limit, wholeSystem);
338+
observer, limit, wholeSystem, named);
305339
}
306340
return written;
307341
}
@@ -320,9 +354,18 @@ public static int resolveLook(UniverseRegistry registry, GalacticCoord look, Cry
320354
long observedTick, IntFunction<String> nameOf,
321355
GalacticCoord observer, double limitMagnitude,
322356
boolean wholeSystem) {
357+
return resolveLook(registry, look, memory, observedTick, nameOf, observer, limitMagnitude,
358+
wholeSystem, null);
359+
}
360+
361+
/** The same, reporting every body this look named to {@code named}. */
362+
public static int resolveLook(UniverseRegistry registry, GalacticCoord look, CrystalMemory memory,
363+
long observedTick, IntFunction<String> nameOf,
364+
GalacticCoord observer, double limitMagnitude,
365+
boolean wholeSystem, IntConsumer named) {
323366
int written = 0;
324367
for (Detection hit : detect(registry, look, observer, limitMagnitude)) {
325-
written += characterise(registry, hit, memory, observedTick, nameOf, wholeSystem);
368+
written += characterise(registry, hit, memory, observedTick, nameOf, wholeSystem, named);
326369
}
327370
return written;
328371
}

src/test/java/zmaster587/advancedRocketry/test/unit/TelescopeConeSurveyTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package zmaster587.advancedRocketry.test.unit;
22

3+
import java.util.Collections;
34
import java.util.ArrayList;
45
import java.util.List;
56
import java.util.Optional;
@@ -607,4 +608,66 @@ public void aGeneratorWithNoStarsGivesAnInstrumentNothingToReach() {
607608
empty.maxRangeLightYears(), 0d);
608609
assertEquals("which is still a pointing, of one territory", 1, empty.maxRangeSteps());
609610
}
611+
612+
// ── what a look may teach the ground it was made from ─────────────────────
613+
614+
@Test
615+
public void aLookReportsOnlyTheBodiesItActuallyMadeOut() {
616+
// The coupling that lets an observatory teach the world underneath it: the instrument may
617+
// pass on a body only when it RESOLVED one. A star has no dimension of its own, so of the
618+
// fixture's two objects exactly one can ever be taught.
619+
double sunLike = StellarMagnitude.luminositySuns(1.15d, 100);
620+
double near = StellarMagnitude.detectionRangeLightYears(sunLike, 12d - 6.5d) / 2d;
621+
UniverseRegistry registry = oneStarAt(near, 1.15f, 100);
622+
List<TelescopeScan.Detection> hits = TelescopeScan.detect(registry, seatAt(near), HOME, 12d);
623+
assertEquals("arrangement: one system to look at", 1, hits.size());
624+
assertTrue("arrangement: and it must be resolvable at this distance", hits.get(0).resolvable());
625+
626+
List<Integer> taught = new ArrayList<>();
627+
TelescopeScan.characterise(registry, hits.get(0), new CrystalMemory(), 1_000L,
628+
id -> "Body-" + id, true, taught::add);
629+
630+
assertEquals("exactly the planet, and not the star that has no world: " + taught,
631+
Collections.singletonList(701), taught);
632+
}
633+
634+
@Test
635+
public void aLookThatOnlyREGISTEREDTeachesNothing() {
636+
// Inside the aperture, outside what it can make out. The crystal still gets the address -
637+
// that is the whole mechanic - but nothing about the system may reach the ground, because
638+
// nothing about it was learned.
639+
double sunLike = StellarMagnitude.luminositySuns(1.15d, 100);
640+
double detectReach = StellarMagnitude.detectionRangeLightYears(sunLike, 12d);
641+
double resolveReach = StellarMagnitude.detectionRangeLightYears(sunLike, 12d - 6.5d);
642+
double far = (detectReach + resolveReach) / 2d;
643+
UniverseRegistry registry = oneStarAt(far, 1.15f, 100);
644+
List<TelescopeScan.Detection> hits = TelescopeScan.detect(registry, seatAt(far), HOME, 12d);
645+
assertEquals("arrangement: it must still register", 1, hits.size());
646+
assertFalse("arrangement: and must not be resolvable", hits.get(0).resolvable());
647+
648+
CrystalMemory memory = new CrystalMemory();
649+
List<Integer> taught = new ArrayList<>();
650+
TelescopeScan.characterise(registry, hits.get(0), memory, 1_000L, id -> "Body-" + id,
651+
true, taught::add);
652+
653+
assertTrue("a point of light teaches the ground nothing: " + taught, taught.isEmpty());
654+
assertEquals("but the address is still written down", 1, memory.size());
655+
}
656+
657+
@Test
658+
public void recordingPositionsOnlyTeachesNothingEither() {
659+
// The operator's own choice, not the aperture's limit. Asking for less must also GIVE less
660+
// to the ground, or "positions only" would quietly be a full survey for tier-1.
661+
double sunLike = StellarMagnitude.luminositySuns(1.15d, 100);
662+
double near = StellarMagnitude.detectionRangeLightYears(sunLike, 12d - 6.5d) / 2d;
663+
UniverseRegistry registry = oneStarAt(near, 1.15f, 100);
664+
List<TelescopeScan.Detection> hits = TelescopeScan.detect(registry, seatAt(near), HOME, 12d);
665+
assertTrue("arrangement: the aperture must not be what limits this", hits.get(0).resolvable());
666+
667+
List<Integer> taught = new ArrayList<>();
668+
TelescopeScan.characterise(registry, hits.get(0), new CrystalMemory(), 1_000L,
669+
id -> "Body-" + id, false, taught::add);
670+
671+
assertTrue("an operator recording addresses teaches no world: " + taught, taught.isEmpty());
672+
}
610673
}

0 commit comments

Comments
 (0)