Add continuous auto-save to IndexedDB for physics recordings
All checks were successful
Build / build (push) Successful in 1m26s
All checks were successful
Build / build (push) Successful in 1m26s
Modified physics recorder to automatically save all captured data to IndexedDB:
- Auto-saves every 10 seconds in batches (non-blocking)
- Creates unique session ID for each gameplay session
- Buffers snapshots between saves to minimize IndexedDB writes
- Saves any remaining buffered data on disposal
- All data preserved indefinitely in browser storage
Technical implementation:
- Auto-save buffer collects snapshots between 10-second intervals
- performAutoSave() copies buffer and clears immediately (non-blocking)
- Async save happens in background without impacting frame rate
- Session ID format: "session-{timestamp}" for easy identification
- Ring buffer (30s) still available for quick exports
Recording flow:
1. Recording starts when XR pose is set
2. Every frame adds snapshot to ring buffer AND auto-save buffer
3. Every 10 seconds, auto-save buffer is flushed to IndexedDB
4. On disposal, any remaining buffered frames are saved
5. All data accessible via existing storage APIs
Performance: Minimal impact - saves happen async every 10s, not per frame.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e473e3d03e
commit
88d380fa3f
BIN
public/assets/themes/default/audio/ammo.mp3
Normal file
BIN
public/assets/themes/default/audio/ammo.mp3
Normal file
Binary file not shown.
BIN
public/assets/themes/default/audio/danger.mp3
Normal file
BIN
public/assets/themes/default/audio/danger.mp3
Normal file
Binary file not shown.
BIN
public/assets/themes/default/audio/fuel.mp3
Normal file
BIN
public/assets/themes/default/audio/fuel.mp3
Normal file
Binary file not shown.
BIN
public/assets/themes/default/audio/hull.mp3
Normal file
BIN
public/assets/themes/default/audio/hull.mp3
Normal file
Binary file not shown.
BIN
public/assets/themes/default/audio/warning.mp3
Normal file
BIN
public/assets/themes/default/audio/warning.mp3
Normal file
Binary file not shown.
@ -74,6 +74,13 @@ export class PhysicsRecorder {
|
|||||||
// IndexedDB storage
|
// IndexedDB storage
|
||||||
private _storage: PhysicsStorage | null = null;
|
private _storage: PhysicsStorage | null = null;
|
||||||
|
|
||||||
|
// Auto-save to IndexedDB
|
||||||
|
private _autoSaveEnabled: boolean = true;
|
||||||
|
private _autoSaveBuffer: PhysicsSnapshot[] = [];
|
||||||
|
private _autoSaveInterval: number = 10000; // Save every 10 seconds
|
||||||
|
private _lastAutoSaveTime: number = 0;
|
||||||
|
private _currentSessionId: string = "";
|
||||||
|
|
||||||
constructor(scene: Scene) {
|
constructor(scene: Scene) {
|
||||||
this._scene = scene;
|
this._scene = scene;
|
||||||
|
|
||||||
@ -86,6 +93,7 @@ export class PhysicsRecorder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the ring buffer recorder (always capturing last 30 seconds)
|
* Start the ring buffer recorder (always capturing last 30 seconds)
|
||||||
|
* Also starts auto-save to IndexedDB
|
||||||
*/
|
*/
|
||||||
public startRingBuffer(): void {
|
public startRingBuffer(): void {
|
||||||
if (this._isEnabled) {
|
if (this._isEnabled) {
|
||||||
@ -95,16 +103,22 @@ export class PhysicsRecorder {
|
|||||||
|
|
||||||
this._isEnabled = true;
|
this._isEnabled = true;
|
||||||
this._startTime = performance.now();
|
this._startTime = performance.now();
|
||||||
|
this._lastAutoSaveTime = performance.now();
|
||||||
this._frameNumber = 0;
|
this._frameNumber = 0;
|
||||||
|
|
||||||
|
// Create unique session ID for this recording
|
||||||
|
this._currentSessionId = `session-${Date.now()}`;
|
||||||
|
|
||||||
// Hook into physics update observable
|
// Hook into physics update observable
|
||||||
this._scene.onAfterPhysicsObservable.add(() => {
|
this._scene.onAfterPhysicsObservable.add(() => {
|
||||||
if (this._isEnabled) {
|
if (this._isEnabled) {
|
||||||
this.captureFrame();
|
this.captureFrame();
|
||||||
|
this.checkAutoSave();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
debugLog("PhysicsRecorder: Ring buffer started (30 second capacity)");
|
debugLog("PhysicsRecorder: Recording started (ring buffer + auto-save to IndexedDB)");
|
||||||
|
debugLog(`PhysicsRecorder: Session ID: ${this._currentSessionId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,6 +251,11 @@ export class PhysicsRecorder {
|
|||||||
this._longRecording.push(snapshot);
|
this._longRecording.push(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add to auto-save buffer if enabled
|
||||||
|
if (this._autoSaveEnabled) {
|
||||||
|
this._autoSaveBuffer.push(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
this._frameNumber++;
|
this._frameNumber++;
|
||||||
|
|
||||||
// Track performance
|
// Track performance
|
||||||
@ -251,6 +270,61 @@ export class PhysicsRecorder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if it's time to auto-save to IndexedDB
|
||||||
|
*/
|
||||||
|
private checkAutoSave(): void {
|
||||||
|
if (!this._autoSaveEnabled || !this._storage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = performance.now();
|
||||||
|
const timeSinceLastSave = now - this._lastAutoSaveTime;
|
||||||
|
|
||||||
|
// Save every 10 seconds
|
||||||
|
if (timeSinceLastSave >= this._autoSaveInterval && this._autoSaveBuffer.length > 0) {
|
||||||
|
this.performAutoSave();
|
||||||
|
this._lastAutoSaveTime = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save buffered snapshots to IndexedDB
|
||||||
|
*/
|
||||||
|
private async performAutoSave(): Promise<void> {
|
||||||
|
if (!this._storage || this._autoSaveBuffer.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy buffer and clear it immediately to avoid blocking next frame
|
||||||
|
const snapshotsToSave = [...this._autoSaveBuffer];
|
||||||
|
this._autoSaveBuffer = [];
|
||||||
|
|
||||||
|
// Create a recording from the buffered snapshots
|
||||||
|
const metadata: RecordingMetadata = {
|
||||||
|
startTime: snapshotsToSave[0].timestamp,
|
||||||
|
endTime: snapshotsToSave[snapshotsToSave.length - 1].timestamp,
|
||||||
|
frameCount: snapshotsToSave.length,
|
||||||
|
recordingDuration: snapshotsToSave[snapshotsToSave.length - 1].timestamp - snapshotsToSave[0].timestamp,
|
||||||
|
physicsUpdateRate: this._physicsUpdateRate
|
||||||
|
};
|
||||||
|
|
||||||
|
const recording: PhysicsRecording = {
|
||||||
|
metadata,
|
||||||
|
snapshots: snapshotsToSave
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Save to IndexedDB with session ID as name
|
||||||
|
await this._storage.saveRecording(this._currentSessionId, recording);
|
||||||
|
|
||||||
|
const duration = (metadata.recordingDuration / 1000).toFixed(1);
|
||||||
|
debugLog(`PhysicsRecorder: Auto-saved ${snapshotsToSave.length} frames (${duration}s) to IndexedDB`);
|
||||||
|
} catch (error) {
|
||||||
|
debugLog("PhysicsRecorder: Error during auto-save", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export last N seconds from ring buffer
|
* Export last N seconds from ring buffer
|
||||||
*/
|
*/
|
||||||
@ -512,11 +586,18 @@ export class PhysicsRecorder {
|
|||||||
/**
|
/**
|
||||||
* Dispose of recorder resources
|
* Dispose of recorder resources
|
||||||
*/
|
*/
|
||||||
public dispose(): void {
|
public async dispose(): Promise<void> {
|
||||||
|
// Save any remaining buffered data before disposing
|
||||||
|
if (this._autoSaveBuffer.length > 0) {
|
||||||
|
debugLog(`PhysicsRecorder: Saving ${this._autoSaveBuffer.length} remaining frames before disposal`);
|
||||||
|
await this.performAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
this.stopRingBuffer();
|
this.stopRingBuffer();
|
||||||
this.stopLongRecording();
|
this.stopLongRecording();
|
||||||
this._ringBuffer = [];
|
this._ringBuffer = [];
|
||||||
this._longRecording = [];
|
this._longRecording = [];
|
||||||
|
this._autoSaveBuffer = [];
|
||||||
|
|
||||||
if (this._storage) {
|
if (this._storage) {
|
||||||
this._storage.close();
|
this._storage.close();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user