Skip to content

Commit

Permalink
Clean up asset downloader, preloader and some improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
xpenatan committed Jul 8, 2024
1 parent 86bede2 commit 175619c
Show file tree
Hide file tree
Showing 20 changed files with 360 additions and 350 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Call dispose when browser closes
- Improve clipboard text copy/paste.
- Change default sound/music api to howler.js
- add shouldEncodePreference config
- add localStoragePrefix config

[1.0.0-b9]
- add TeaClassFilter printAllowedClasses() and printExcludedClasses()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package com.badlogic.gdx.assets;

import com.badlogic.gdx.Files;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.loaders.AssetLoader;
import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader;
import com.badlogic.gdx.assets.loaders.SynchronousAssetLoader;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.Logger;
import com.badlogic.gdx.utils.TimeUtils;
import com.badlogic.gdx.utils.async.AsyncExecutor;
import com.badlogic.gdx.utils.async.AsyncResult;
import com.badlogic.gdx.utils.async.AsyncTask;
import com.github.xpenatan.gdx.backends.teavm.TeaApplication;
import com.github.xpenatan.gdx.backends.teavm.gen.Emulate;
import com.github.xpenatan.gdx.backends.teavm.preloader.AssetType;
import com.github.xpenatan.gdx.backends.teavm.preloader.Preloader;

