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