fix(world): Einsetzen eines Orts gegen Collider-Fehler absichern
#show baute bisher scene.add, bevor player.addCollider lief: wirft addCollider (z. B. toter Worker), blieb die Wurzel ohne active-Eintrag in der Szene stehen — sichtbar, aber nie wieder entfernbar — und die Ausnahme entkam synchron aus update() in die Spielschleife. Der Collider-Handle wird jetzt vor scene.add in einem try/catch gebaut; schlägt er fehl, bleibt die Szene unverändert, der Ort landet in this.failed (kein erneuter Versuch) und #show überspringt ihn beim nächsten Aufruf. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1c24fbf71e
commit
ed5254f88e
2 files changed
+43
-5
No files matched your search
+18
-5
@@ -185,15 +185,28 @@ export class PlaceLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#show(id) {
|
#show(id) {
|
||||||
|
if (this.failed.has(id)) return;
|
||||||
const model = this.models.get(id);
|
const model = this.models.get(id);
|
||||||
if (!model || this.active.has(id)) return;
|
if (!model || this.active.has(id)) return;
|
||||||
|
// Collider vor der Szene bauen: prepareModel hat schon updateMatrixWorld(true) auf der Wurzel
|
||||||
|
// aufgerufen, die Quelle hat also eine gültige Weltmatrix, obwohl sie noch nicht in der Szene
|
||||||
|
// hängt. Wirft addCollider (z. B. toter Worker), bleibt die Szene unverändert statt eine
|
||||||
|
// Wurzel ohne active-Eintrag (und damit nie wieder entfernbar) stehen zu lassen — und aus
|
||||||
|
// update(), das #show synchron aufruft, entkäme sonst eine Ausnahme in die Spielschleife.
|
||||||
|
let handle;
|
||||||
|
try {
|
||||||
|
// Ohne feste Teile keinen Collider: eine leere Quelle liefert im Controller nur ein Handle,
|
||||||
|
// das nie fertig wird (vgl. Ruling 51 in streaming.js).
|
||||||
|
handle = model.solid
|
||||||
|
? this.player.addCollider({ motion: "static", useWorker: true, shape: { kind: "mesh", source: model.root } })
|
||||||
|
: null;
|
||||||
|
} catch (err) {
|
||||||
|
this.failed.add(id);
|
||||||
|
console.warn(`Ort ${id} konnte nicht eingesetzt werden:`, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.scene.add(model.root);
|
this.scene.add(model.root);
|
||||||
model.root.updateMatrixWorld(true);
|
model.root.updateMatrixWorld(true);
|
||||||
// Ohne feste Teile keinen Collider: eine leere Quelle liefert im Controller nur ein Handle,
|
|
||||||
// das nie fertig wird (vgl. Ruling 51 in streaming.js).
|
|
||||||
const handle = model.solid
|
|
||||||
? this.player.addCollider({ motion: "static", useWorker: true, shape: { kind: "mesh", source: model.root } })
|
|
||||||
: null;
|
|
||||||
this.active.set(id, { root: model.root, handle });
|
this.active.set(id, { root: model.root, handle });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -292,4 +292,29 @@ describe("PlaceLayer", () => {
|
|||||||
|
|
||||||
expect(warn).toHaveBeenCalledWith(expect.stringContaining("hof"), expect.any(Error));
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining("hof"), expect.any(Error));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Bruch: addCollider wirft, scene.add lief aber schon davor — die Wurzel bliebe unsichtbar und
|
||||||
|
// für immer in der Szene, ohne active-Eintrag also nie mehr entfernbar; oder die Ausnahme entkäme
|
||||||
|
// synchron aus update() in die Spielschleife (#show läuft hier direkt, nicht über die Ladekette).
|
||||||
|
it("hält die Szene unverändert und warnt, wenn addCollider beim Einsetzen wirft", async () => {
|
||||||
|
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||||
|
const model = fakeModel();
|
||||||
|
const load = vi.fn(async () => model);
|
||||||
|
const { scene, player, layer } = makeLayer(load);
|
||||||
|
player.addCollider.mockImplementation(() => { throw new Error("Worker tot"); });
|
||||||
|
|
||||||
|
layer.update(south(500));
|
||||||
|
await vi.waitFor(() => expect(layer.models.has("hof")).toBe(true));
|
||||||
|
|
||||||
|
expect(() => layer.update(south(100))).not.toThrow();
|
||||||
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining("hof"), expect.any(Error));
|
||||||
|
expect(scene.children).not.toContain(model);
|
||||||
|
|
||||||
|
const callsBefore = player.addCollider.mock.calls.length;
|
||||||
|
layer.update(south(500));
|
||||||
|
layer.update(south(100));
|
||||||
|
|
||||||
|
expect(scene.children).not.toContain(model);
|
||||||
|
expect(player.addCollider.mock.calls.length).toBe(callsBefore);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user