I’ve applied the Cocos Creator Scorching Replace system for my Android mission. The replace course of appears to work nice, the information are efficiently downloaded from the distant server, and I reset/restart the sport after the replace.
Nonetheless, after restarting, the sport nonetheless runs with the previous belongings and logic. The newly downloaded information don’t appear to take impact.
import { _decorator, assetManager, Part, instantiate, JsonAsset, Node, Prefab, sys, SpriteFrame, Texture2D, AudioClip, Sprite, AudioSource, native, Asset, sport, director, AssetManager, Label, ProgressBar, Button } from ‘cc’;
import { NATIVE } from ‘cc/env’;
const { ccclass, property } = _decorator;
@ccclass(‘RemoteEntryLoader’)
export class RemoteEntryLoader extends Part {
@property(Asset)
manifestUrl: Asset = null!;
@property(Label)
statusLabel: Label = null!;
@property(ProgressBar)
progressBar: ProgressBar = null!;
@property(Button)
checkUpdateBtn: Button = null!;
@property(Button)
retryBtn: Button = null!;
non-public assetsManager: any = null;
non-public storagePath: string = ‘’;
// non-public manifestUrl: string = ‘’;
non-public updating: boolean = false;
non-public canRetry: boolean = false;
non-public updateListener: any = null;
onLoad() {
// Solely initialize on native platforms
if (!NATIVE) {
this.statusLabel.string = ‘Scorching replace is just obtainable on native platforms’;
return;
}
// Setup paths - customise these on your mission
this.storagePath = ((native.fileUtils && native.fileUtils.getWritablePath) ?
native.fileUtils.getWritablePath() : "https://discussion board.cocosengine.org/") + 'hot-update/';
// Your distant manifest URL - exchange together with your server URL
// this.manifestUrl="https://thanisthani.github.io/CocosHotUpdate/remote-assets/mission.manifest";
this.initHotUpdate();
this.setupUI();
}
non-public initHotUpdate(): void {
if (!NATIVE) return;
attempt {
if (NATIVE) {
const hotUpdateRoot = native.fileUtils.getWritablePath() + 'hot-update/';
console.log('Scorching replace listing exists:', native.fileUtils.isDirectoryExist(hotUpdateRoot));
console.log('Manifest exists:', native.fileUtils.isFileExist(hotUpdateRoot + 'mission.manifest'));
// Checklist information in sizzling replace listing
const information = native.fileUtils.listFiles(hotUpdateRoot);
console.log('Recordsdata in sizzling replace listing:', information);
}
// Guarantee storage listing exists
if (!native.fileUtils.isDirectoryExist(this.storagePath)) {
native.fileUtils.createDirectory(this.storagePath);
}
// Create AssetsManager occasion
this.assetsManager = new native.AssetsManager(this.manifestUrl.nativeUrl, this.storagePath);
// Arrange search paths for warm up to date belongings
this.setupSearchPaths();
// Configure AssetsManager
this.assetsManager.setMaxConcurrentTask(5); // Restrict concurrent downloads
// Set model comparability perform (optionally available)
this.assetsManager.setVersionCompareHandle((versionA: string, versionB: string) => {
return this.compareVersion(versionA, versionB);
});
// Set file verification callback (optionally available however really helpful)
this.assetsManager.setVerifyCallback((path: string, asset: any) => {
return this.verifyAsset(path, asset);
});
// Set replace occasion listener
this.updateListener = (occasion: any) => this.updateCallback(occasion);
this.assetsManager.setEventCallback(this.updateListener);
this.statusLabel.string = 'Scorching replace initialized';
} catch (error) {
console.error('Did not initialize sizzling replace:', error);
this.statusLabel.string = 'Did not initialize sizzling replace';
}
}
non-public setupSearchPaths(): void {
if (!NATIVE || !this.assetsManager) return;
attempt {
if (this.assetsManager.getState() === native.AssetsManager.State.UNINITED) {
console.log("loadLocalManifest is named")
if (this.manifestUrl && this.manifestUrl.nativeUrl) {
this.assetsManager.loadLocalManifest(this.manifestUrl.nativeUrl);
}
}
// Get sizzling replace search paths from native manifest
const localManifest = this.assetsManager.getLocalManifest();
if (!localManifest || !localManifest.isLoaded()) {
console.log("Did not load bundled manifest");
throw new Error('Did not load bundled manifest');
}
if (localManifest && localManifest.isLoaded()) {
console.log("localManifest is loaded");
const hotUpdateSearchPaths = localManifest.getSearchPaths();
const searchPaths = native.fileUtils.getSearchPaths();
// Insert sizzling replace paths at the start for precedence
Array.prototype.unshift.apply(searchPaths, hotUpdateSearchPaths);
native.fileUtils.setSearchPaths(searchPaths);
console.log('Search paths up to date:', searchPaths);
}
} catch (error) {
console.error('Did not setup search paths:', error);
}
}
non-public setupUI(): void {
// Setup button callbacks
if (this.checkUpdateBtn) {
this.checkUpdateBtn.node.on(‘click on’, this.checkForUpdate, this);
}
if (this.retryBtn) {
this.retryBtn.node.on('click on', this.retry, this);
this.retryBtn.node.energetic = false;
}
}
non-public checkForUpdate(): void
non-public retry(): void {
if (!this.canRetry) return;
this.canRetry = false;
this.retryBtn.node.energetic = false;
this.statusLabel.string = 'Retrying failed downloads...';
// Retry downloading failed belongings
this.assetsManager.downloadFailedAssets();
}
non-public updateCallback(occasion: any): void {
const eventCode = occasion.getEventCode();
console.log(“replace eventCode is”, eventCode);
change (eventCode) {
case native.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
this.statusLabel.string = 'No native manifest file discovered';
this.onUpdateFinished(false);
break;
case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
this.statusLabel.string = 'Did not obtain manifest';
this.onUpdateFinished(false);
break;
case native.EventAssetsManager.ERROR_PARSE_MANIFEST:
this.statusLabel.string = 'Did not parse manifest';
this.onUpdateFinished(false);
break;
case native.EventAssetsManager.NEW_VERSION_FOUND:
this.statusLabel.string = 'New model discovered, beginning obtain...';
this.assetsManager.replace();
break;
case native.EventAssetsManager.ALREADY_UP_TO_DATE:
this.statusLabel.string = 'Already updated';
this.onUpdateFinished(true);
break;
case native.EventAssetsManager.UPDATE_PROGRESSION:
this.handleUpdateProgress(occasion);
break;
case native.EventAssetsManager.ASSET_UPDATED:
// Particular person asset up to date
break;
case native.EventAssetsManager.ERROR_UPDATING:
this.statusLabel.string = 'Replace error: ' + occasion.getMessage();
this.onUpdateFinished(false);
break;
case native.EventAssetsManager.UPDATE_FINISHED:
this.statusLabel.string = 'Replace accomplished! Restart required.';
const searchPaths = native.fileUtils.getSearchPaths();
console.log("searchPaths is", searchPaths);
const hotUpdatePath = this.storagePath;
// Take away if exists, then add at starting
const index = searchPaths.indexOf(hotUpdatePath);
if (index > -1) searchPaths.splice(index, 1);
searchPaths.unshift(hotUpdatePath);
native.fileUtils.setSearchPaths(searchPaths);
console.log("up to date searchPaths is", searchPaths);
// Save restart flag
sys.localStorage.setItem('hotUpdateReady', 'true');
sys.localStorage.setItem('hotUpdatePath', hotUpdatePath);
this.onUpdateFinished(true, true);
break;
case native.EventAssetsManager.UPDATE_FAILED:
this.statusLabel.string = 'Replace failed: ' + occasion.getMessage();
this.canRetry = true;
this.retryBtn.node.energetic = true;
this.onUpdateFinished(false);
break;
case native.EventAssetsManager.ERROR_DECOMPRESS:
this.statusLabel.string = 'Decompression failed';
this.onUpdateFinished(false);
break;
}
}
non-public handleUpdateProgress(occasion: any): void {
const p.c = occasion.getPercent();
const filePercent = occasion.getPercentByFile();
const downloadedBytes = occasion.getDownloadedBytes();
const totalBytes = occasion.getTotalBytes();
const downloadedFiles = occasion.getDownloadedFiles();
const totalFiles = occasion.getTotalFiles();
// Replace progress bar (utilizing byte progress)
this.progressBar.progress = p.c / 100;
// Replace standing label with detailed progress
this.statusLabel.string = `Downloading... ${Math.flooring(p.c)}%n` +
`Recordsdata: ${downloadedFiles}/${totalFiles}n` +
`Dimension: ${this.formatBytes(downloadedBytes)}/${this.formatBytes(totalBytes)}`;
console.log(`Downloading... ${Math.flooring(p.c)}%n` +
`Recordsdata: ${downloadedFiles}/${totalFiles}n` +
`Dimension: ${this.formatBytes(downloadedBytes)}/${this.formatBytes(totalBytes)}`)
}
non-public onUpdateFinished(success: boolean, needRestart: boolean = false): void {
this.updating = false;
this.checkUpdateBtn.node.energetic = true;
if (needRestart) {
// Present restart button or robotically restart
this.showRestartOption();
}
}
non-public showRestartOption(): void {
// You’ll be able to present a restart dialog right here
// For now, we’ll simply add a restart button performance
this.statusLabel.string = ‘Replace accomplished! Faucet to restart.’;
this.checkUpdateBtn.getComponentInChildren(Label)!.string = ‘Restart Recreation’;
this.checkUpdateBtn.node.off(‘click on’, this.checkForUpdate, this);
this.checkUpdateBtn.node.on(‘click on’, this.restartGame, this);
}
non-public restartGame(): void {
console.log(“restartGame is named”);
// Clear up earlier than restart
if (this.assetsManager) {
this.assetsManager.setEventCallback(null);
}
// Restart the sport
sport.restart();
}
non-public compareVersion(versionA: string, versionB: string): quantity {
// Customized model comparability logic
// Return > 0 if versionA > versionB
// Return 0 if versionA == versionB
// Return
const parseVersion = (model: string) => 0);
;
const vA = parseVersion(versionA);
const vB = parseVersion(versionB);
for (let i = 0; i
}
non-public verifyAsset(filePath: string, asset: any): boolean {
// Asset verification logic
// You’ll be able to implement MD5 examine right here if wanted
// For now, simply return true to skip verification
return true;
}
non-public formatBytes(bytes: quantity): string {
if (bytes === 0) return ‘0 B’;
const okay = 1024;
const sizes = [‘B’, ‘KB’, ‘MB’, ‘GB’];
const i = Math.flooring(Math.log(bytes) / Math.log(okay));
return parseFloat((bytes / Math.pow(okay, i)).toFixed(2)) + ’ ’ + sizes[i];
}
onDestroy(): void {
// Clear up
if (this.assetsManager && this.updateListener) {
this.assetsManager.setEventCallback(null);
}
}
}