Added decorators for AssetLoader functions

AssetLoader functions will now notify passed Promise of exceptions
Changed DcParams#combineFrames from boolean to int [-1 (auto), 0 (don't combine), 1 (combine)]
This commit is contained in:
Collin Smith
2021-12-05 18:54:43 -08:00
parent 8dec04360d
commit 9dfe541319
12 changed files with 150 additions and 60 deletions

View File

@ -2,18 +2,93 @@ package com.riiablo.asset;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
public abstract class AssetLoader<T> {
protected Array<AssetDesc> dependencies(AssetDesc<T> asset) {
public final Array<AssetDesc> dependencies(
Promise<T> promise,
AssetDesc<T> asset
) {
try {
return dependencies0(asset);
} catch (Throwable t) {
promise.setFailure(t);
throw t;
}
}
public final void validate(
Promise<T> promise,
AssetDesc<T> asset
) {
try {
validate0(asset);
} catch (Throwable t) {
promise.setFailure(t);
throw t;
}
}
public final <F extends FileHandle> Future<?> ioAsync(
Promise<T> promise,
EventExecutor executor,
AssetManager assets,
AssetDesc<T> asset,
F handle,
Adapter<F> adapter
) {
try {
return ioAsync0(executor, assets, asset, handle, adapter);
} catch (Throwable t) {
promise.setFailure(t);
throw t;
}
}
public final <F extends FileHandle> T loadAsync(
Promise<T> promise,
AssetManager assets,
AssetDesc<T> asset,
F handle,
Object data
) {
try {
return loadAsync0(assets, asset, handle, data);
} catch (Throwable t) {
promise.setFailure(t);
throw t;
}
}
public final T loadSync(
Promise<T> promise,
AssetManager assets,
AssetDesc<T> asset,
T object
) {
try {
return loadSync0(assets, asset, object);
} catch (Throwable t) {
promise.setFailure(t);
throw t;
}
}
protected Array<AssetDesc> dependencies0(
AssetDesc<T> asset
) {
return EmptyArray.empty();
}
protected void validate(AssetDesc<T> asset) {}
protected void validate0(
AssetDesc<T> asset
) {
}
protected <F extends FileHandle> Future<?> ioAsync(
protected <F extends FileHandle> Future<?> ioAsync0(
EventExecutor executor,
AssetManager assets,
AssetDesc<T> asset,
@ -23,7 +98,7 @@ public abstract class AssetLoader<T> {
return executor.newSucceededFuture(null);
}
protected <F extends FileHandle> T loadAsync(
protected <F extends FileHandle> T loadAsync0(
AssetManager assets,
AssetDesc<T> asset,
F handle,
@ -32,7 +107,7 @@ public abstract class AssetLoader<T> {
return null;
}
protected T loadSync(
protected T loadSync0(
AssetManager assets,
AssetDesc<T> asset,
T object

View File

@ -142,7 +142,7 @@ public final class AssetManager implements Disposable {
<T> AssetContainer[] loadDependencies(Promise<T> promise, AssetDesc<T> asset) {
final AssetLoader loader = findLoader(asset.type);
@SuppressWarnings("unchecked") // guaranteed by loader contract
Array<AssetDesc> dependencies = loader.dependencies(asset);
Array<AssetDesc> dependencies = loader.dependencies(promise, asset);
final int numDependencies = dependencies.size;
final AssetContainer[] containers = numDependencies > 0
? new AssetContainer[dependencies.size]
@ -169,10 +169,10 @@ public final class AssetManager implements Disposable {
final FileHandle handle = resolve(asset); // TODO: refactor AssetLoader#resolver?
final Adapter adapter = findAdapter(handle);
loader
.ioAsync(executor, AssetManager.this, asset, handle, adapter)
.ioAsync(promise, executor, AssetManager.this, asset, handle, adapter)
.addListener((FutureListener) future -> {
@SuppressWarnings("unchecked") // guaranteed by loader contract
T object = (T) loader.loadAsync(AssetManager.this, asset, handle, future.getNow());
T object = (T) loader.loadAsync(promise, AssetManager.this, asset, handle, future.getNow());
boolean inserted = syncQueue.offer(SyncMessage.wrap(container, promise, loader, object));
if (!inserted) log.error("Failed to enqueue {}", asset);
});
@ -185,15 +185,22 @@ public final class AssetManager implements Disposable {
if (container0 != null) return container0.retain();
final Promise<T> promise = sync.newPromise();
promise.setUncancellable();
promise.addListener(future -> {
final Throwable cause = future.cause();
if (cause != null) {
log.warn("Failed to load asset {}", asset, cause);
}
});
// FIXME: loadDependencies may throw an exception to propagate to caller
final AssetContainer[] dependencies = loadDependencies(promise, asset);
final AssetContainer container = AssetContainer.wrap(asset, promise, dependencies);
loadedAssets.put(asset, container);
if (promise.isDone()) return container; // one or more dependencies was invalid
try {
findLoader(asset.type).validate(asset);
} catch (Throwable cause) {
promise.setFailure(cause);
findLoader(asset.type).validate(promise, asset);
} catch (Throwable t) {
return container;
}
@ -241,7 +248,10 @@ public final class AssetManager implements Disposable {
start = end;
msg = syncQueue.poll(timeoutRemaining, TimeUnit.MILLISECONDS);
if (msg == null) break;
msg.loadSync(this);
try {
msg.loadSync(this);
} catch (Throwable ignored) {
}
end = System.currentTimeMillis();
timeoutRemaining -= (end - start);
}

View File

@ -33,8 +33,8 @@ final class SyncMessage<T> {
@SuppressWarnings("unchecked") // guaranteed by loader contract
Future<?> loadSync(AssetManager assets) {
loader.loadSync(assets, container.asset, object);
promise.setSuccess(object);
loader.loadSync(promise, assets, container.asset, object);
promise.trySuccess(object);
return promise;
}

View File

@ -35,7 +35,7 @@ public class Dc6Loader extends AssetLoader<Dc6> {
};
@Override
public Array<AssetDesc> dependencies(AssetDesc<Dc6> asset) {
public Array<AssetDesc> dependencies0(AssetDesc<Dc6> asset) {
DcParams params = asset.params(DcParams.class);
if (params.direction < 0) return EmptyArray.empty();
AssetDesc<Dc6> header = AssetDesc.of(asset, PARENT_DC);
@ -45,14 +45,14 @@ public class Dc6Loader extends AssetLoader<Dc6> {
}
@Override
protected <F extends FileHandle> Future<?> ioAsync(
protected <F extends FileHandle> Future<?> ioAsync0(
EventExecutor executor,
AssetManager assets,
AssetDesc<Dc6> asset,
F handle,
Adapter<F> adapter
) {
log.traceEntry("ioAsync(executor: {}, asset: {}, handle: {}, adapter: {})", executor, asset, handle, adapter);
log.traceEntry("ioAsync0(executor: {}, asset: {}, handle: {}, adapter: {})", executor, asset, handle, adapter);
DcParams params = asset.params(DcParams.class);
if (params.direction >= 0) {
Dc6 dc6 = assets.getDepNow(AssetDesc.of(asset, PARENT_DC));
@ -65,13 +65,13 @@ public class Dc6Loader extends AssetLoader<Dc6> {
}
@Override
protected <F extends FileHandle> Dc6 loadAsync(
protected <F extends FileHandle> Dc6 loadAsync0(
AssetManager assets,
AssetDesc<Dc6> asset,
F handle,
Object data
) {
log.traceEntry("loadAsync(assets: {}, asset: {}, handle: {}, data: {})", assets, asset, handle, data);
log.traceEntry("loadAsync0(assets: {}, asset: {}, handle: {}, data: {})", assets, asset, handle, data);
DcParams params = asset.params(DcParams.class);
if (params.direction >= 0) {
boolean released = ReferenceCountUtil.release(handle); // dcc already owns a reference
@ -100,8 +100,8 @@ public class Dc6Loader extends AssetLoader<Dc6> {
}
@Override
protected Dc6 loadSync(AssetManager assets, AssetDesc<Dc6> asset, Dc6 dc6) {
log.traceEntry("loadSync(assets: {}, asset: {}, dcc: {})", assets, asset, dc6);
protected Dc6 loadSync0(AssetManager assets, AssetDesc<Dc6> asset, Dc6 dc6) {
log.traceEntry("loadSync0(assets: {}, asset: {}, dcc: {})", assets, asset, dc6);
DcParams params = asset.params(DcParams.class);
if (params.direction < 0) return dc6;
dc6.uploadTextures(params.direction, params.combineFrames);

View File

@ -36,7 +36,7 @@ public class DccLoader extends AssetLoader<Dcc> {
};
@Override
protected Array<AssetDesc> dependencies(AssetDesc<Dcc> asset) {
protected Array<AssetDesc> dependencies0(AssetDesc<Dcc> asset) {
DcParams params = asset.params(DcParams.class);
if (params.direction < 0) return EmptyArray.empty();
AssetDesc<Dcc> header = AssetDesc.of(asset, PARENT_DC);
@ -46,21 +46,21 @@ public class DccLoader extends AssetLoader<Dcc> {
}
@Override
protected void validate(AssetDesc<Dcc> asset) {
protected void validate0(AssetDesc<Dcc> asset) {
DcParams params = asset.params(DcParams.class);
if (params.combineFrames) throw new InvalidParams(asset,
"Dcc does not support DcParams#combineFrames=true");
if (params.combineFrames == 1) throw new InvalidParams(asset,
"Dcc does not support DcParams#combineFrames=1");
}
@Override
protected <F extends FileHandle> Future<?> ioAsync(
protected <F extends FileHandle> Future<?> ioAsync0(
EventExecutor executor,
AssetManager assets,
AssetDesc<Dcc> asset,
F handle,
Adapter<F> adapter
) {
log.traceEntry("ioAsync(executor: {}, asset: {}, handle: {}, adapter: {})", executor, asset, handle, adapter);
log.traceEntry("ioAsync0(executor: {}, asset: {}, handle: {}, adapter: {})", executor, asset, handle, adapter);
DcParams params = asset.params(DcParams.class);
if (params.direction >= 0) {
Dcc dcc = assets.getDepNow(AssetDesc.of(asset, PARENT_DC));
@ -73,13 +73,13 @@ public class DccLoader extends AssetLoader<Dcc> {
}
@Override
protected <F extends FileHandle> Dcc loadAsync(
protected <F extends FileHandle> Dcc loadAsync0(
AssetManager assets,
AssetDesc<Dcc> asset,
F handle,
Object data
) {
log.traceEntry("loadAsync(assets: {}, asset: {}, handle: {}, data: {})", assets, asset, handle, data);
log.traceEntry("loadAsync0(assets: {}, asset: {}, handle: {}, data: {})", assets, asset, handle, data);
DcParams params = asset.params(DcParams.class);
if (params.direction >= 0) {
boolean released = ReferenceCountUtil.release(handle); // dcc already owns a reference
@ -108,8 +108,8 @@ public class DccLoader extends AssetLoader<Dcc> {
}
@Override
protected Dcc loadSync(AssetManager assets, AssetDesc<Dcc> asset, Dcc dcc) {
log.traceEntry("loadSync(assets: {}, asset: {}, dcc: {})", assets, asset, dcc);
protected Dcc loadSync0(AssetManager assets, AssetDesc<Dcc> asset, Dcc dcc) {
log.traceEntry("loadSync0(assets: {}, asset: {}, dcc: {})", assets, asset, dcc);
DcParams params = asset.params(DcParams.class);
if (params.direction < 0) return dcc;
dcc.uploadTextures(params.direction, params.combineFrames);

View File

@ -14,7 +14,7 @@ import com.riiablo.asset.AssetManager;
public class MusicLoader extends AssetLoader<Music> {
@Override
protected <F extends FileHandle> Future<?> ioAsync(
protected <F extends FileHandle> Future<?> ioAsync0(
EventExecutor executor,
AssetManager assets,
AssetDesc<Music> asset,
@ -25,7 +25,7 @@ public class MusicLoader extends AssetLoader<Music> {
}
@Override
protected <F extends FileHandle> Music loadAsync(
protected <F extends FileHandle> Music loadAsync0(
AssetManager assets,
AssetDesc<Music> asset,
F handle,
@ -35,7 +35,7 @@ public class MusicLoader extends AssetLoader<Music> {
}
@Override
protected Music loadSync(
protected Music loadSync0(
AssetManager assets,
AssetDesc<Music> asset,
Music music

View File

@ -6,7 +6,6 @@ import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.riiablo.asset.Adapter;
import com.riiablo.asset.AssetDesc;
@ -20,25 +19,20 @@ public class PaletteLoader extends AssetLoader<Palette> {
private static final Logger log = LogManager.getLogger(PaletteLoader.class);
@Override
public Array<AssetDesc> dependencies(AssetDesc<Palette> asset) {
return super.dependencies(asset);
}
@Override
protected <F extends FileHandle> Future<?> ioAsync(
protected <F extends FileHandle> Future<?> ioAsync0(
EventExecutor executor,
AssetManager assets,
AssetDesc<Palette> asset,
F handle,
Adapter<F> adapter
) {
log.traceEntry("ioAsync(executor: {}, assets: {}, asset: {}, handle: {}, adapter: {})", executor, assets, asset, handle, adapter);
log.traceEntry("ioAsync0(executor: {}, assets: {}, asset: {}, handle: {}, adapter: {})", executor, assets, asset, handle, adapter);
return adapter.buffer(executor, handle, 0, (int) handle.length());
}
@Override
protected <F extends FileHandle> Palette loadAsync(AssetManager assets, AssetDesc<Palette> asset, F handle, Object data) {
log.traceEntry("loadAsync(assets: {}, asset: {}, handle: {}, data: {})", assets, asset, handle, data);
protected <F extends FileHandle> Palette loadAsync0(AssetManager assets, AssetDesc<Palette> asset, F handle, Object data) {
log.traceEntry("loadAsync0(assets: {}, asset: {}, handle: {}, data: {})", assets, asset, handle, data);
assert data instanceof ByteBuf;
ByteBuf buffer = (ByteBuf) data; // borrowed, don't release
try {
@ -49,8 +43,8 @@ public class PaletteLoader extends AssetLoader<Palette> {
}
@Override
protected Palette loadSync(AssetManager assets, AssetDesc<Palette> asset, Palette palette) {
log.traceEntry("loadSync(assets: {}, asset: {}, object: {})", assets, asset, palette);
protected Palette loadSync0(AssetManager assets, AssetDesc<Palette> asset, Palette palette) {
log.traceEntry("loadSync0(assets: {}, asset: {}, object: {})", assets, asset, palette);
return palette;
}
}

View File

@ -7,42 +7,46 @@ import com.riiablo.mpq_bytebuf.Mpq;
public class DcParams extends MpqParams<Dc> {
public static DcParams of(int direction) {
return of(direction, false);
return of(direction, -1);
}
public static DcParams of(int direction, boolean combineFrames) {
public static DcParams of(int direction, int combineFrames) {
return new DcParams(direction, combineFrames);
}
public static DcParams of(short locale, int direction, boolean combineFrames) {
public static DcParams of(short locale, int direction, int combineFrames) {
return new DcParams(locale, direction, combineFrames);
}
public static DcParams of(short locale, short platform, int direction, boolean combineFrames) {
public static DcParams of(short locale, short platform, int direction, int combineFrames) {
return new DcParams(locale, platform, direction, combineFrames);
}
public final int direction;
public final boolean combineFrames;
public final int combineFrames;
protected DcParams(int direction, boolean combineFrames) {
protected DcParams(int direction, int combineFrames) {
super();
this.direction = direction;
this.combineFrames = combineFrames;
}
protected DcParams(short locale, int direction, boolean combineFrames) {
protected DcParams(short locale, int direction, int combineFrames) {
super(locale);
this.direction = direction;
this.combineFrames = combineFrames;
}
protected DcParams(short locale, short platform, int direction, boolean combineFrames) {
protected DcParams(short locale, short platform, int direction, int combineFrames) {
super(locale, platform);
this.direction = direction;
this.combineFrames = combineFrames;
}
public DcParams copy(int direction) {
return of(locale, platform, direction, combineFrames);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -57,7 +61,7 @@ public class DcParams extends MpqParams<Dc> {
public int hashCode() {
int result = super.hashCode();
result = 31 * result + direction;
result = 31 * result + (combineFrames ? 1 : 0);
result = 31 * result + combineFrames;
return result;
}

View File

@ -84,7 +84,11 @@ public abstract class Dc<D extends Dc.Direction>
return directions[d];
}
public void uploadTextures(int d, boolean combineFrames) {}
public boolean loaded(int d) {
return directions[d] != null;
}
public void uploadTextures(int d, int combineFrames) {}
public BBox box() {
return todo();

View File

@ -116,12 +116,12 @@ public class Dc6 extends Dc<Dc6.Dc6Direction> {
}
@Override
public void uploadTextures(int d, boolean combineFrames) {
public void uploadTextures(int d, int combineFrames) {
final Dc6Direction direction = directions[d];
final Dc6Frame[] frame = direction.frames;
final Pixmap[] pixmap = direction.pixmap;
final Texture[] texture = direction.texture;
if (!combineFrames) {
if (combineFrames == 0 || (combineFrames == -1 && frame[0].width < Dc6.PAGE_SIZE)) {
for (int f = 0; f < numFrames; f++) {
Texture t = texture[f] = new Texture(pixmap[f]);
frame[f].texture.setRegion(t);

View File

@ -113,8 +113,8 @@ public final class Dcc extends Dc<Dcc.DccDirection> {
}
@Override
public void uploadTextures(int d, boolean combineFrames) {
if (combineFrames) throw new UnsupportedOperationException("DCC do not support combined frames");
public void uploadTextures(int d, int combineFrames) {
if (combineFrames == 1) throw new UnsupportedOperationException("DCC do not support combined frames");
final DccDirection direction = directions[d];
final DccFrame[] frame = direction.frames;
final Pixmap[] pixmap = direction.pixmap;

View File

@ -14,10 +14,12 @@ import com.badlogic.gdx.files.FileHandle;
import com.riiablo.RiiabloTest;
import com.riiablo.asset.adapter.GdxFileHandleAdapter;
import com.riiablo.asset.adapter.MpqFileHandleAdapter;
import com.riiablo.asset.loader.Dc6Loader;
import com.riiablo.asset.loader.DccLoader;
import com.riiablo.asset.param.DcParams;
import com.riiablo.asset.resolver.GdxFileHandleResolver;
import com.riiablo.file.Dc;
import com.riiablo.file.Dc6;
import com.riiablo.file.Dcc;
import com.riiablo.logger.Level;
import com.riiablo.logger.LogManager;
@ -121,6 +123,7 @@ public class AssetManagerTest extends RiiabloTest {
.adapter(FileHandle.class, new GdxFileHandleAdapter())
.adapter(MpqFileHandle.class, new MpqFileHandleAdapter())
.loader(Dcc.class, new DccLoader())
.loader(Dc6.class, new Dc6Loader())
;
}