mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2024-11-22 04:27:59 +01:00
Allow for pony data to be loaded from off-thread processes
This commit is contained in:
parent
b3667ec3dc
commit
42cebcc1f6
5 changed files with 96 additions and 45 deletions
43
src/main/java/com/minelittlepony/client/pony/Memoize.java
Normal file
43
src/main/java/com/minelittlepony/client/pony/Memoize.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package com.minelittlepony.client.pony;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface Memoize<T> {
|
||||
T get();
|
||||
|
||||
default T get(T fallback) {
|
||||
T value = get();
|
||||
return value == null ? fallback : value;
|
||||
}
|
||||
|
||||
default boolean isPresent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static <T> Memoize<T> of(T value) {
|
||||
return () -> value;
|
||||
}
|
||||
|
||||
static <T> Memoize<T> load(Consumer<Consumer<T>> factory) {
|
||||
return new Memoize<>() {
|
||||
T value;
|
||||
boolean loadRequested;
|
||||
@Override
|
||||
public T get() {
|
||||
synchronized (this) {
|
||||
if (!loadRequested) {
|
||||
loadRequested = true;
|
||||
factory.accept(value -> {
|
||||
this.value = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return value != null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -37,11 +37,11 @@ import org.jetbrains.annotations.Unmodifiable;
|
|||
public class Pony implements IPony {
|
||||
|
||||
private final Identifier texture;
|
||||
private final IPonyData metadata;
|
||||
private final Memoize<IPonyData> metadata;
|
||||
|
||||
private boolean defaulted = false;
|
||||
|
||||
Pony(Identifier resource, IPonyData data) {
|
||||
Pony(Identifier resource, Memoize<IPonyData> data) {
|
||||
texture = resource;
|
||||
metadata = data;
|
||||
}
|
||||
|
@ -62,16 +62,20 @@ public class Pony implements IPony {
|
|||
|
||||
@Override
|
||||
public void updateForEntity(Entity entity) {
|
||||
if (!metadata.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity instanceof RegistrationHandler && ((RegistrationHandler)entity).shouldUpdateRegistration(this)) {
|
||||
entity.calculateDimensions();
|
||||
|
||||
PlayerEntity clientPlayer = MinecraftClient.getInstance().player;
|
||||
if (clientPlayer != null) {
|
||||
if (Objects.equals(entity, clientPlayer) || Objects.equals(((PlayerEntity)entity).getGameProfile(), clientPlayer.getGameProfile())) {
|
||||
Channel.broadcastPonyData(new MsgPonyData(metadata, defaulted));
|
||||
Channel.broadcastPonyData(new MsgPonyData(getMetadata(), defaulted));
|
||||
}
|
||||
}
|
||||
PonyDataCallback.EVENT.invoker().onPonyDataAvailable((PlayerEntity)entity, metadata, defaulted, EnvType.CLIENT);
|
||||
PonyDataCallback.EVENT.invoker().onPonyDataAvailable((PlayerEntity)entity, getMetadata(), defaulted, EnvType.CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +148,7 @@ public class Pony implements IPony {
|
|||
}
|
||||
|
||||
protected Vec3d getVisualEyePosition(LivingEntity entity) {
|
||||
Size size = entity.isBaby() ? Sizes.FOAL : metadata.getSize();
|
||||
Size size = entity.isBaby() ? Sizes.FOAL : getMetadata().getSize();
|
||||
|
||||
return new Vec3d(
|
||||
entity.getX(),
|
||||
|
@ -155,7 +159,7 @@ public class Pony implements IPony {
|
|||
|
||||
@Override
|
||||
public Race getRace(boolean ignorePony) {
|
||||
return getEffectiveRace(metadata.getRace(), ignorePony);
|
||||
return getEffectiveRace(getMetadata().getRace(), ignorePony);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,15 +169,12 @@ public class Pony implements IPony {
|
|||
|
||||
@Override
|
||||
public IPonyData getMetadata() {
|
||||
return metadata;
|
||||
return metadata.get(PonyData.NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSitting(LivingEntity entity) {
|
||||
return entity.hasVehicle()/*
|
||||
|| (entity instanceof PlayerEntity
|
||||
&& entity.getVelocity().x == 0 && entity.getVelocity().z == 0
|
||||
&& !entity.isInsideWaterOrBubbleColumn() && entity.onGround && isCrouching(entity))*/;
|
||||
return entity.hasVehicle();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,21 +34,22 @@ public class PonyData implements IPonyData {
|
|||
private static final PonyDataSerialiser SERIALISER = new PonyDataSerialiser();
|
||||
|
||||
public static final IPonyData NULL = new PonyData(Race.HUMAN);
|
||||
public static final Memoize<IPonyData> MEM_NULL = Memoize.of(NULL);
|
||||
|
||||
/**
|
||||
* Parses the given resource into a new IPonyData.
|
||||
* This may either come from an attached json file or the image itself.
|
||||
*/
|
||||
public static IPonyData parse(@Nullable Identifier identifier) {
|
||||
public static Memoize<IPonyData> parse(@Nullable Identifier identifier) {
|
||||
if (identifier == null) {
|
||||
return NULL;
|
||||
return MEM_NULL;
|
||||
}
|
||||
|
||||
try (Resource res = MinecraftClient.getInstance().getResourceManager().getResource(identifier)) {
|
||||
PonyData data = res.getMetadata(SERIALISER);
|
||||
|
||||
if (data != null) {
|
||||
return data;
|
||||
return Memoize.of(data);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// Ignore uploaded texture
|
||||
|
@ -56,12 +57,14 @@ public class PonyData implements IPonyData {
|
|||
MineLittlePony.logger.warn("Unable to read {} metadata", identifier, e);
|
||||
}
|
||||
|
||||
try {
|
||||
return NativeUtil.parseImage(identifier, NativePonyData::new);
|
||||
} catch (IllegalStateException e) {
|
||||
MineLittlePony.logger.fatal("Unable to read {} metadata", identifier, e);
|
||||
return NULL;
|
||||
}
|
||||
return Memoize.load(callback -> {
|
||||
NativeUtil.parseImage(identifier, img -> {
|
||||
callback.accept(new NativePonyData(img));
|
||||
}, e -> {
|
||||
MineLittlePony.logger.fatal("Unable to read {} metadata", identifier, e);
|
||||
callback.accept(NULL);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Expose
|
||||
|
|
|
@ -49,7 +49,7 @@ public class PonyManager implements IPonyManager, IdentifiableResourceReloadList
|
|||
try {
|
||||
return poniesCache.get(resource);
|
||||
} catch (ExecutionException e) {
|
||||
return new Pony(resource, PonyData.NULL);
|
||||
return new Pony(resource, Memoize.of(PonyData.NULL));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import com.mojang.blaze3d.systems.RenderSystem;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.mojang.blaze3d.platform.GlStateManager._getTexLevelParameter;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
@ -91,34 +91,38 @@ public class NativeUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static <T> T parseImage(Identifier resource, Function<NativeImage, T> consumer) {
|
||||
MinecraftClient mc = MinecraftClient.getInstance();
|
||||
TextureManager textures = mc.getTextureManager();
|
||||
public static void parseImage(Identifier resource, Consumer<NativeImage> consumer, Consumer<Exception> fail) {
|
||||
try {
|
||||
if (!RenderSystem.isOnRenderThread()) {
|
||||
RenderSystem.recordRenderCall(() -> parseImage(resource, consumer, fail));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!RenderSystem.isOnRenderThread()) {
|
||||
throw new IllegalStateException("This can only be called from the main thread.");
|
||||
}
|
||||
MinecraftClient mc = MinecraftClient.getInstance();
|
||||
TextureManager textures = mc.getTextureManager();
|
||||
|
||||
// recreate NativeImage from the GL matrix
|
||||
textures.bindTexture(resource);
|
||||
// recreate NativeImage from the GL matrix
|
||||
textures.bindTexture(resource);
|
||||
|
||||
// TODO: This returns values that are too specific.
|
||||
// Can we change the level (0) here to something
|
||||
// else to actually get what we need?
|
||||
int format = _getTexLevelParameter(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT);
|
||||
int width = _getTexLevelParameter(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH);
|
||||
int height = _getTexLevelParameter(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT);
|
||||
// TODO: This returns values that are too specific.
|
||||
// Can we change the level (0) here to something
|
||||
// else to actually get what we need?
|
||||
int format = _getTexLevelParameter(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT);
|
||||
int width = _getTexLevelParameter(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH);
|
||||
int height = _getTexLevelParameter(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT);
|
||||
|
||||
if (width * height == 0) {
|
||||
throw new IllegalStateException("GL texture not uploaded yet");
|
||||
}
|
||||
if (width * height == 0) {
|
||||
throw new IllegalStateException("GL texture not uploaded yet");
|
||||
}
|
||||
|
||||
try (NativeImage image = new NativeImage(InternalFormat.valueOf(format).getClassification(), width, height, false)) {
|
||||
// This allocates a new array to store the image every time.
|
||||
// Don't do this every time. Keep a cache and store it so we don't destroy memory.
|
||||
image.loadFromTextureImage(0, false);
|
||||
|
||||
return consumer.apply(image);
|
||||
try (NativeImage image = new NativeImage(InternalFormat.valueOf(format).getClassification(), width, height, false)) {
|
||||
// This allocates a new array to store the image every time.
|
||||
// Don't do this every time. Keep a cache and store it so we don't destroy memory.
|
||||
image.loadFromTextureImage(0, false);
|
||||
consumer.accept(image);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail.accept(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue