diff --git a/src/main/java/com/minelittlepony/client/pony/Memoize.java b/src/main/java/com/minelittlepony/client/pony/Memoize.java new file mode 100644 index 00000000..f8952e93 --- /dev/null +++ b/src/main/java/com/minelittlepony/client/pony/Memoize.java @@ -0,0 +1,43 @@ +package com.minelittlepony.client.pony; + +import java.util.function.Consumer; + +public interface Memoize { + T get(); + + default T get(T fallback) { + T value = get(); + return value == null ? fallback : value; + } + + default boolean isPresent() { + return true; + } + + static Memoize of(T value) { + return () -> value; + } + + static Memoize load(Consumer> 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; + } + }; + } +} diff --git a/src/main/java/com/minelittlepony/client/pony/Pony.java b/src/main/java/com/minelittlepony/client/pony/Pony.java index 6c699139..c3517276 100644 --- a/src/main/java/com/minelittlepony/client/pony/Pony.java +++ b/src/main/java/com/minelittlepony/client/pony/Pony.java @@ -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 metadata; private boolean defaulted = false; - Pony(Identifier resource, IPonyData data) { + Pony(Identifier resource, Memoize 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 diff --git a/src/main/java/com/minelittlepony/client/pony/PonyData.java b/src/main/java/com/minelittlepony/client/pony/PonyData.java index 7414af93..57bc2532 100644 --- a/src/main/java/com/minelittlepony/client/pony/PonyData.java +++ b/src/main/java/com/minelittlepony/client/pony/PonyData.java @@ -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 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 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 diff --git a/src/main/java/com/minelittlepony/client/pony/PonyManager.java b/src/main/java/com/minelittlepony/client/pony/PonyManager.java index 13f44012..a451f66d 100644 --- a/src/main/java/com/minelittlepony/client/pony/PonyManager.java +++ b/src/main/java/com/minelittlepony/client/pony/PonyManager.java @@ -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)); } } diff --git a/src/main/java/com/minelittlepony/client/util/render/NativeUtil.java b/src/main/java/com/minelittlepony/client/util/render/NativeUtil.java index fc1d0556..a627eb22 100644 --- a/src/main/java/com/minelittlepony/client/util/render/NativeUtil.java +++ b/src/main/java/com/minelittlepony/client/util/render/NativeUtil.java @@ -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 parseImage(Identifier resource, Function consumer) { - MinecraftClient mc = MinecraftClient.getInstance(); - TextureManager textures = mc.getTextureManager(); + public static void parseImage(Identifier resource, Consumer consumer, Consumer 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); } } }