|
|
@@ -12,6 +12,8 @@ import {
|
|
|
type GameLaunchEnvelope,
|
|
|
type MapPageLaunchOptions,
|
|
|
} from '../../utils/gameLaunch'
|
|
|
+import { finishSession, startSession, type BackendSessionFinishSummaryPayload } from '../../utils/backendApi'
|
|
|
+import { loadBackendBaseUrl } from '../../utils/backendAuth'
|
|
|
import { loadRemoteMapConfig, type RemoteMapConfig } from '../../utils/remoteMapConfig'
|
|
|
import { type H5ExperienceFallbackPayload, type H5ExperienceRequest } from '../../game/experience/h5Experience'
|
|
|
import { type TrackColorPreset } from '../../game/presentation/trackStyleConfig'
|
|
|
@@ -173,6 +175,8 @@ let lastPunchHintHapticAt = 0
|
|
|
let currentSystemSettingsConfig: SystemSettingsConfig | undefined
|
|
|
let currentRemoteMapConfig: RemoteMapConfig | undefined
|
|
|
let systemSettingsLockLifetimeActive = false
|
|
|
+let syncedBackendSessionStartId = ''
|
|
|
+let syncedBackendSessionFinishId = ''
|
|
|
let lastCenterScaleRulerStablePatch: Pick<
|
|
|
MapPageData,
|
|
|
| 'centerScaleRulerVisible'
|
|
|
@@ -441,6 +445,37 @@ function hasExplicitLaunchOptions(options?: MapPageLaunchOptions | null): boolea
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+function getCurrentBackendSessionContext(): { sessionId: string; sessionToken: string } | null {
|
|
|
+ const business = currentGameLaunchEnvelope.business
|
|
|
+ if (!business || !business.sessionId || !business.sessionToken) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ sessionId: business.sessionId,
|
|
|
+ sessionToken: business.sessionToken,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getBackendSessionContextFromLaunchEnvelope(envelope: GameLaunchEnvelope | null | undefined): { sessionId: string; sessionToken: string } | null {
|
|
|
+ if (!envelope || !envelope.business || !envelope.business.sessionId || !envelope.business.sessionToken) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ sessionId: envelope.business.sessionId,
|
|
|
+ sessionToken: envelope.business.sessionToken,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getCurrentBackendBaseUrl(): string {
|
|
|
+ const app = getApp<IAppOption>()
|
|
|
+ if (app.globalData && app.globalData.backendBaseUrl) {
|
|
|
+ return app.globalData.backendBaseUrl
|
|
|
+ }
|
|
|
+ return loadBackendBaseUrl()
|
|
|
+}
|
|
|
+
|
|
|
function buildSideButtonVisibility(mode: SideButtonMode) {
|
|
|
return {
|
|
|
sideButtonMode: mode,
|
|
|
@@ -871,6 +906,8 @@ Page({
|
|
|
|
|
|
onLoad(options: MapPageLaunchOptions) {
|
|
|
clearSessionRecoveryPersistTimer()
|
|
|
+ syncedBackendSessionStartId = ''
|
|
|
+ syncedBackendSessionFinishId = ''
|
|
|
currentGameLaunchEnvelope = resolveGameLaunchEnvelope(options)
|
|
|
if (!hasExplicitLaunchOptions(options)) {
|
|
|
const recoverySnapshot = loadSessionRecoverySnapshot()
|
|
|
@@ -991,6 +1028,8 @@ Page({
|
|
|
const nextAnimationLevel = typeof nextPatch.animationLevel === 'string'
|
|
|
? nextPatch.animationLevel
|
|
|
: this.data.animationLevel
|
|
|
+ let shouldSyncBackendSessionStart = false
|
|
|
+ let backendSessionFinishStatus: 'finished' | 'failed' | null = null
|
|
|
|
|
|
if (nextAnimationLevel === 'lite') {
|
|
|
clearHudFxTimer('timer')
|
|
|
@@ -1055,6 +1094,7 @@ Page({
|
|
|
nextData.showGameInfoPanel = false
|
|
|
nextData.showSystemSettingsPanel = false
|
|
|
clearGameInfoPanelSyncTimer()
|
|
|
+ backendSessionFinishStatus = nextPatch.gameSessionStatus === 'finished' ? 'finished' : 'failed'
|
|
|
} else if (
|
|
|
nextPatch.gameSessionStatus !== this.data.gameSessionStatus
|
|
|
&& nextPatch.gameSessionStatus === 'idle'
|
|
|
@@ -1064,6 +1104,11 @@ Page({
|
|
|
shouldSyncRuntimeSystemSettings = true
|
|
|
clearSessionRecoverySnapshot()
|
|
|
clearSessionRecoveryPersistTimer()
|
|
|
+ } else if (
|
|
|
+ nextPatch.gameSessionStatus !== this.data.gameSessionStatus
|
|
|
+ && nextPatch.gameSessionStatus === 'running'
|
|
|
+ ) {
|
|
|
+ shouldSyncBackendSessionStart = true
|
|
|
} else if (nextPatch.gameSessionStatus === 'running' || nextPatch.gameSessionStatus === 'idle') {
|
|
|
nextData.showResultScene = false
|
|
|
}
|
|
|
@@ -1077,6 +1122,12 @@ Page({
|
|
|
if (typeof nextPatch.gameSessionStatus === 'string') {
|
|
|
this.syncSessionRecoveryLifecycle(nextPatch.gameSessionStatus)
|
|
|
}
|
|
|
+ if (shouldSyncBackendSessionStart) {
|
|
|
+ this.syncBackendSessionStart()
|
|
|
+ }
|
|
|
+ if (backendSessionFinishStatus) {
|
|
|
+ this.syncBackendSessionFinish(backendSessionFinishStatus)
|
|
|
+ }
|
|
|
if (shouldSyncRuntimeSystemSettings) {
|
|
|
this.applyRuntimeSystemSettings(nextLockLifetimeActive)
|
|
|
}
|
|
|
@@ -1088,6 +1139,12 @@ Page({
|
|
|
if (typeof nextPatch.gameSessionStatus === 'string') {
|
|
|
this.syncSessionRecoveryLifecycle(nextPatch.gameSessionStatus)
|
|
|
}
|
|
|
+ if (shouldSyncBackendSessionStart) {
|
|
|
+ this.syncBackendSessionStart()
|
|
|
+ }
|
|
|
+ if (backendSessionFinishStatus) {
|
|
|
+ this.syncBackendSessionFinish(backendSessionFinishStatus)
|
|
|
+ }
|
|
|
if (shouldSyncRuntimeSystemSettings) {
|
|
|
this.applyRuntimeSystemSettings(nextLockLifetimeActive)
|
|
|
}
|
|
|
@@ -1283,6 +1340,8 @@ Page({
|
|
|
onUnload() {
|
|
|
this.persistSessionRecoverySnapshot()
|
|
|
clearSessionRecoveryPersistTimer()
|
|
|
+ syncedBackendSessionStartId = ''
|
|
|
+ syncedBackendSessionFinishId = ''
|
|
|
clearGameInfoPanelSyncTimer()
|
|
|
clearCenterScaleRulerSyncTimer()
|
|
|
clearCenterScaleRulerUpdateTimer()
|
|
|
@@ -1332,6 +1391,114 @@ Page({
|
|
|
return true
|
|
|
},
|
|
|
|
|
|
+ syncBackendSessionStart() {
|
|
|
+ const sessionContext = getCurrentBackendSessionContext()
|
|
|
+ if (!sessionContext || syncedBackendSessionStartId === sessionContext.sessionId) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ startSession({
|
|
|
+ baseUrl: getCurrentBackendBaseUrl(),
|
|
|
+ sessionId: sessionContext.sessionId,
|
|
|
+ sessionToken: sessionContext.sessionToken,
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ syncedBackendSessionStartId = sessionContext.sessionId
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ const message = error && error.message ? error.message : '未知错误'
|
|
|
+ this.setData({
|
|
|
+ statusText: `session start 上报失败: ${message}`,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ syncBackendSessionFinish(statusOverride?: 'finished' | 'failed' | 'cancelled') {
|
|
|
+ const sessionContext = getCurrentBackendSessionContext()
|
|
|
+ if (!sessionContext || syncedBackendSessionFinishId === sessionContext.sessionId || !mapEngine) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const finishSummary = mapEngine.getSessionFinishSummary(statusOverride)
|
|
|
+ if (!finishSummary) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const summaryPayload: BackendSessionFinishSummaryPayload = {}
|
|
|
+ if (typeof finishSummary.finalDurationSec === 'number') {
|
|
|
+ summaryPayload.finalDurationSec = finishSummary.finalDurationSec
|
|
|
+ }
|
|
|
+ if (typeof finishSummary.finalScore === 'number') {
|
|
|
+ summaryPayload.finalScore = finishSummary.finalScore
|
|
|
+ }
|
|
|
+ if (typeof finishSummary.completedControls === 'number') {
|
|
|
+ summaryPayload.completedControls = finishSummary.completedControls
|
|
|
+ }
|
|
|
+ if (typeof finishSummary.totalControls === 'number') {
|
|
|
+ summaryPayload.totalControls = finishSummary.totalControls
|
|
|
+ }
|
|
|
+ if (typeof finishSummary.distanceMeters === 'number') {
|
|
|
+ summaryPayload.distanceMeters = finishSummary.distanceMeters
|
|
|
+ }
|
|
|
+ if (typeof finishSummary.averageSpeedKmh === 'number') {
|
|
|
+ summaryPayload.averageSpeedKmh = finishSummary.averageSpeedKmh
|
|
|
+ }
|
|
|
+
|
|
|
+ finishSession({
|
|
|
+ baseUrl: getCurrentBackendBaseUrl(),
|
|
|
+ sessionId: sessionContext.sessionId,
|
|
|
+ sessionToken: sessionContext.sessionToken,
|
|
|
+ status: finishSummary.status,
|
|
|
+ summary: summaryPayload,
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ syncedBackendSessionFinishId = sessionContext.sessionId
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ const message = error && error.message ? error.message : '未知错误'
|
|
|
+ this.setData({
|
|
|
+ statusText: `session finish 上报失败: ${message}`,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ reportAbandonedRecoverySnapshot(snapshot: SessionRecoverySnapshot) {
|
|
|
+ const sessionContext = getBackendSessionContextFromLaunchEnvelope(snapshot.launchEnvelope)
|
|
|
+ if (!sessionContext) {
|
|
|
+ clearSessionRecoverySnapshot()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ finishSession({
|
|
|
+ baseUrl: getCurrentBackendBaseUrl(),
|
|
|
+ sessionId: sessionContext.sessionId,
|
|
|
+ sessionToken: sessionContext.sessionToken,
|
|
|
+ status: 'cancelled',
|
|
|
+ summary: {},
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ syncedBackendSessionFinishId = sessionContext.sessionId
|
|
|
+ clearSessionRecoverySnapshot()
|
|
|
+ wx.showToast({
|
|
|
+ title: '已放弃上次对局',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 1400,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ clearSessionRecoverySnapshot()
|
|
|
+ const message = error && error.message ? error.message : '未知错误'
|
|
|
+ this.setData({
|
|
|
+ statusText: `放弃恢复已生效,后端取消上报失败: ${message}`,
|
|
|
+ })
|
|
|
+ wx.showToast({
|
|
|
+ title: '已放弃上次对局',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 1400,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
syncSessionRecoveryLifecycle(status: MapPageData['gameSessionStatus']) {
|
|
|
if (status === 'running') {
|
|
|
this.persistSessionRecoverySnapshot()
|
|
|
@@ -1368,7 +1535,7 @@ Page({
|
|
|
cancelText: '放弃',
|
|
|
success: (result) => {
|
|
|
if (!result.confirm) {
|
|
|
- clearSessionRecoverySnapshot()
|
|
|
+ this.reportAbandonedRecoverySnapshot(snapshot)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
@@ -1385,15 +1552,19 @@ Page({
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- this.setData({
|
|
|
- showResultScene: false,
|
|
|
- showDebugPanel: false,
|
|
|
- showGameInfoPanel: false,
|
|
|
- showSystemSettingsPanel: false,
|
|
|
- })
|
|
|
- this.syncSessionRecoveryLifecycle('running')
|
|
|
- },
|
|
|
- })
|
|
|
+ this.setData({
|
|
|
+ showResultScene: false,
|
|
|
+ showDebugPanel: false,
|
|
|
+ showGameInfoPanel: false,
|
|
|
+ showSystemSettingsPanel: false,
|
|
|
+ })
|
|
|
+ const sessionContext = getCurrentBackendSessionContext()
|
|
|
+ if (sessionContext) {
|
|
|
+ syncedBackendSessionStartId = sessionContext.sessionId
|
|
|
+ }
|
|
|
+ this.syncSessionRecoveryLifecycle('running')
|
|
|
+ },
|
|
|
+ })
|
|
|
},
|
|
|
|
|
|
compileCurrentRuntimeProfile(lockLifetimeActive = isSystemSettingsLockLifetimeActive()) {
|
|
|
@@ -1537,7 +1708,10 @@ Page({
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- const errorMessage = error && error.message ? error.message : '未知错误'
|
|
|
+ const rawErrorMessage = error && error.message ? error.message : '未知错误'
|
|
|
+ const errorMessage = rawErrorMessage.indexOf('404') >= 0
|
|
|
+ ? `release manifest 不存在或未发布 (${configLabel})`
|
|
|
+ : rawErrorMessage
|
|
|
this.setData({
|
|
|
configStatusText: `载入失败: ${errorMessage}`,
|
|
|
statusText: `远程地图配置载入失败: ${errorMessage} (${INTERNAL_BUILD_VERSION})`,
|
|
|
@@ -1939,18 +2113,19 @@ Page({
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- wx.showModal({
|
|
|
- title: '确认退出',
|
|
|
- content: '确认强制结束当前对局并返回开始前状态?',
|
|
|
- confirmText: '确认退出',
|
|
|
- cancelText: '取消',
|
|
|
- success: (result) => {
|
|
|
- if (result.confirm && mapEngine) {
|
|
|
- systemSettingsLockLifetimeActive = false
|
|
|
- mapEngine.handleForceExitGame()
|
|
|
- }
|
|
|
- },
|
|
|
- })
|
|
|
+ wx.showModal({
|
|
|
+ title: '确认退出',
|
|
|
+ content: '确认强制结束当前对局并返回开始前状态?',
|
|
|
+ confirmText: '确认退出',
|
|
|
+ cancelText: '取消',
|
|
|
+ success: (result) => {
|
|
|
+ if (result.confirm && mapEngine) {
|
|
|
+ this.syncBackendSessionFinish('cancelled')
|
|
|
+ systemSettingsLockLifetimeActive = false
|
|
|
+ mapEngine.handleForceExitGame()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ })
|
|
|
},
|
|
|
|
|
|
handleSkipAction() {
|