declare const wx: any; /** A terminal gate for this process. Only a real package restart clears it. */ export default class ClientUpdate { private static initialized = false; private static manager: any = null; private static state: "checking" | "downloading" | "ready" | "unavailable" = "checking"; private static rejection: any = null; private static dialogOpen = false; private static waitTimer: any = null; private static previewPanel: HTMLElement = null; static get blocked(): boolean { return this.rejection !== null; } static get blockedResponse(): any { return this.rejection; } static initialize(simulateInPreview = false): void { if (this.initialized) return; this.initialized = true; if (typeof wx !== "undefined" && typeof wx.getUpdateManager === "function") { try { this.manager = wx.getUpdateManager(); this.manager.onCheckForUpdate(result => { if (this.state === "ready") return; this.state = result.hasUpdate ? "downloading" : "unavailable"; if (this.blocked) this.showPrompt(); }); this.manager.onUpdateReady(() => { this.state = "ready"; if (this.blocked) this.showPrompt(); }); this.manager.onUpdateFailed(() => { this.state = "unavailable"; if (this.blocked) this.showPrompt(); }); } catch (_) { this.state = "unavailable"; } } else { this.state = "unavailable"; } // Local UI simulation only. Never changes the reported version or bypasses the server gate. if (typeof CC_PREVIEW !== "undefined" && CC_PREVIEW && simulateInPreview) { this.handleResponse({ code: 0, errorCode: "CLIENT_UPDATE_REQUIRED", msg: "强更流程预览", data: null }); } } /** Also converts late successful responses to the same error after another request blocks us. */ static handleResponse(response: any): any { if (!this.blocked && response?.errorCode === "CLIENT_UPDATE_REQUIRED") { this.initialize(); this.rejection = response; this.freezeGame(); this.showPrompt(); } return this.rejection || response; } /** GameManager calls this before its normal foreground resume. */ static onShow(): boolean { if (!this.blocked) return false; this.freezeGame(); this.showPrompt(); return true; } private static freezeGame(): void { cc.fx.GameConfig.GM_INFO.gameState = false; const scene = cc.director.getScene(); if (scene) scene.pauseSystemEvents(true); cc.audioEngine.pauseAll(); cc.game.pause(); } private static clearWait(): void { if (this.waitTimer !== null) clearTimeout(this.waitTimer); this.waitTimer = null; } private static showPrompt(): void { if (typeof wx === "undefined" || typeof wx.showModal !== "function") { this.showPreviewPanel(); return; } if (this.state === "checking" || this.state === "downloading") { if (this.dialogOpen) return; wx.showLoading({ title: "正在准备更新", mask: true }); if (this.waitTimer === null) { this.waitTimer = setTimeout(() => { this.waitTimer = null; if (this.state !== "ready") { this.state = "unavailable"; this.showPrompt(); } }, 30000); } return; } this.clearWait(); if (this.dialogOpen) return; wx.hideLoading(); const promptState = this.state; const ready = promptState === "ready"; this.dialogOpen = true; wx.showModal({ title: "游戏需要更新", content: ready ? "新版本已准备好,请重启更新。更新后将自动登录。" : "当前版本已无法继续游戏,暂未取得可用的新版本。请退出后重新进入;仍有问题请稍后再试。", showCancel: false, confirmText: ready ? "重启更新" : "退出游戏", success: result => { if (!result.confirm || this.state !== promptState) return; if (ready) { try { this.manager.applyUpdate(); } catch (_) { this.state = "unavailable"; } } else if (typeof wx.exitMiniProgram === "function") { wx.exitMiniProgram({ fail: () => { wx.showToast({ title: "请手动退出后重新进入", icon: "none", duration: 3000 }); } }); } }, complete: () => { this.dialogOpen = false; if (this.state !== promptState) { setTimeout(() => this.showPrompt(), 0); } else { // Keep a visible mask even if the platform refuses to exit/restart or dismisses the modal. wx.showLoading({ title: ready ? "请重启游戏" : "请重新进入游戏", mask: true }); } }, }); } private static showPreviewPanel(): void { if (this.previewPanel || typeof document === "undefined" || !document.body) return; const panel = document.createElement("div"); panel.style.cssText = "position:fixed;inset:0;z-index:2147483647;background:#182036;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:24px;font:18px sans-serif"; const title = document.createElement("h2"); title.textContent = "游戏需要更新"; const message = document.createElement("p"); message.textContent = "请加载兼容的新版本后刷新页面。更新后将自动登录。"; const button = document.createElement("button"); button.textContent = "刷新页面"; button.style.cssText = "padding:12px 28px;margin-top:20px;font:18px sans-serif;cursor:pointer"; button.onclick = () => window.location.reload(); panel.appendChild(title); panel.appendChild(message); panel.appendChild(button); document.body.appendChild(panel); this.previewPanel = panel; } }