Files
MatchMaster/assets/Script/module/Config/ClientUpdate.ts
T
2026-10-13 09:08:25 +08:00

150 lines
6.4 KiB
TypeScript

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;
}
}