feat: 收敛玩法运行时配置并加入故障恢复
This commit is contained in:
146
miniprogram/game/core/sessionRecovery.ts
Normal file
146
miniprogram/game/core/sessionRecovery.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { type LonLatPoint } from '../../utils/projection'
|
||||
import { type GameLaunchEnvelope } from '../../utils/gameLaunch'
|
||||
import { type GameSessionState } from './gameSessionState'
|
||||
|
||||
export interface RecoveryTelemetrySnapshot {
|
||||
distanceMeters: number
|
||||
currentSpeedKmh: number | null
|
||||
averageSpeedKmh: number | null
|
||||
heartRateBpm: number | null
|
||||
caloriesKcal: number | null
|
||||
lastGpsPoint: LonLatPoint | null
|
||||
lastGpsAt: number | null
|
||||
lastGpsAccuracyMeters: number | null
|
||||
}
|
||||
|
||||
export interface RecoveryViewportSnapshot {
|
||||
zoom: number
|
||||
centerTileX: number
|
||||
centerTileY: number
|
||||
rotationDeg: number
|
||||
gpsLockEnabled: boolean
|
||||
hasGpsCenteredOnce: boolean
|
||||
}
|
||||
|
||||
export interface RecoveryRuntimeSnapshot {
|
||||
gameState: GameSessionState
|
||||
telemetry: RecoveryTelemetrySnapshot
|
||||
viewport: RecoveryViewportSnapshot
|
||||
currentGpsPoint: LonLatPoint | null
|
||||
currentGpsAccuracyMeters: number | null
|
||||
currentGpsInsideMap: boolean
|
||||
bonusScore: number
|
||||
quizCorrectCount: number
|
||||
quizWrongCount: number
|
||||
quizTimeoutCount: number
|
||||
}
|
||||
|
||||
export interface SessionRecoverySnapshot {
|
||||
schemaVersion: 1
|
||||
savedAt: number
|
||||
launchEnvelope: GameLaunchEnvelope
|
||||
configAppId: string
|
||||
configVersion: string
|
||||
runtime: RecoveryRuntimeSnapshot
|
||||
}
|
||||
|
||||
const SESSION_RECOVERY_STORAGE_KEY = 'cmr.sessionRecovery.v1'
|
||||
|
||||
function cloneLonLatPoint(point: LonLatPoint | null): LonLatPoint | null {
|
||||
if (!point) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
lon: point.lon,
|
||||
lat: point.lat,
|
||||
}
|
||||
}
|
||||
|
||||
function cloneGameSessionState(state: GameSessionState): GameSessionState {
|
||||
return {
|
||||
status: state.status,
|
||||
endReason: state.endReason,
|
||||
startedAt: state.startedAt,
|
||||
endedAt: state.endedAt,
|
||||
completedControlIds: state.completedControlIds.slice(),
|
||||
skippedControlIds: state.skippedControlIds.slice(),
|
||||
currentTargetControlId: state.currentTargetControlId,
|
||||
inRangeControlId: state.inRangeControlId,
|
||||
score: state.score,
|
||||
guidanceState: state.guidanceState,
|
||||
modeState: state.modeState
|
||||
? JSON.parse(JSON.stringify(state.modeState)) as Record<string, unknown>
|
||||
: null,
|
||||
}
|
||||
}
|
||||
|
||||
export function cloneSessionRecoverySnapshot(snapshot: SessionRecoverySnapshot): SessionRecoverySnapshot {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
savedAt: snapshot.savedAt,
|
||||
launchEnvelope: JSON.parse(JSON.stringify(snapshot.launchEnvelope)) as GameLaunchEnvelope,
|
||||
configAppId: snapshot.configAppId,
|
||||
configVersion: snapshot.configVersion,
|
||||
runtime: {
|
||||
gameState: cloneGameSessionState(snapshot.runtime.gameState),
|
||||
telemetry: {
|
||||
distanceMeters: snapshot.runtime.telemetry.distanceMeters,
|
||||
currentSpeedKmh: snapshot.runtime.telemetry.currentSpeedKmh,
|
||||
averageSpeedKmh: snapshot.runtime.telemetry.averageSpeedKmh,
|
||||
heartRateBpm: snapshot.runtime.telemetry.heartRateBpm,
|
||||
caloriesKcal: snapshot.runtime.telemetry.caloriesKcal,
|
||||
lastGpsPoint: cloneLonLatPoint(snapshot.runtime.telemetry.lastGpsPoint),
|
||||
lastGpsAt: snapshot.runtime.telemetry.lastGpsAt,
|
||||
lastGpsAccuracyMeters: snapshot.runtime.telemetry.lastGpsAccuracyMeters,
|
||||
},
|
||||
viewport: {
|
||||
zoom: snapshot.runtime.viewport.zoom,
|
||||
centerTileX: snapshot.runtime.viewport.centerTileX,
|
||||
centerTileY: snapshot.runtime.viewport.centerTileY,
|
||||
rotationDeg: snapshot.runtime.viewport.rotationDeg,
|
||||
gpsLockEnabled: snapshot.runtime.viewport.gpsLockEnabled,
|
||||
hasGpsCenteredOnce: snapshot.runtime.viewport.hasGpsCenteredOnce,
|
||||
},
|
||||
currentGpsPoint: cloneLonLatPoint(snapshot.runtime.currentGpsPoint),
|
||||
currentGpsAccuracyMeters: snapshot.runtime.currentGpsAccuracyMeters,
|
||||
currentGpsInsideMap: snapshot.runtime.currentGpsInsideMap,
|
||||
bonusScore: snapshot.runtime.bonusScore,
|
||||
quizCorrectCount: snapshot.runtime.quizCorrectCount,
|
||||
quizWrongCount: snapshot.runtime.quizWrongCount,
|
||||
quizTimeoutCount: snapshot.runtime.quizTimeoutCount,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSessionRecoverySnapshot(raw: unknown): SessionRecoverySnapshot | null {
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
return null
|
||||
}
|
||||
|
||||
const candidate = raw as SessionRecoverySnapshot
|
||||
if (candidate.schemaVersion !== 1 || !candidate.runtime || !candidate.runtime.gameState) {
|
||||
return null
|
||||
}
|
||||
|
||||
return cloneSessionRecoverySnapshot(candidate)
|
||||
}
|
||||
|
||||
export function loadSessionRecoverySnapshot(): SessionRecoverySnapshot | null {
|
||||
try {
|
||||
return normalizeSessionRecoverySnapshot(wx.getStorageSync(SESSION_RECOVERY_STORAGE_KEY))
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function saveSessionRecoverySnapshot(snapshot: SessionRecoverySnapshot): void {
|
||||
try {
|
||||
wx.setStorageSync(SESSION_RECOVERY_STORAGE_KEY, cloneSessionRecoverySnapshot(snapshot))
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function clearSessionRecoverySnapshot(): void {
|
||||
try {
|
||||
wx.removeStorageSync(SESSION_RECOVERY_STORAGE_KEY)
|
||||
} catch {}
|
||||
}
|
||||
Reference in New Issue
Block a user