mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 14:22:21 +00:00
revert(cockpit): no water read timeout, and loaded reads not raise-only
Both reviews of the timeout found that Cesium cannot cancel a full-detail read: each one given up kept its nine picks traversing every frame, so stalled tiles piled up a new set every 30 s, while slow but valid answers were dropped. Raise-only loaded reads then locked in a wrong-high height. Back to one full-detail read at a time whose answer always applies, and loaded reads that may adjust a river height but never take it to the sea; a coarse probe can again lower it partway until the full read lands. Keeps the test for another ship selected inside the double-click memory, and corrects CURRENT-STATE: a failed read keeps the height it had.
This commit is contained in:
1 parent
1bc4f980e0
commit
1b9bb844e6
4 files changed
+18
-96
No files matched your search
@@ -1574,10 +1574,9 @@ the full-detail read replaces it; it repeats every 5 s. Only one full-detail
|
|||||||
read runs at a time: while one is still running, a refresh reads the loaded
|
read runs at a time: while one is still running, a refresh reads the loaded
|
||||||
tiles instead, and the slow read applies whenever it lands. Once a
|
tiles instead, and the slow read applies whenever it lands. Once a
|
||||||
full-detail read has answered, loaded-tile reads no longer change the height;
|
full-detail read has answered, loaded-tile reads no longer change the height;
|
||||||
before then they may only raise it. A read that sampled nothing keeps the
|
before then they may find raised water but never take it back to the sea.
|
||||||
height it had, and a full-detail read still running after 30 s
|
A read that sampled nothing, or failed, keeps the height it had and is
|
||||||
(`VESSEL_WATERLINE_READ_TIMEOUT_MS`) is given up for a fresh one. A failed read keeps
|
tried again on the next refresh.
|
||||||
the sea surface.
|
|
||||||
|
|
||||||
The HUD keeps its frame, vision styles, briefing, weather and Display/Radio
|
The HUD keeps its frame, vision styles, briefing, weather and Display/Radio
|
||||||
utilities. It names the ship, reads `{type} · LIVE AIS · EYE {height}`, shows
|
utilities. It names the ship, reads `{type} · LIVE AIS · EYE {height}`, shows
|
||||||
|
|||||||
@@ -38,13 +38,6 @@ export const VESSEL_WATERLINE_PROBE_COUNT = 8;
|
|||||||
/** How often the bridge reads the water under its ship again, ms. */
|
/** How often the bridge reads the water under its ship again, ms. */
|
||||||
export const VESSEL_WATERLINE_REFRESH_MS = 5000;
|
export const VESSEL_WATERLINE_REFRESH_MS = 5000;
|
||||||
|
|
||||||
/**
|
|
||||||
* A full-detail water read still running after this long is given up and
|
|
||||||
* a fresh one started, ms: tile requests can stall, and one read that never
|
|
||||||
* lands would otherwise hold the height for the rest of the bridge.
|
|
||||||
*/
|
|
||||||
export const VESSEL_WATERLINE_READ_TIMEOUT_MS = 30000;
|
|
||||||
|
|
||||||
/** A surface less than this above sea level is the sea itself, metres. */
|
/** A surface less than this above sea level is the sea itself, metres. */
|
||||||
export const VESSEL_WATERLINE_MIN_RISE_M = 2;
|
export const VESSEL_WATERLINE_MIN_RISE_M = 2;
|
||||||
|
|
||||||
|
|||||||
@@ -1376,59 +1376,6 @@ test('before any full read lands, a coarse loaded read keeps the river it found'
|
|||||||
ship.viewer.scene.sampleHeight = () => 0.5;
|
ship.viewer.scene.sampleHeight = () => 0.5;
|
||||||
frames(ship.cockpit, clock, ship.shots, 240);
|
frames(ship.cockpit, clock, ship.shots, 240);
|
||||||
near(eyeHeightM(ship.shots.at(-1)) - seaEyeM, 27, 'still on the river', 0.5);
|
near(eyeHeightM(ship.shots.at(-1)) - seaEyeM, 27, 'still on the river', 0.5);
|
||||||
// Nor partway down: a probe on a 15 m bank is not the river either.
|
|
||||||
ship.viewer.scene.sampleHeight = () => 15;
|
|
||||||
frames(ship.cockpit, clock, ship.shots, 240);
|
|
||||||
near(eyeHeightM(ship.shots.at(-1)) - seaEyeM, 27, 'not lowered partway', 0.5);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a full water read that never lands is given up for a fresh one', async (t) => {
|
|
||||||
const clock = manualClock(t, START_MS);
|
|
||||||
const sea = await bridgeAt(t, clock);
|
|
||||||
frames(sea.cockpit, clock, sea.shots, 60);
|
|
||||||
const seaEyeM = eyeHeightM(sea.shots.at(-1));
|
|
||||||
sea.cockpit.exit();
|
|
||||||
|
|
||||||
clock.set(START_MS);
|
|
||||||
const record = { ...RECORD, lastPositionEpoch: START_MS / 1000 };
|
|
||||||
const ship = fixture(t, { record });
|
|
||||||
const reads = [];
|
|
||||||
ship.viewer.scene.sampleHeightMostDetailed = (positions) =>
|
|
||||||
new Promise((resolve) => reads.push({ positions, resolve }));
|
|
||||||
// Coarse tiles first put the ship 27 m up; it is really at sea.
|
|
||||||
ship.viewer.scene.sampleHeight = () => 27;
|
|
||||||
ship.cockpit.enter();
|
|
||||||
await settle();
|
|
||||||
ship.cockpit.update();
|
|
||||||
await settle();
|
|
||||||
assert.equal(reads.length, 1);
|
|
||||||
// The first read stalls. Twenty seconds on, still no second read.
|
|
||||||
frames(ship.cockpit, clock, ship.shots, 400);
|
|
||||||
await settle();
|
|
||||||
assert.equal(reads.length, 1, 'not stacked while it may still land');
|
|
||||||
// Past the timeout, a fresh read starts, and its answer applies.
|
|
||||||
frames(ship.cockpit, clock, ship.shots, 300);
|
|
||||||
await settle();
|
|
||||||
assert.equal(reads.length, 2, 'a fresh read after the stalled one');
|
|
||||||
const answer = (read, height) =>
|
|
||||||
read.resolve(
|
|
||||||
read.positions.map((position) => Object.assign(position, { height })),
|
|
||||||
);
|
|
||||||
answer(reads[1], 0.5);
|
|
||||||
await settle();
|
|
||||||
// The eye eases down; give it time to settle.
|
|
||||||
frames(ship.cockpit, clock, ship.shots, 400);
|
|
||||||
near(eyeHeightM(ship.shots.at(-1)), seaEyeM, 'back on the sea', 0.5);
|
|
||||||
// The stalled read landing now changes nothing.
|
|
||||||
answer(reads[0], 27);
|
|
||||||
await settle();
|
|
||||||
frames(ship.cockpit, clock, ship.shots, 60);
|
|
||||||
near(
|
|
||||||
eyeHeightM(ship.shots.at(-1)),
|
|
||||||
seaEyeM,
|
|
||||||
'the given-up read is dropped',
|
|
||||||
0.5,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('a client clock minutes fast does not age the bridge into a stale feed', async (t) => {
|
test('a client clock minutes fast does not age the bridge into a stale feed', async (t) => {
|
||||||
|
|||||||
+15
-32
@@ -9,7 +9,6 @@ import {
|
|||||||
} from '../cockpitMath.js';
|
} from '../cockpitMath.js';
|
||||||
import {
|
import {
|
||||||
VESSEL_DEAD_RECKON_MAX_S,
|
VESSEL_DEAD_RECKON_MAX_S,
|
||||||
VESSEL_WATERLINE_READ_TIMEOUT_MS,
|
|
||||||
VESSEL_WATERLINE_REFRESH_MS,
|
VESSEL_WATERLINE_REFRESH_MS,
|
||||||
deadReckonVesselFix,
|
deadReckonVesselFix,
|
||||||
vesselCameraProfile,
|
vesselCameraProfile,
|
||||||
@@ -111,11 +110,10 @@ export function update() {
|
|||||||
* every frame until it settles): while one is still running, a refresh
|
* every frame until it settles): while one is still running, a refresh
|
||||||
* reads the loaded tiles instead, and the slow read applies whenever it
|
* reads the loaded tiles instead, and the slow read applies whenever it
|
||||||
* lands. Once a full-detail read has answered, only full-detail reads
|
* lands. Once a full-detail read has answered, only full-detail reads
|
||||||
* change the height; before then a loaded-tile read may only raise it, so
|
* change the height; before then a loaded-tile read may find raised water
|
||||||
* one coarse probe cannot drop a river bridge, and a read that sampled
|
* but never takes a raised height back to the sea. One coarse probe cannot
|
||||||
* nothing keeps the height it had. A full-detail read still running after
|
* drop a river bridge to the sea, and a read that sampled nothing keeps
|
||||||
* VESSEL_WATERLINE_READ_TIMEOUT_MS is given up for a fresh one, so a
|
* the height it had.
|
||||||
* stalled read cannot hold a wrong height for the whole bridge.
|
|
||||||
* @param {{latitude: number, longitude: number}} [at] Where to read the
|
* @param {{latitude: number, longitude: number}} [at] Where to read the
|
||||||
* water: the dead-reckoned position the camera rides.
|
* water: the dead-reckoned position the camera rides.
|
||||||
* @returns {number} Ellipsoidal metres.
|
* @returns {number} Ellipsoidal metres.
|
||||||
@@ -142,49 +140,35 @@ function vesselSurfaceM(info, nowMs, at = info) {
|
|||||||
// Whether a full-detail read has answered for this ship.
|
// Whether a full-detail read has answered for this ship.
|
||||||
landed: false,
|
landed: false,
|
||||||
inFlight: false,
|
inFlight: false,
|
||||||
// The running full-detail read: its number and start time.
|
|
||||||
attempt: 0,
|
|
||||||
startedMs: 0,
|
|
||||||
};
|
};
|
||||||
this.vesselWaterline = read;
|
this.vesselWaterline = read;
|
||||||
} else {
|
} else {
|
||||||
read.sampledMs = nowMs;
|
read.sampledMs = nowMs;
|
||||||
if (read.inFlight && !read.landed) {
|
if (read.inFlight && !read.landed) {
|
||||||
const loadedM = loadedWaterlineM(scene, probes, seaM);
|
const loadedM = loadedWaterlineM(scene, probes, seaM);
|
||||||
// Lowering the water is the full-detail read's to say.
|
// Taking a river back to the sea is the full-detail read's to say.
|
||||||
if (
|
const lowers =
|
||||||
Number.isFinite(loadedM) &&
|
Number.isFinite(read.heightM) &&
|
||||||
!(Number.isFinite(read.heightM) && loadedM < read.heightM)
|
read.heightM > seaM &&
|
||||||
)
|
!(loadedM > seaM);
|
||||||
read.heightM = loadedM;
|
if (Number.isFinite(loadedM) && !lowers) read.heightM = loadedM;
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
read.inFlight &&
|
|
||||||
nowMs - read.startedMs >= VESSEL_WATERLINE_READ_TIMEOUT_MS
|
|
||||||
)
|
|
||||||
read.inFlight = false;
|
|
||||||
}
|
}
|
||||||
if (!read.inFlight)
|
if (!read.inFlight) readWaterline.call(this, read, scene, probes, seaM);
|
||||||
readWaterline.call(this, read, scene, probes, seaM, nowMs);
|
|
||||||
}
|
}
|
||||||
return read && Number.isFinite(read.heightM)
|
return read && Number.isFinite(read.heightM)
|
||||||
? Math.max(seaM, read.heightM)
|
? Math.max(seaM, read.heightM)
|
||||||
: seaM;
|
: seaM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Start one full-detail water read for `read`; its answer lands on it. */
|
||||||
* Start one full-detail water read for `read`; its answer lands on it
|
function readWaterline(read, scene, probes, seaM) {
|
||||||
* unless it was given up for a newer read.
|
|
||||||
*/
|
|
||||||
function readWaterline(read, scene, probes, seaM, nowMs) {
|
|
||||||
const attempt = ++read.attempt;
|
|
||||||
read.inFlight = true;
|
read.inFlight = true;
|
||||||
read.startedMs = nowMs;
|
|
||||||
Promise.resolve()
|
Promise.resolve()
|
||||||
.then(() => scene.sampleHeightMostDetailed(probes))
|
.then(() => scene.sampleHeightMostDetailed(probes))
|
||||||
.then(
|
.then(
|
||||||
(samples) => {
|
(samples) => {
|
||||||
if (this.vesselWaterline !== read || attempt !== read.attempt) return;
|
if (this.vesselWaterline !== read) return;
|
||||||
read.inFlight = false;
|
read.inFlight = false;
|
||||||
// However late it lands, it applies; one that sampled nothing (tiles
|
// However late it lands, it applies; one that sampled nothing (tiles
|
||||||
// not loaded yet) is no answer and keeps the height already read.
|
// not loaded yet) is no answer and keeps the height already read.
|
||||||
@@ -194,8 +178,7 @@ function readWaterline(read, scene, probes, seaM, nowMs) {
|
|||||||
read.heightM = vesselWaterlineM(seaM, heights);
|
read.heightM = vesselWaterlineM(seaM, heights);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if (this.vesselWaterline === read && attempt === read.attempt)
|
if (this.vesselWaterline === read) read.inFlight = false;
|
||||||
read.inFlight = false;
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user