Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 47 additions & 43 deletions flock.js
Original file line number Diff line number Diff line change
Expand Up @@ -1273,55 +1273,59 @@ export const flock = {
console.error("Error initializing CSG2:", error);
}

flock.canvas.addEventListener(
"touchend",
(event) => {
if (event.touches.length === 0) {
const input = flock.scene.activeCamera.inputs?.attached?.pointers;
// Add null check for input itself
if (
input &&
(input._pointA !== null ||
input._pointB !== null ||
input._isMultiTouch === true)
) {
flock._hardResetCameraControls(flock.scene.activeCamera, {
reattachDelayMs: 100,
noPreventDefault: true,
});
if (!flock._canvasListenersRegistered) {
flock._canvasListenersRegistered = true;

flock.canvas.addEventListener(
"touchend",
(event) => {
if (event.touches.length === 0) {
const input = flock.scene.activeCamera.inputs?.attached?.pointers;
// Add null check for input itself
if (
input &&
(input._pointA !== null ||
input._pointB !== null ||
input._isMultiTouch === true)
) {
flock._hardResetCameraControls(flock.scene.activeCamera, {
reattachDelayMs: 100,
Comment on lines +1283 to +1292
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard flock.scene before dereferencing in touchend handler.

flock.scene.activeCamera is accessed directly; during dispose/re-init windows flock.scene can be null, causing a runtime exception in the event handler.

Proposed fix
-            const input = flock.scene.activeCamera.inputs?.attached?.pointers;
+            const activeCamera = flock.scene?.activeCamera;
+            const input = activeCamera?.inputs?.attached?.pointers;
             // Add null check for input itself
             if (
               input &&
               (input._pointA !== null ||
                 input._pointB !== null ||
                 input._isMultiTouch === true)
             ) {
-              flock._hardResetCameraControls(flock.scene.activeCamera, {
+              flock._hardResetCameraControls(activeCamera, {
                 reattachDelayMs: 100,
                 noPreventDefault: true,
               });
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const input = flock.scene.activeCamera.inputs?.attached?.pointers;
// Add null check for input itself
if (
input &&
(input._pointA !== null ||
input._pointB !== null ||
input._isMultiTouch === true)
) {
flock._hardResetCameraControls(flock.scene.activeCamera, {
reattachDelayMs: 100,
const activeCamera = flock.scene?.activeCamera;
const input = activeCamera?.inputs?.attached?.pointers;
// Add null check for input itself
if (
input &&
(input._pointA !== null ||
input._pointB !== null ||
input._isMultiTouch === true)
) {
flock._hardResetCameraControls(activeCamera, {
reattachDelayMs: 100,
🧰 Tools
🪛 GitHub Actions: Prettier / prettier

[warning] Prettier reported formatting issues.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flock.js` around lines 1283 - 1292, The touchend handler currently
dereferences flock.scene and flock.scene.activeCamera directly which can be null
during dispose/re-init; update the handler to guard against a missing scene
before accessing activeCamera and its inputs (e.g., check flock && flock.scene
&& flock.scene.activeCamera first), then continue with the existing logic that
reads inputs and calls flock._hardResetCameraControls; ensure the same
null-check pattern surrounds the input retrieval and subsequent use of
flock._hardResetCameraControls to avoid runtime exceptions.

noPreventDefault: true,
});
}
}
}
},
{ passive: false },
);
},
{ passive: false },
);

flock.canvas.addEventListener("keydown", function (event) {
flock.canvas.currentKeyPressed = event.key;
flock.canvas.pressedKeys.add(event.key);
});
flock.canvas.addEventListener("keydown", function (event) {
flock.canvas.currentKeyPressed = event.key;
flock.canvas.pressedKeys.add(event.key);
});

flock.canvas.addEventListener("keyup", function (event) {
flock.canvas.pressedKeys.delete(event.key);
});
flock.canvas.addEventListener("keyup", function (event) {
flock.canvas.pressedKeys.delete(event.key);
});

flock.canvas.addEventListener("blur", () => {
// Clear all pressed keys when window loses focus
flock.canvas.pressedKeys.clear();
flock.canvas.pressedButtons.clear();
flock._hardResetCameraControls(flock.scene?.activeCamera);
});
flock.canvas.addEventListener("blur", () => {
// Clear all pressed keys when window loses focus
flock.canvas.pressedKeys.clear();
flock.canvas.pressedButtons.clear();
flock._hardResetCameraControls(flock.scene?.activeCamera);
});

// Hardening for rare pointer/input desync where camera keeps moving
// after input ends or browser focus changes.
window.addEventListener("blur", () => {
flock.canvas.pressedKeys.clear();
flock.canvas.pressedButtons.clear();
flock._hardResetCameraControls(flock.scene?.activeCamera);
});
// Hardening for rare pointer/input desync where camera keeps moving
// after input ends or browser focus changes.
window.addEventListener("blur", () => {
flock.canvas.pressedKeys.clear();
flock.canvas.pressedButtons.clear();
flock._hardResetCameraControls(flock.scene?.activeCamera);
});

window.addEventListener("pointerup", () => {
flock._hardResetCameraControls(flock.scene?.activeCamera);
});
window.addEventListener("pointerup", () => {
flock._hardResetCameraControls(flock.scene?.activeCamera);
});
}

flock.engineReady = true;
},
Expand Down
Loading