@Emulate(AssetLoadingTask.class)
class AssetLoadingTaskEmu implements AsyncTask<Void> {
AssetManager manager;
final AssetDescriptor assetDesc;
final AssetLoader loader;
final AsyncExecutor executor;
final long startTime;

volatile boolean asyncDone;
volatile boolean dependenciesLoaded;
volatile Array<AssetDescriptor> dependencies;
volatile AsyncResult<Void> depsFuture;
volatile AsyncResult<Void> loadFuture;
volatile Object asset;

int ticks = 0;
volatile boolean cancel;

public AssetLoadingTaskEmu(AssetManager manager, AssetDescriptor assetDesc, AssetLoader loader, AsyncExecutor threadPool) {
this.manager = manager;
this.assetDesc = assetDesc;
this.loader = loader;
this.executor = threadPool;
startTime = manager.log.getLevel() == Logger.DEBUG ? TimeUtils.nanoTime() : 0;
}

/**
* Loads parts of the asset asynchronously if the loader is an {@link AsynchronousAssetLoader}.
*/
@Override
public Void call() throws Exception {
if(cancel) return null;
AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader;
if(!dependenciesLoaded) {
dependencies = asyncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
if(dependencies != null) {
removeDuplicates(dependencies);
manager.injectDependencies(assetDesc.fileName, dependencies);
}
else {
// if we have no dependencies, we load the async part of the task immediately.
asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
asyncDone = true;
}
}
else {
asyncLoader.loadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
asyncDone = true;
}
return null;
}

/**
* Updates the loading of the asset. In case the asset is loaded with an {@link AsynchronousAssetLoader}, the loaders
* {@link AsynchronousAssetLoader#loadAsync(AssetManager, String, FileHandle, AssetLoaderParameters)} method is first called on
* a worker thread. Once this method returns, the rest of the asset is loaded on the rendering thread via
* {@link AsynchronousAssetLoader#loadSync(AssetManager, String, FileHandle, AssetLoaderParameters)}.
*
* @return true in case the asset was fully loaded, false otherwise
* @throws GdxRuntimeException
*/
public boolean update() {
ticks++;

// GTW: check if we have a file that was not preloaded and is not done loading yet
Preloader preloader = ((TeaApplication)Gdx.app).getPreloader();
if(!preloader.isAssetLoaded(Files.FileType.Internal, assetDesc.fileName)) {
preloader.loadAsset(AssetType.Binary, Files.FileType.Internal, assetDesc.fileName);
boolean assetInQueue = preloader.isAssetInQueue(assetDesc.fileName);
// Loader.finishLoading breaks everything
if(!assetInQueue && ticks > 100000)
throw new GdxRuntimeException("File not prefetched, but finishLoading was probably called: " + assetDesc.fileName);
}
else {
if(loader instanceof SynchronousAssetLoader)
handleSyncLoader();
else
handleAsyncLoader();
}
return asset != null;
}

private void handleSyncLoader() {
SynchronousAssetLoader syncLoader = (SynchronousAssetLoader)loader;
if(!dependenciesLoaded) {
dependenciesLoaded = true;
dependencies = syncLoader.getDependencies(assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
if(dependencies == null) {
asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
return;
}
removeDuplicates(dependencies);
manager.injectDependencies(assetDesc.fileName, dependencies);
}
else
asset = syncLoader.load(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
}

private void handleAsyncLoader() {
AsynchronousAssetLoader asyncLoader = (AsynchronousAssetLoader)loader;
if(!dependenciesLoaded) {
if(depsFuture == null)
depsFuture = executor.submit(this);
else if(depsFuture.isDone()) {
try {
depsFuture.get();
} catch(Exception e) {
throw new GdxRuntimeException("Couldn't load dependencies of asset: " + assetDesc.fileName, e);
}
dependenciesLoaded = true;
if(asyncDone)
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
}
}
else if(loadFuture == null && !asyncDone)
loadFuture = executor.submit(this);
else if(asyncDone)
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
else if(loadFuture.isDone()) {
try {
loadFuture.get();
} catch(Exception e) {
throw new GdxRuntimeException("Couldn't load asset: " + assetDesc.fileName, e);
}
asset = asyncLoader.loadSync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
}
}

/**
* Called when this task is the task that is currently being processed and it is unloaded.
*/
public void unload() {
if(loader instanceof AsynchronousAssetLoader)
((AsynchronousAssetLoader)loader).unloadAsync(manager, assetDesc.fileName, resolve(loader, assetDesc), assetDesc.params);
}

private FileHandle resolve(AssetLoader loader, AssetDescriptor assetDesc) {
if(assetDesc.file == null) assetDesc.file = loader.resolve(assetDesc.fileName);
return assetDesc.file;
}

private void removeDuplicates(Array<AssetDescriptor> array) {
boolean ordered = array.ordered;
array.ordered = true;
for(int i = 0; i < array.size; ++i) {
final String fn = array.get(i).fileName;
final Class type = array.get(i).type;
for(int j = array.size - 1; j > i; --j)
if(type == array.get(j).type && fn.equals(array.get(j).fileName)) array.removeIndex(j);
}
array.ordered = ordered;
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,14 @@ public void onFailure(String url) {
}

@Override
public boolean onSuccess(String url, Blob result) {
public void onSuccess(String url, Blob result) {
Int8ArrayWrapper data = result.getData();
byte[] byteArray = TypedArrays.toByteArray(data);
Pixmap pixmapEmu = new Pixmap(byteArray, 0, byteArray.length);
responseListener.downloadComplete(pixmapEmu);
return false;
}
};
AssetDownloader.getInstance().load(true, url, AssetType.Binary, null, listener);
AssetDownloader.getInstance().load(true, url, AssetType.Binary, listener);
}

public PixmapEmu(FileHandle file) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ public void load (String libraryName) {
Preloader preloader = app.getPreloader();
preloader.loadScript(false, libraryName + ".js", new AssetLoaderListener<Object>() {
@Override
public boolean onSuccess(String url, Object result) {
return true;
public void onSuccess(String url, Object result) {
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ public void onProgress(double amount) {
public void onFailure(String url) {
}

public boolean onSuccess(String url, T result) {
return false;
public void onSuccess(String url, T result) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ public Preferences getPreferences(String name) {
Preferences pref = prefs.get(name);
if(pref == null) {
Storage storage = Storage.getLocalStorage();;
pref = new TeaPreferences(storage, config.storagePrefix + name);
pref = new TeaPreferences(storage, config.storagePrefix + ":" + name, config.shouldEncodePreference);
prefs.put(name, pref);
}
return pref;
Expand Down Expand Up @@ -481,18 +481,16 @@ public enum AppState {
private void initGdx() {
preloader.loadScript(true, "gdx.wasm.js", new AssetLoaderListener<Object>() {
@Override
public boolean onSuccess(String url, Object result) {
return true;
public void onSuccess(String url, Object result) {
}
});
}

private void initSound() {
preloader.loadScript(true, "howler.js", new AssetLoaderListener<Object>() {
@Override
public boolean onSuccess(String url, Object result) {
return true;
public void onSuccess(String url, Object result) {
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public class TeaApplicationConfiguration {
* browser is not shared between the applications. If you leave the storage prefix at "", all the data
* and files stored will be shared between the applications.
*/
public String storagePrefix = "db/assets";
public String storagePrefix = "app";

public String localStoragePrefix = "db/assets";

public boolean shouldEncodePreference = false;

/**
* Show download logs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ public class TeaFiles implements Files {
public MemoryFileStorage internalStorage;
public MemoryFileStorage classpathStorage;
public MemoryFileStorage localStorage;
public String storagePath;
public String localStoragePrefix;

public TeaFiles(TeaApplicationConfiguration config, TeaApplication teaApplication) {
this.internalStorage = new InternalStorage();
this.classpathStorage = new ClasspathStorage();
this.localStorage = new LocalDBStorage(teaApplication);
storagePath = config.storagePrefix;
localStoragePrefix = config.localStoragePrefix;
}

public FileDB getFileDB(FileType type) {
Expand Down Expand Up @@ -91,7 +91,7 @@ public boolean isExternalStorageAvailable() {

@Override
public String getLocalStoragePath() {
return storagePath;
return localStoragePrefix;
}

@Override
Expand Down
Loading

0 comments on commit 175619c

Please sign in to comment.