From c1fbd889c013d9d009a7b13960357fefc827e4e2 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Sun, 2 May 2021 14:31:54 +0900 Subject: [PATCH] release 1.1 --- gradle.properties | 4 +- .../signpictureported/Box.java | 46 ++++ .../signpictureported/ImageWrapper.java | 42 +++ .../signpictureported/MayThrowFunction.java | 5 - .../signpictureported/NativeImageFactory.java | 32 +++ .../signpictureported/OutsideCache.java | 109 ++++++++ .../signpictureported/SignPicturePorted.java | 10 + .../api/DisplayConfigurationParseResult.java | 44 +++- .../internal/ErrorOrValid.java | 78 ++++++ .../{ => internal}/Functions.java | 6 +- .../InternalSpecialUtility.java | 2 +- .../internal/ThrowableRunnable.java | 5 + .../internal/ThrowableSupplier.java | 5 + .../internal/WrapExceptions.java | 45 ++++ .../signpictureported/mixin/BIRRMixin.java | 10 +- .../mixin/BlockInvalidateMixin.java | 35 +++ .../mixin/ChunkInvalidateMixin.java | 26 ++ .../mixin/SignBlockEntityRenderMixin.java | 242 ++++++------------ src/main/resources/fabric.mod.json | 2 +- .../resources/signpictureported.mixins.json | 7 +- 20 files changed, 563 insertions(+), 192 deletions(-) create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/Box.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/ImageWrapper.java delete mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/MayThrowFunction.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/NativeImageFactory.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/OutsideCache.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/internal/ErrorOrValid.java rename src/main/java/com/github/kisaragieffective/signpictureported/{ => internal}/Functions.java (83%) rename src/main/java/com/github/kisaragieffective/signpictureported/{ => internal}/InternalSpecialUtility.java (96%) create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableRunnable.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableSupplier.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/internal/WrapExceptions.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/mixin/BlockInvalidateMixin.java create mode 100644 src/main/java/com/github/kisaragieffective/signpictureported/mixin/ChunkInvalidateMixin.java diff --git a/gradle.properties b/gradle.properties index 7ae0eec..3f988ba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,8 +13,8 @@ org.gradle.jvmargs=-Xmx1G loom_version=0.5-SNAPSHOT # Mod Properties - mod_version = 1.0.5 - maven_group = net.fabricmc + mod_version = 1.1.0 + maven_group = com.github.kisaragieffective.signpictureported archives_base_name = SignPictureReloaded # Kotlin diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/Box.java b/src/main/java/com/github/kisaragieffective/signpictureported/Box.java new file mode 100644 index 0000000..9de89df --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/Box.java @@ -0,0 +1,46 @@ +package com.github.kisaragieffective.signpictureported; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Objects; +import java.util.function.Supplier; + +public class Box implements Closeable { + public final Supplier value; + private boolean needsInit = true; + // late-bind + private T cache = null; + public Box(Supplier value) { + this.value = value; + } + + public T getValue() { + if (needsInit) { + cache = value.get(); + needsInit = false; + } + return cache; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Box box = (Box) o; + return Objects.equals(cache, box.cache); + } + + @Override + public int hashCode() { + return Objects.hash(cache); + } + + @Override + public void close() throws IOException { + if (cache instanceof Closeable) { + ((Closeable) cache).close(); + needsInit = true; + cache = null; + } + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/ImageWrapper.java b/src/main/java/com/github/kisaragieffective/signpictureported/ImageWrapper.java new file mode 100644 index 0000000..21b20ea --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/ImageWrapper.java @@ -0,0 +1,42 @@ +package com.github.kisaragieffective.signpictureported; + +import net.minecraft.client.texture.NativeImageBackedTexture; + +import java.io.Closeable; +import java.lang.ref.SoftReference; +import java.util.Objects; + +public class ImageWrapper implements Closeable { + public final SoftReference nibt; + + public ImageWrapper(NativeImageBackedTexture nibt) { + this.nibt = new SoftReference<>(nibt); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImageWrapper imageWrapper = (ImageWrapper) o; + return Objects.equals(nibt, imageWrapper.nibt); + } + + @Override + public int hashCode() { + return Objects.hash(nibt); + } + + @Override + public String toString() { + return "ImageWrapper{" + + "nibt=" + nibt.get() + + '}'; + } + + @Override + public void close() { + NativeImageBackedTexture nibt = this.nibt.get(); + if (nibt != null) nibt.close(); + this.nibt.clear(); + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/MayThrowFunction.java b/src/main/java/com/github/kisaragieffective/signpictureported/MayThrowFunction.java deleted file mode 100644 index 3d5f05e..0000000 --- a/src/main/java/com/github/kisaragieffective/signpictureported/MayThrowFunction.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.kisaragieffective.signpictureported; - -public interface MayThrowFunction { - R apply(A a) throws X; -} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/NativeImageFactory.java b/src/main/java/com/github/kisaragieffective/signpictureported/NativeImageFactory.java new file mode 100644 index 0000000..ef1781e --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/NativeImageFactory.java @@ -0,0 +1,32 @@ +package com.github.kisaragieffective.signpictureported; + +import net.minecraft.client.texture.NativeImage; +import net.minecraft.client.texture.NativeImageBackedTexture; + +public class NativeImageFactory { + public static final Box errorImage = new Box<>(NativeImageFactory::getErrorImage); + public static NativeImageBackedTexture getErrorImage() { + final NativeImage nini = new NativeImage(128, 128, true); + for (int xv = 0; xv < 128; xv++) { + nini.setPixelColor(xv, 0, 0xFF_00_00_FF); + nini.setPixelColor(xv, 127, 0xFF_00_00_FF); + nini.setPixelColor(0, xv, 0xFF_00_00_FF); + nini.setPixelColor(127, xv, 0xFF_00_00_FF); + } + return new NativeImageBackedTexture(nini); + } + + public static final Box loadingImage = new Box<>(NativeImageFactory::getLoadingImage); + + public static NativeImageBackedTexture getLoadingImage() { + final NativeImage nini = new NativeImage(128, 128, true); + final int gray = 0x80808080; + for (int xv = 0; xv < 128; xv++) { + nini.setPixelColor(xv, 0, gray); + nini.setPixelColor(xv, 127, gray); + nini.setPixelColor(0, xv, gray); + nini.setPixelColor(127, xv, gray); + } + return new NativeImageBackedTexture(nini); + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/OutsideCache.java b/src/main/java/com/github/kisaragieffective/signpictureported/OutsideCache.java new file mode 100644 index 0000000..685bf55 --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/OutsideCache.java @@ -0,0 +1,109 @@ +package com.github.kisaragieffective.signpictureported; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.texture.NativeImageBackedTexture; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class OutsideCache { + private OutsideCache() { + + } + + public static final Map cache = new ConcurrentHashMap<>(); + public static final Map flippedCache = new ConcurrentHashMap<>(); + public static final Set invalidURL = new HashSet<>(); + + private static final Map identifierMap = new ConcurrentHashMap<>(); + private static final Map flippedIdentifierMap = new ConcurrentHashMap<>(); + public static final Set sbp = new HashSet<>(); + + public static ImageWrapper putOrCached(BlockPos pos, Supplier nibt) { + return putImage(cache, pos, nibt); + } + + public static ImageWrapper putFlippedOrCached(BlockPos pos, Supplier flipped) { + return putImage(flippedCache, pos, flipped); + } + + public static void put(BlockPos pos, NativeImageBackedTexture nibt, boolean flipped) { + putNewIdentifier(pos, nibt); + if (flipped) { + flippedCache.put(pos, new ImageWrapper(nibt)); + } else { + cache.put(pos, new ImageWrapper(nibt)); + } + } + + public static Optional get(BlockPos pos, boolean flipped) { + if (flipped) { + return flippedCache.containsKey(pos) ? Optional.of(flippedCache.get(pos)) : Optional.empty(); + } else { + return cache.containsKey(pos) ? Optional.of(cache.get(pos)) : Optional.empty(); + } + } + + public static Set locations() { + return identifierMap.keySet(); + } + + public static void drop(BlockPos pos) { + if (flippedCache.containsKey(pos)) { + final ImageWrapper nibt1 = flippedCache.get(pos); + nibt1.close(); + } + if (cache.containsKey(pos)) { + final ImageWrapper nibt = flippedCache.get(pos); + nibt.close(); + } + identifierMap.remove(pos); + flippedIdentifierMap.remove(pos); + } + + private static ImageWrapper putImage( + Map m, + BlockPos pos, + Supplier v + ) { + return m.computeIfAbsent(pos, ig -> { + SignPicturePorted.LOGGER.info("cache(" + pos + "): no hit, creating"); + return new ImageWrapper(v.get()); + }); + } + + public static Identifier putNewIdentifierOrCached(BlockPos pos, NativeImageBackedTexture nibt) { + return identifierMap.computeIfAbsent(pos, p -> computeNewIdentifier(p, nibt)); + } + + public static Identifier putNewIdentifier(BlockPos pos, NativeImageBackedTexture nibt) { + return identifierMap.put(pos, computeNewIdentifier(pos, nibt)); + } + + public static Identifier computeNewIdentifier(BlockPos pos, NativeImageBackedTexture nibt) { + Identifier newIdentifier = MinecraftClient.getInstance().getTextureManager().registerDynamicTexture("sgpc_reloaded", nibt); + SignPicturePorted.LOGGER.info("New identifier payed out (for " + pos + "): " + newIdentifier); + return newIdentifier; + } + + public static Identifier putFlippedNewIdentifierOrCached(BlockPos pos, NativeImageBackedTexture nibt) { + return flippedIdentifierMap.computeIfAbsent(pos, p -> computeNewIdentifier(p, nibt)); + } + + public static Identifier putFlippedNewIdentifier(BlockPos pos, NativeImageBackedTexture nibt) { + return flippedIdentifierMap.put(pos, computeNewIdentifier(pos, nibt)); + } + + public static void invalidate() { + OutsideCache.cache.values() + .forEach(x -> x.close()); + OutsideCache.cache.clear(); + OutsideCache.flippedCache.values() + .forEach(x -> x.close()); + OutsideCache.identifierMap.clear(); + OutsideCache.invalidURL.clear(); + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/SignPicturePorted.java b/src/main/java/com/github/kisaragieffective/signpictureported/SignPicturePorted.java index 720a069..22306cb 100644 --- a/src/main/java/com/github/kisaragieffective/signpictureported/SignPicturePorted.java +++ b/src/main/java/com/github/kisaragieffective/signpictureported/SignPicturePorted.java @@ -10,6 +10,16 @@ public class SignPicturePorted implements ModInitializer, ClientModInitializer, DedicatedServerModInitializer { public static final String MOD_ID = "signpictureported"; public static final Logger LOGGER = LogManager.getLogger(); + public static final boolean DEBUG = false; + + static { + if (DEBUG) { + LOGGER.info("Hint: This build is DEBUG build."); + LOGGER.debug("If you can see this message, it's ok."); + LOGGER.info("Or else: logger can't log debug message."); + } + } + @Override public void onInitialize() { LOGGER.info("SignPictureReloaded was loaded"); diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/api/DisplayConfigurationParseResult.java b/src/main/java/com/github/kisaragieffective/signpictureported/api/DisplayConfigurationParseResult.java index 6b66c51..ce5a56a 100644 --- a/src/main/java/com/github/kisaragieffective/signpictureported/api/DisplayConfigurationParseResult.java +++ b/src/main/java/com/github/kisaragieffective/signpictureported/api/DisplayConfigurationParseResult.java @@ -1,12 +1,18 @@ package com.github.kisaragieffective.signpictureported.api; +import it.unimi.dsi.fastutil.doubles.DoubleList; +import it.unimi.dsi.fastutil.doubles.DoubleLists; + +import java.util.Optional; import java.util.OptionalDouble; import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.DoubleStream; -import static com.github.kisaragieffective.signpictureported.Functions.compose; -import static com.github.kisaragieffective.signpictureported.Functions.switchBasedNullish; +import static com.github.kisaragieffective.signpictureported.internal.Functions.compose; +import static com.github.kisaragieffective.signpictureported.internal.Functions.switchBasedNullish; public final class DisplayConfigurationParseResult { public final double offsetRight; @@ -67,10 +73,13 @@ public DisplayConfigurationParseResult(OptionalDouble offsetRight, OptionalDoubl ); public static final String DOUBLE = "[-+]?(?:\\d+|\\d+\\.\\d+)"; + // see: https://github.com/Team-Fruit/SignPicture/blob/3a10ae618ed32415d524a76e3c90b60cb33af6b2/sources/universal/src/main/java/net/teamfruit/signpic/attr/prop/PropSyntax.java public static final String REGEX = "(?:(?(?" + DOUBLE + ")x(?" + DOUBLE + "))" + "|(?x(?" + DOUBLE + ")))?" + "(?X" + DOUBLE + ")?(?Y" + DOUBLE + ")?(?Z" + DOUBLE + ")?" + - "(?R" + DOUBLE + ")?(?U" + DOUBLE + ")?(?D" + DOUBLE + ")?"; + "(?:(?R" + DOUBLE + ")|(?L" + DOUBLE + "))?" + + "(?:(?U" + DOUBLE + ")|(?D" + DOUBLE + "))?" + + "((?F" + DOUBLE + ")|(?B" + DOUBLE + "))?"; public static final Pattern PATTERN = Pattern.compile(REGEX); /** @@ -80,8 +89,7 @@ public DisplayConfigurationParseResult(OptionalDouble offsetRight, OptionalDoubl * @return parse result */ public static DisplayConfigurationParseResult parse(String from) { - Matcher m = PATTERN - .matcher(from); + Matcher m = PATTERN.matcher(from); if (m.matches()) { Function extractor = groupName -> switchBasedNullish( m.group(groupName), @@ -103,12 +111,32 @@ public static DisplayConfigurationParseResult parse(String from) { final OptionalDouble rotateX = extractor.apply("x"); final OptionalDouble rotateY = extractor.apply("y"); final OptionalDouble rotateZ = extractor.apply("z"); - final OptionalDouble offsetRight = extractor.apply("r"); - final OptionalDouble offsetUp = extractor.apply("u"); - final OptionalDouble offsetDepth = extractor.apply("d"); + final OptionalDouble offsetRight = or(extractor.apply("right"), () -> + toStream(extractor.apply("left")) + .map(x -> -x) + .findFirst() + ); + final OptionalDouble offsetUp = or(extractor.apply("up"), () -> + toStream(extractor.apply("down")) + .map(x -> -x) + .findFirst() + ); + final OptionalDouble offsetDepth = or(extractor.apply("front"), () -> + toStream(extractor.apply("beside")) + .map(x -> -x) + .findFirst() + ); return new DisplayConfigurationParseResult(offsetRight, offsetUp, offsetDepth, rotateX, rotateY, rotateZ, scaleX, scaleY); } else { return DisplayConfigurationParseResult.DEFAULT; } } + + private static OptionalDouble or(OptionalDouble od1, Supplier od2) { + return od1.isPresent() ? od1 : od2.get(); + } + + private static DoubleStream toStream(OptionalDouble od) { + return od.isPresent() ? DoubleStream.of(od.getAsDouble()) : DoubleStream.empty(); + } } diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/internal/ErrorOrValid.java b/src/main/java/com/github/kisaragieffective/signpictureported/internal/ErrorOrValid.java new file mode 100644 index 0000000..edb5891 --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/internal/ErrorOrValid.java @@ -0,0 +1,78 @@ +package com.github.kisaragieffective.signpictureported.internal; + +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public interface ErrorOrValid { + boolean isError(); + + boolean isValid(); + + Optional error(); + + Optional value(); + + static Error error(E e) { + return new Error<>(e); + } + + static Valid valid(V v) { + return new Valid<>(v); + } + + final class Error implements ErrorOrValid { + private final E error; + + public Error(@NotNull E error) { + this.error = error; + } + @Override + public boolean isError() { + return true; + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public Optional error() { + return Optional.of(error); + } + + @Override + public Optional value() { + return Optional.empty(); + } + } + + final class Valid implements ErrorOrValid { + private final V value; + + public Valid(V value) { + this.value = value; + } + + @Override + public boolean isError() { + return false; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public Optional error() { + return Optional.empty(); + } + + @Override + public Optional value() { + return Optional.of(value); + } + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/Functions.java b/src/main/java/com/github/kisaragieffective/signpictureported/internal/Functions.java similarity index 83% rename from src/main/java/com/github/kisaragieffective/signpictureported/Functions.java rename to src/main/java/com/github/kisaragieffective/signpictureported/internal/Functions.java index f31f8df..eca6bb0 100644 --- a/src/main/java/com/github/kisaragieffective/signpictureported/Functions.java +++ b/src/main/java/com/github/kisaragieffective/signpictureported/internal/Functions.java @@ -1,4 +1,4 @@ -package com.github.kisaragieffective.signpictureported; +package com.github.kisaragieffective.signpictureported.internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,10 +21,10 @@ public static R switchBasedNullish( public static Function compose( Function ab, Function bc ) { - return a -> bc.apply(ab.apply(a)); + return ab.andThen(bc); } - public static BiFunction _1( + public static BiFunction _1( Function ar ) { return (a, o) -> ar.apply(a); diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/InternalSpecialUtility.java b/src/main/java/com/github/kisaragieffective/signpictureported/internal/InternalSpecialUtility.java similarity index 96% rename from src/main/java/com/github/kisaragieffective/signpictureported/InternalSpecialUtility.java rename to src/main/java/com/github/kisaragieffective/signpictureported/internal/InternalSpecialUtility.java index 7f8cad8..f5784c6 100644 --- a/src/main/java/com/github/kisaragieffective/signpictureported/InternalSpecialUtility.java +++ b/src/main/java/com/github/kisaragieffective/signpictureported/internal/InternalSpecialUtility.java @@ -1,4 +1,4 @@ -package com.github.kisaragieffective.signpictureported; +package com.github.kisaragieffective.signpictureported.internal; import net.minecraft.block.entity.SignBlockEntity; import net.minecraft.nbt.CompoundTag; diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableRunnable.java b/src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableRunnable.java new file mode 100644 index 0000000..0926d66 --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableRunnable.java @@ -0,0 +1,5 @@ +package com.github.kisaragieffective.signpictureported.internal; + +public interface ThrowableRunnable { + void run() throws X; +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableSupplier.java b/src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableSupplier.java new file mode 100644 index 0000000..5aae4a6 --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/internal/ThrowableSupplier.java @@ -0,0 +1,5 @@ +package com.github.kisaragieffective.signpictureported.internal; + +public interface ThrowableSupplier { + A get() throws X; +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/internal/WrapExceptions.java b/src/main/java/com/github/kisaragieffective/signpictureported/internal/WrapExceptions.java new file mode 100644 index 0000000..a116284 --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/internal/WrapExceptions.java @@ -0,0 +1,45 @@ +package com.github.kisaragieffective.signpictureported.internal; + +import org.jetbrains.annotations.Contract; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.function.Supplier; + +import static com.github.kisaragieffective.signpictureported.internal.InternalSpecialUtility.never; + +public final class WrapExceptions { + public static Supplier wrapExceptionToUnchecked(ThrowableSupplier sa) { + return () -> { + try { + return sa.get(); + } catch (Throwable e) { + handleThrowable(e); + return never(); + } + }; + } + + public static Runnable wrapExceptionToUnchecked(ThrowableRunnable tr) { + return () -> { + try { + tr.run(); + } catch (Throwable e) { + handleThrowable(e); + } + }; + } + + @Contract("_->fail") + private static void handleThrowable(Throwable e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } else if (e instanceof Error) { + throw (Error) e; + } else if (e instanceof IOException) { + throw new UncheckedIOException((IOException) e); + } else { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/mixin/BIRRMixin.java b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/BIRRMixin.java index cb2d6be..6e9848b 100644 --- a/src/main/java/com/github/kisaragieffective/signpictureported/mixin/BIRRMixin.java +++ b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/BIRRMixin.java @@ -65,11 +65,11 @@ private void initializerTail(CallbackInfo ci) { BuiltinItemRendererRegistry.DynamicItemRenderer defaultRenderer = defRenderer.get(sign); BuiltinItemRendererRegistry.DynamicItemRenderer customRenderer = getRenderer(defaultRenderer); SignPicturePorted.LOGGER.info("Overriding renderer: for:" + sign + ",render class:" + defaultRenderer); - // defRenderer.remove(sign); - this.register( - Objects.requireNonNull(sign, "sign"), - Objects.requireNonNull(customRenderer, "customRender") - ); + // defRenderer.remove(sign); + this.register( + Objects.requireNonNull(sign, "sign"), + Objects.requireNonNull(customRenderer, "customRender") + ); }); RENDERERS = defRenderer; SignPicturePorted.LOGGER.info("Done"); diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/mixin/BlockInvalidateMixin.java b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/BlockInvalidateMixin.java new file mode 100644 index 0000000..b8e1a58 --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/BlockInvalidateMixin.java @@ -0,0 +1,35 @@ +package com.github.kisaragieffective.signpictureported.mixin; + +import com.github.kisaragieffective.signpictureported.NativeImageFactory; +import com.github.kisaragieffective.signpictureported.OutsideCache; +import com.github.kisaragieffective.signpictureported.ImageWrapper; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.texture.NativeImageBackedTexture; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +@Mixin(World.class) +public class BlockInvalidateMixin { + @Inject(method = "markDirty", at = @At("HEAD")) + public void onBlockInvalidate(BlockPos pos, BlockEntity blockEntity, CallbackInfo ci) { + final ImageWrapper r2 = OutsideCache.cache.get(pos); + if (r2 == null) return; + final NativeImageBackedTexture nibt = r2.nibt.get(); + final Set<@NotNull ? extends NativeImageBackedTexture> excludedSet = new HashSet<>(Arrays.asList( + NativeImageFactory.errorImage.getValue(), + NativeImageFactory.loadingImage.getValue() + )); + if (nibt != null && excludedSet.stream().noneMatch(x -> x.equals(nibt))) { + OutsideCache.drop(pos); + } + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/mixin/ChunkInvalidateMixin.java b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/ChunkInvalidateMixin.java new file mode 100644 index 0000000..a0c9511 --- /dev/null +++ b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/ChunkInvalidateMixin.java @@ -0,0 +1,26 @@ +package com.github.kisaragieffective.signpictureported.mixin; + +import com.github.kisaragieffective.signpictureported.OutsideCache; +import net.minecraft.client.world.ClientChunkManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientChunkManager.class) +public class ChunkInvalidateMixin { + @Inject(method = "unload", at = @At("HEAD")) + public void onChunkUnloaded(int chunkX, int chunkZ, CallbackInfo ci) { + final int lowerX = chunkX * 16; + final int upperX = lowerX + 15; + final int lowerZ = chunkZ * 16; + final int upperZ = lowerZ + 15; + OutsideCache.locations() + .stream() + .filter(pos -> lowerX <= pos.getX() + && pos.getX() <= upperX + && lowerZ <= pos.getZ() + && pos.getZ() <= upperZ) + .forEach(OutsideCache::drop); + } +} diff --git a/src/main/java/com/github/kisaragieffective/signpictureported/mixin/SignBlockEntityRenderMixin.java b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/SignBlockEntityRenderMixin.java index b815f4d..b30c266 100644 --- a/src/main/java/com/github/kisaragieffective/signpictureported/mixin/SignBlockEntityRenderMixin.java +++ b/src/main/java/com/github/kisaragieffective/signpictureported/mixin/SignBlockEntityRenderMixin.java @@ -1,7 +1,13 @@ package com.github.kisaragieffective.signpictureported.mixin; -import com.github.kisaragieffective.signpictureported.*; +import com.github.kisaragieffective.signpictureported.ImageWrapper; +import com.github.kisaragieffective.signpictureported.NativeImageFactory; +import com.github.kisaragieffective.signpictureported.OutsideCache; +import com.github.kisaragieffective.signpictureported.SignPicturePorted; +import com.github.kisaragieffective.signpictureported.TextureFlipper; import com.github.kisaragieffective.signpictureported.api.DisplayConfigurationParseResult; +import com.github.kisaragieffective.signpictureported.internal.ErrorOrValid; +import com.github.kisaragieffective.signpictureported.internal.InternalSpecialUtility; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -26,11 +32,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.lang.ref.SoftReference; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -39,19 +40,14 @@ import java.util.concurrent.ExecutionException; import java.util.function.Supplier; -import static com.github.kisaragieffective.signpictureported.InternalSpecialUtility.implicitCast; -import static com.github.kisaragieffective.signpictureported.InternalSpecialUtility.never; +import static com.github.kisaragieffective.signpictureported.internal.InternalSpecialUtility.never; +import static com.github.kisaragieffective.signpictureported.internal.WrapExceptions.wrapExceptionToUnchecked; @Mixin(SignBlockEntityRenderer.class) public class SignBlockEntityRenderMixin { // TODO out-memory cache - private final Map> correspond = new HashMap<>(); private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - private final Set invalidURL = new HashSet<>(); - private final Set badURLs = new HashSet<>(); - private final Map> flippedCache = new HashMap<>(); private static final float EPS_F = 0.0001F; - private static final double EPS_D = 0.0000001; @Contract("_ -> fail") private static void notifyUnknownBlock(BlockPos pos) { SignPicturePorted.LOGGER.warn("Unknown sign-like block: " + pos); @@ -72,13 +68,23 @@ private static float getSignRotation(SignBlockEntity sbe) { return rotateAngle; } else { notifyUnknownBlock(sbe.getPos()); - return dummy(); + return never(); } } - private static void selectTexture(NativeImageBackedTexture nibt) { + private static void selectTexture(BlockPos pos, NativeImageBackedTexture nibt) { if (nibt == null) return; - Identifier id = newIdentifierOrCached(nibt); + Identifier id = OutsideCache.putNewIdentifierOrCached(pos, nibt); + selectTexturePure(id); + } + + private static void selectFlippedTexture(BlockPos pos, NativeImageBackedTexture nibt) { + if (nibt == null) return; + Identifier id = OutsideCache.putFlippedNewIdentifierOrCached(pos, nibt); + selectTexturePure(id); + } + + private static void selectTexturePure(Identifier id) { Objects.requireNonNull( CLIENT.getTextureManager().getTexture(id), "TextureManager.getTexture" ).bindTexture(); @@ -129,16 +135,15 @@ private static void fixMatrices(MatrixStack matrices, SignBlockEntity signBlockE private void injectRender(SignBlockEntity signBlockEntity, float f, MatrixStack matrices, VertexConsumerProvider vertexConsumerProvider, int i, int j, CallbackInfo defModelRender) { - final List x = InternalSpecialUtility.getPlaintextLines(signBlockEntity); - final String specs = String.join("", x); + final String specs = String.join("", InternalSpecialUtility.getPlaintextLines(signBlockEntity)); final boolean isURLSpec = specs.startsWith("#$"); final boolean isTextureSpec = specs.startsWith("!"); + final BlockPos pos = signBlockEntity.getPos(); if (!isURLSpec && !isTextureSpec) { return; } if (!isTarget(signBlockEntity)) { - final BlockPos pos = signBlockEntity.getPos(); notifyUnknownBlock(pos); return; } @@ -150,7 +155,7 @@ private void injectRender(SignBlockEntity signBlockEntity, float f, int confBegin = specs.indexOf("{"); // SignPicturePorted.LOGGER.debug("conf start:" + confBegin); String urlFlag = confBegin >= 0 ? specs.substring(2, confBegin) : specs.substring(2); - if (invalidURL.contains(urlFlag)) { + if (OutsideCache.invalidURL.contains(urlFlag)) { return; } @@ -160,27 +165,37 @@ private void injectRender(SignBlockEntity signBlockEntity, float f, final String url = urlFlag.startsWith("http://") || urlFlag.startsWith("https://") ? urlFlag : "https://" + urlFlag; - SignPicturePorted.LOGGER.info(url); + // SignPicturePorted.LOGGER.info(url); final Optional urlOpt = parseURL(url); // empty implies parsing was failed - if (urlOpt.isEmpty()) return; + if (!urlOpt.isPresent()) return; final URL url2 = urlOpt.get(); - - NativeImageBackedTexture nibt = null; - try { - nibt = CompletableFuture.supplyAsync(() -> url2) - .handle((r, l) -> registerFrom(r)) - .get(); - } catch (InterruptedException | ExecutionException | NoSuchElementException e) { - SignPicturePorted.LOGGER.catching(e); + final NativeImageBackedTexture nibt; + final NativeImage ni; + { + final Supplier fetch = () -> fetchFrom(url2, OptionalLong.empty()) + .value() + .map(NativeImageBackedTexture::new) + .orElseGet(NativeImageFactory.errorImage::getValue); + if (!OutsideCache.sbp.contains(pos)) { + // unsafeRunAsyncAndForget + CompletableFuture.supplyAsync(fetch) + .thenAccept(x -> OutsideCache.put(pos, x, false)); + OutsideCache.sbp.add(pos); + } + final Supplier loadSupplier = NativeImageFactory.loadingImage::getValue; + final ImageWrapper cacheEntry = OutsideCache.putOrCached(pos, loadSupplier); + final Optional cache = Optional.ofNullable(cacheEntry.nibt.get()); + nibt = cache.orElseGet(loadSupplier); + ni = nibt.getImage(); + // SignPicturePorted.LOGGER.info("back(pos: " + pos + "): " + ni); + Objects.requireNonNull(ni); + selectTexture(pos, nibt); } - - if (nibt == null) return; - selectTexture(nibt); // NOTE テクスチャ向いてるほうがZ- int lightLevel = signBlockEntity.getWorld().getLightLevel(LightType.BLOCK, signBlockEntity.getPos()); - SignPicturePorted.LOGGER.info("light: " + lightLevel + ", pos: " + signBlockEntity.getPos()); + // SignPicturePorted.LOGGER.info("light: " + lightLevel + ", pos: " + signBlockEntity.getPos()); matrices.push(); // color(1, 0, 1); // @@ -196,7 +211,7 @@ private void injectRender(SignBlockEntity signBlockEntity, float f, double offsetRight = pr.offsetRight; double offsetDepth = pr.offsetDepth; // against Z-fighting - matrices.translate(offsetRight, offsetUp, offsetDepth + EPS_D); + matrices.translate(offsetRight, offsetUp, offsetDepth + EPS_F); float scaleX = (float) pr.scaleX; float scaleY = (float) pr.scaleY; matrices.scale(scaleX, scaleY, 1.0F); @@ -204,7 +219,16 @@ private void injectRender(SignBlockEntity signBlockEntity, float f, drawImage(matrices); // 左右反転したテクスチャを裏側に表示させる - selectTexture(new NativeImageBackedTexture(TextureFlipper.flipHorizontal(nibt.getImage()))); + { + Supplier ff = + () -> new NativeImageBackedTexture(TextureFlipper.flipHorizontal(ni)); + ImageWrapper cache2 = OutsideCache.putFlippedOrCached(pos, ff); + final NativeImageBackedTexture nibt2; + final Optional cache12 = Optional.ofNullable(cache2.nibt.get()); + nibt2 = cache12.orElseGet(ff); + selectFlippedTexture(pos, nibt2); + } + matrices.translate(scaleX, 0.0, 0.0); matrices.multiply(new Quaternion(0.0F, 180.0F, 0.0F, true)); drawImage(matrices); @@ -233,136 +257,26 @@ private static Optional parseURL(String fragment) { } } - private static final Map identifierMap = new HashMap<>(); - private static Identifier newIdentifierOrCached(NativeImageBackedTexture nibt) { - if (identifierMap.containsKey(nibt)) { - return identifierMap.get(nibt); - } - - Identifier newIdentifier = CLIENT.getTextureManager().registerDynamicTexture("sgpc_reloaded", nibt); - identifierMap.put(nibt, newIdentifier); - return newIdentifier; - } - - private void invalidate() { - correspond.values() - .stream() - .map(SoftReference::get) - .filter(Objects::nonNull) - .forEach(NativeImageBackedTexture::close); - correspond.clear(); - flippedCache.values() - .stream() - .map(SoftReference::get) - .filter(Objects::nonNull) - .forEach(NativeImageBackedTexture::close); - flippedCache.clear(); - identifierMap.keySet().forEach(x -> x.close()); - identifierMap.clear(); - invalidURL.clear(); - } - - private NativeImageBackedTexture registerFrom(URL url) { - if (correspond.containsKey(url) && correspond.get(url).get() != null) { - return correspond.get(url).get(); - } - - // If we don't do that, the game will be frozen - if (badURLs.contains(url.toString())) { - return null; - } - /* - System.out.println(flippedCache.entrySet().parallelStream() - .map(x -> x.getKey() + ": " + x.getValue()) - .collect(Collectors.joining(", ", "[", "]"))); - */ - NativeImageBackedTexture returning; - { - InputStream httpBuffer; - try { - HttpURLConnection huc1; - if (url.getHost() == null || url.getProtocol() == null) return null; - huc1 = implicitCast(url.openConnection()); - huc1.setInstanceFollowRedirects(true); - huc1.connect(); - final int code = huc1.getResponseCode(); - if (code == 200) { - // OK: Do nothing - } else if (code == 204) { - throw new IOException("Nothing can't be rendered (HTTP 204)"); - } else { - throw new IOException("URL connection failed: {code: " + code + "}"); - } - - /* - Check response MIME type, these types will be accepted: - * image/png - * image/jpg - * image/xml+svg - */ - String mimeType = huc1.getContentType(); - - if (!mimeType.startsWith("image/")) { - throw new IOException("Content-Type " + mimeType + " is not image"); - } - httpBuffer = huc1.getInputStream(); - } catch (IllegalArgumentException | IOException e) { - SignPicturePorted.LOGGER.error("Exception caught", e); - badURLs.add(url.toString()); - return null; - } - - NativeImage mayImage; - InputStream bufferedHttpIS = new BufferedInputStream(httpBuffer); - try { - mayImage = NativeImage.read(bufferedHttpIS); - } catch (IOException | UnsupportedOperationException e) { - badURLs.add(url.toString()); - SignPicturePorted.LOGGER.error("During get NativeImage", e); - return null; - } - - returning = new NativeImageBackedTexture(mayImage); - correspond.put(url, new SoftReference<>(returning)); - } - - return returning; - } - - private static final HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .followRedirects(HttpClient.Redirect.ALWAYS) - // .authenticator(Authenticator.getDefault()) - .build(); - private static Optional completeSuccessfulOrNone(A a, MayThrowFunction arx) { - return completeSuccessfulOrNone(a, arx, Tag.of()); - } - - private static Optional completeSuccessfulOrNone(A a, MayThrowFunction arx, Tag tx) { + private static ErrorOrValid fetchFrom(URL url, OptionalLong ifModifiedSince) { + SignPicturePorted.LOGGER.info("fetchFrom: " + url); try { - return Optional.of(arx.apply(a)); - } catch (Throwable t) { - if (tx.isAcceptable(t)) { - return Optional.empty(); - } else { - throw new RuntimeException(t); - } + //noinspection Convert2MethodRef + return CompletableFuture.supplyAsync(wrapExceptionToUnchecked(() -> url.openConnection())) + .thenApplyAsync(x -> (HttpURLConnection) x) + .thenApplyAsync(x -> { + x.setInstanceFollowRedirects(true); + ifModifiedSince.ifPresent(x::setIfModifiedSince); + wrapExceptionToUnchecked(x::connect).run(); + return x; + }) + .thenApplyAsync(x -> wrapExceptionToUnchecked(x::getInputStream).get()) + .thenApplyAsync(x -> wrapExceptionToUnchecked(() -> NativeImage.read(x)).get()) + .>handleAsync((res, ex) -> + res != null ? ErrorOrValid.valid(res) : ErrorOrValid.error(ex.toString())) + .get(); + } catch (InterruptedException | ExecutionException e) { + return ErrorOrValid.error(e.getMessage()); } } - - private Optional getContent(URL url) { - final Optional xxx = completeSuccessfulOrNone(url, URL::toURI); - if (xxx.isEmpty()) return Optional.empty(); - - HttpRequest req = HttpRequest.newBuilder() - .uri(xxx.get()) - .GET() - .build(); - - return client.sendAsync(req, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(HttpResponse::body) - .thenApply(x -> completeSuccessfulOrNone(x, NativeImage::read)) - .join(); - } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 3bd9dcc..de09bba 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "signpictutureported", + "id": "signpictureported", "version": "${version}", "name": "SignPicture Ported For Fabric", diff --git a/src/main/resources/signpictureported.mixins.json b/src/main/resources/signpictureported.mixins.json index 3c5ab7c..2913063 100644 --- a/src/main/resources/signpictureported.mixins.json +++ b/src/main/resources/signpictureported.mixins.json @@ -3,9 +3,10 @@ "package": "com.github.kisaragieffective.signpictureported.mixin", "compatibilityLevel": "JAVA_8", "mixins": [ - "BlockEntityMixin", - "SignBlockEntityRenderMixin", - "BIRRMixin" + "BIRRMixin", + "BlockInvalidateMixin", + "ChunkInvalidateMixin", + "SignBlockEntityRenderMixin" ], "client": [ ],