| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- export type DemoGamePreset = 'classic' | 'score-o'
- export type BusinessLaunchSource = 'demo' | 'competition' | 'direct-event' | 'custom'
- export interface GameConfigLaunchRequest {
- configUrl: string
- configLabel: string
- configChecksumSha256?: string | null
- releaseId?: string | null
- routeCode?: string | null
- }
- export interface GameResolvedReleaseLaunchContext {
- launchMode?: string | null
- source?: string | null
- eventId?: string | null
- releaseId?: string | null
- configLabel?: string | null
- manifestUrl?: string | null
- manifestChecksumSha256?: string | null
- routeCode?: string | null
- }
- export interface BusinessLaunchContext {
- source: BusinessLaunchSource
- competitionId?: string | null
- eventId?: string | null
- launchRequestId?: string | null
- participantId?: string | null
- sessionId?: string | null
- sessionToken?: string | null
- sessionTokenExpiresAt?: string | null
- realtimeEndpoint?: string | null
- realtimeToken?: string | null
- }
- export interface GameVariantLaunchContext {
- variantId?: string | null
- variantName?: string | null
- routeCode?: string | null
- assignmentMode?: string | null
- }
- export interface GameRuntimeLaunchContext {
- runtimeBindingId?: string | null
- placeId?: string | null
- placeName?: string | null
- mapId?: string | null
- mapName?: string | null
- tileReleaseId?: string | null
- courseSetId?: string | null
- courseVariantId?: string | null
- routeCode?: string | null
- }
- export interface GamePresentationLaunchContext {
- presentationId?: string | null
- templateKey?: string | null
- version?: string | null
- }
- export interface GameContentBundleLaunchContext {
- bundleId?: string | null
- bundleType?: string | null
- version?: string | null
- }
- export interface GameLaunchEnvelope {
- config: GameConfigLaunchRequest
- business: BusinessLaunchContext | null
- resolvedRelease?: GameResolvedReleaseLaunchContext | null
- variant?: GameVariantLaunchContext | null
- runtime?: GameRuntimeLaunchContext | null
- presentation?: GamePresentationLaunchContext | null
- contentBundle?: GameContentBundleLaunchContext | null
- }
- export interface MapPageLaunchOptions {
- launchId?: string
- recoverSession?: string
- autoStartOnEnter?: string
- preset?: string
- configUrl?: string
- configLabel?: string
- configChecksumSha256?: string
- releaseId?: string
- routeCode?: string
- launchSource?: string
- competitionId?: string
- eventId?: string
- launchRequestId?: string
- participantId?: string
- sessionId?: string
- sessionToken?: string
- sessionTokenExpiresAt?: string
- realtimeEndpoint?: string
- realtimeToken?: string
- variantId?: string
- variantName?: string
- assignmentMode?: string
- runtimeBindingId?: string
- placeId?: string
- placeName?: string
- mapId?: string
- mapName?: string
- tileReleaseId?: string
- courseSetId?: string
- courseVariantId?: string
- presentationId?: string
- presentationTemplateKey?: string
- presentationVersion?: string
- contentBundleId?: string
- contentBundleType?: string
- contentBundleVersion?: string
- }
- type PendingGameLaunchStore = Record<string, GameLaunchEnvelope>
- const PENDING_GAME_LAUNCH_STORAGE_KEY = 'cmr.pendingGameLaunch.v1'
- const CLASSIC_REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/gotomars/event/classic-sequential.json'
- const SCORE_O_REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/gotomars/event/score-o.json'
- function normalizeOptionalString(value: unknown): string | null {
- if (typeof value !== 'string') {
- return null
- }
- const normalized = decodeURIComponent(value).trim()
- return normalized ? normalized : null
- }
- function resolveDemoPreset(value: string | null): DemoGamePreset {
- return value === 'score-o' ? 'score-o' : 'classic'
- }
- function resolveBusinessLaunchSource(value: string | null): BusinessLaunchSource {
- if (value === 'competition' || value === 'direct-event' || value === 'custom') {
- return value
- }
- return 'demo'
- }
- function buildDemoConfig(preset: DemoGamePreset): GameConfigLaunchRequest {
- if (preset === 'score-o') {
- return {
- configUrl: SCORE_O_REMOTE_GAME_CONFIG_URL,
- configLabel: '积分赛配置',
- }
- }
- return {
- configUrl: CLASSIC_REMOTE_GAME_CONFIG_URL,
- configLabel: '顺序赛配置',
- }
- }
- function hasBusinessFields(context: Omit<BusinessLaunchContext, 'source'>): boolean {
- return Object.values(context).some((value) => typeof value === 'string' && value.length > 0)
- }
- function buildBusinessLaunchContext(options?: MapPageLaunchOptions | null): BusinessLaunchContext | null {
- if (!options) {
- return null
- }
- const context = {
- competitionId: normalizeOptionalString(options.competitionId),
- eventId: normalizeOptionalString(options.eventId),
- launchRequestId: normalizeOptionalString(options.launchRequestId),
- participantId: normalizeOptionalString(options.participantId),
- sessionId: normalizeOptionalString(options.sessionId),
- sessionToken: normalizeOptionalString(options.sessionToken),
- sessionTokenExpiresAt: normalizeOptionalString(options.sessionTokenExpiresAt),
- realtimeEndpoint: normalizeOptionalString(options.realtimeEndpoint),
- realtimeToken: normalizeOptionalString(options.realtimeToken),
- }
- const launchSource = normalizeOptionalString(options.launchSource)
- if (!hasBusinessFields(context) && launchSource === null) {
- return null
- }
- return {
- source: resolveBusinessLaunchSource(launchSource),
- ...context,
- }
- }
- function buildVariantLaunchContext(options?: MapPageLaunchOptions | null): GameVariantLaunchContext | null {
- if (!options) {
- return null
- }
- const variantId = normalizeOptionalString(options.variantId)
- const variantName = normalizeOptionalString(options.variantName)
- const routeCode = normalizeOptionalString(options.routeCode)
- const assignmentMode = normalizeOptionalString(options.assignmentMode)
- if (!variantId && !variantName && !routeCode && !assignmentMode) {
- return null
- }
- return {
- variantId,
- variantName,
- routeCode,
- assignmentMode,
- }
- }
- function buildRuntimeLaunchContext(options?: MapPageLaunchOptions | null): GameRuntimeLaunchContext | null {
- if (!options) {
- return null
- }
- const runtimeBindingId = normalizeOptionalString(options.runtimeBindingId)
- const placeId = normalizeOptionalString(options.placeId)
- const placeName = normalizeOptionalString(options.placeName)
- const mapId = normalizeOptionalString(options.mapId)
- const mapName = normalizeOptionalString(options.mapName)
- const tileReleaseId = normalizeOptionalString(options.tileReleaseId)
- const courseSetId = normalizeOptionalString(options.courseSetId)
- const courseVariantId = normalizeOptionalString(options.courseVariantId)
- const routeCode = normalizeOptionalString(options.routeCode)
- if (!runtimeBindingId && !placeId && !placeName && !mapId && !mapName && !tileReleaseId && !courseSetId && !courseVariantId && !routeCode) {
- return null
- }
- return {
- runtimeBindingId,
- placeId,
- placeName,
- mapId,
- mapName,
- tileReleaseId,
- courseSetId,
- courseVariantId,
- routeCode,
- }
- }
- function buildPresentationLaunchContext(options?: MapPageLaunchOptions | null): GamePresentationLaunchContext | null {
- if (!options) {
- return null
- }
- const presentationId = normalizeOptionalString(options.presentationId)
- const templateKey = normalizeOptionalString(options.presentationTemplateKey)
- const version = normalizeOptionalString(options.presentationVersion)
- if (!presentationId && !templateKey && !version) {
- return null
- }
- return {
- presentationId,
- templateKey,
- version,
- }
- }
- function buildContentBundleLaunchContext(options?: MapPageLaunchOptions | null): GameContentBundleLaunchContext | null {
- if (!options) {
- return null
- }
- const bundleId = normalizeOptionalString(options.contentBundleId)
- const bundleType = normalizeOptionalString(options.contentBundleType)
- const version = normalizeOptionalString(options.contentBundleVersion)
- if (!bundleId && !bundleType && !version) {
- return null
- }
- return {
- bundleId,
- bundleType,
- version,
- }
- }
- function loadPendingGameLaunchStore(): PendingGameLaunchStore {
- try {
- const stored = wx.getStorageSync(PENDING_GAME_LAUNCH_STORAGE_KEY)
- if (!stored || typeof stored !== 'object') {
- return {}
- }
- return stored as PendingGameLaunchStore
- } catch {
- return {}
- }
- }
- function savePendingGameLaunchStore(store: PendingGameLaunchStore): void {
- try {
- wx.setStorageSync(PENDING_GAME_LAUNCH_STORAGE_KEY, store)
- } catch {}
- }
- export function getDemoGameLaunchEnvelope(preset: DemoGamePreset = 'classic'): GameLaunchEnvelope {
- return {
- config: buildDemoConfig(preset),
- business: {
- source: 'demo',
- },
- resolvedRelease: null,
- variant: null,
- runtime: null,
- presentation: null,
- contentBundle: null,
- }
- }
- export function stashPendingGameLaunchEnvelope(envelope: GameLaunchEnvelope): string {
- const launchId = `launch_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`
- const store = loadPendingGameLaunchStore()
- store[launchId] = envelope
- savePendingGameLaunchStore(store)
- return launchId
- }
- export function consumePendingGameLaunchEnvelope(launchId: string): GameLaunchEnvelope | null {
- const normalizedLaunchId = normalizeOptionalString(launchId)
- if (!normalizedLaunchId) {
- return null
- }
- const store = loadPendingGameLaunchStore()
- const envelope = store[normalizedLaunchId] || null
- if (!envelope) {
- return null
- }
- delete store[normalizedLaunchId]
- savePendingGameLaunchStore(store)
- return envelope
- }
- export function buildMapPageUrlWithLaunchId(launchId: string, extraQuery?: Record<string, string>): string {
- const queryParts = [`launchId=${encodeURIComponent(launchId)}`]
- if (extraQuery) {
- Object.keys(extraQuery).forEach((key) => {
- const value = extraQuery[key]
- if (typeof value === 'string' && value) {
- queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
- }
- })
- }
- return `/pages/map/map?${queryParts.join('&')}`
- }
- export function prepareMapPageUrlForLaunch(envelope: GameLaunchEnvelope): string {
- return buildMapPageUrlWithLaunchId(
- stashPendingGameLaunchEnvelope(envelope),
- { autoStartOnEnter: '1' },
- )
- }
- export function prepareMapPageUrlForRecovery(envelope: GameLaunchEnvelope): string {
- return `${buildMapPageUrlWithLaunchId(stashPendingGameLaunchEnvelope(envelope))}&recoverSession=1`
- }
- export 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,
- }
- }
- export function resolveGameLaunchEnvelope(options?: MapPageLaunchOptions | null): GameLaunchEnvelope {
- const launchId = normalizeOptionalString(options ? options.launchId : undefined)
- if (launchId) {
- const pendingEnvelope = consumePendingGameLaunchEnvelope(launchId)
- if (pendingEnvelope) {
- return pendingEnvelope
- }
- }
- const configUrl = normalizeOptionalString(options ? options.configUrl : undefined)
- if (configUrl) {
- return {
- config: {
- configUrl,
- configLabel: normalizeOptionalString(options ? options.configLabel : undefined) || '线上配置',
- configChecksumSha256: normalizeOptionalString(options ? options.configChecksumSha256 : undefined),
- releaseId: normalizeOptionalString(options ? options.releaseId : undefined),
- routeCode: normalizeOptionalString(options ? options.routeCode : undefined),
- },
- business: buildBusinessLaunchContext(options),
- resolvedRelease: null,
- variant: buildVariantLaunchContext(options),
- runtime: buildRuntimeLaunchContext(options),
- presentation: buildPresentationLaunchContext(options),
- contentBundle: buildContentBundleLaunchContext(options),
- }
- }
- const preset = resolveDemoPreset(normalizeOptionalString(options ? options.preset : undefined))
- return getDemoGameLaunchEnvelope(preset)
- }
|