PictureSign 1.0.0 - Initial release!

This commit is contained in:
Motschen
2021-10-03 10:00:56 +02:00
parent 3c6f119cfb
commit bc212c9471
18 changed files with 775 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
package eu.midnightdust.picturesign;
import eu.midnightdust.picturesign.config.PictureSignConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.util.Identifier;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.Hashtable;
import java.util.concurrent.ExecutorService;
import static java.util.concurrent.Executors.newFixedThreadPool;
public class PictureDownloader {
public static class PictureData {
public String url;
public Identifier identifier;
public PictureData(String url) {
this.url = url;
}
}
static PictureDownloader downloader = new PictureDownloader();
public static PictureDownloader getInstance() {
return downloader;
}
// Create a service for downloading the picture
private final ExecutorService service = newFixedThreadPool(PictureSignConfig.maxThreads);
private final Hashtable<String, PictureData> cache = new Hashtable<>();
private final Object mutex = new Object();
// Downloads the picture, or returns the cached picture
public PictureData getPicture(String url) {
synchronized (mutex) {
// Try to get the picture from cache
PictureData data = this.cache.get(url);
if (data == null) {
// Download the picture if not in cache
this.downloadPicture(url);
return null;
}
if (data.identifier == null) {
return null;
}
return data;
}
}
// Download the image and save it in cache
private void downloadPicture(String url) {
if (PictureSignConfig.debug) PictureSignClient.LOGGER.info("Started downloading picture: " + url);
this.cache.put(url, new PictureData(url));
service.submit(() -> {
try {
BufferedInputStream in = new BufferedInputStream(new URL(url).openStream());
File file = File.createTempFile(".picturesign", "temp");
file.deleteOnExit();
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
out.write(dataBuffer, 0, bytesRead);
}
out.close();
// Convert to png
BufferedImage bufferedImage = ImageIO.read(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
NativeImage nativeImage = NativeImage.read(inputStream);
NativeImageBackedTexture nativeImageBackedTexture = new NativeImageBackedTexture(nativeImage);
Identifier texture = MinecraftClient.getInstance().getTextureManager().registerDynamicTexture("picturesign/image",
nativeImageBackedTexture);
// Cache the downloaded picture
synchronized (mutex) {
PictureData data = this.cache.get(url);
data.identifier = texture;
}
if (PictureSignConfig.debug) PictureSignClient.LOGGER.info("Finished downloading picture: " + url);
} catch (IOException error) {
PictureSignClient.LOGGER.error("Error downloading picture: " + error);
}
});
}
}

View File

@@ -0,0 +1,14 @@
package eu.midnightdust.picturesign;
import eu.midnightdust.picturesign.config.PictureSignConfig;
import net.fabricmc.api.ClientModInitializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class PictureSignClient implements ClientModInitializer {
public static Logger LOGGER = LogManager.getLogger("PictureSign");
@Override
public void onInitializeClient() {
PictureSignConfig.init("picturesign", PictureSignConfig.class);
}
}

View File

@@ -0,0 +1,9 @@
package eu.midnightdust.picturesign.config;
import eu.midnightdust.lib.config.MidnightConfig;
public class PictureSignConfig extends MidnightConfig {
@Entry public static boolean enabled = true;
@Entry public static boolean debug = false;
@Entry(min = 1, max = 10) public static int maxThreads = 4;
}

View File

@@ -0,0 +1,30 @@
package eu.midnightdust.picturesign.mixin;
import eu.midnightdust.picturesign.config.PictureSignConfig;
import eu.midnightdust.picturesign.render.PictureSignRenderer;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.client.render.*;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.block.entity.SignBlockEntityRenderer;
import net.minecraft.client.util.math.MatrixStack;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SignBlockEntityRenderer.class)
public abstract class MixinSignBlockEntityRenderer implements BlockEntityRenderer<SignBlockEntity> {
PictureSignRenderer psRenderer = new PictureSignRenderer();
@Inject(at = @At("HEAD"), method = "render")
public void ps$onRender(SignBlockEntity signBlockEntity, float f, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, CallbackInfo ci) {
if (PictureSignConfig.enabled && signBlockEntity.getTextOnRow(0,false).getString().matches("(!PS:.*)")) {
psRenderer.render(signBlockEntity, matrixStack, light, overlay);
}
}
@Inject(at = @At("HEAD"), method = "shouldRender", cancellable = true)
private static void shouldRender(SignBlockEntity sign, int signColor, CallbackInfoReturnable<Boolean> cir) {
if (PictureSignConfig.enabled && sign.getTextOnRow(0,false).getString().matches("(!PS:.*)")) cir.setReturnValue(true);
}
}

View File

@@ -0,0 +1,120 @@
package eu.midnightdust.picturesign.render;
import com.mojang.blaze3d.systems.RenderSystem;
import eu.midnightdust.picturesign.PictureDownloader;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Quaternion;
import net.minecraft.util.math.Vec3f;
import java.util.Arrays;
import java.util.List;
public class PictureSignRenderer {
public void render(SignBlockEntity signBlockEntity, MatrixStack matrixStack, int light, int overlay) {
String text = signBlockEntity.getTextOnRow(0, false).getString() +
signBlockEntity.getTextOnRow(1, false).getString() +
signBlockEntity.getTextOnRow(2, false).getString();
String url = text.replaceAll("!PS:", "").replaceAll(" ","");
if (url.contains("imgur:")) url = url.replace("imgur:", "https://i.imgur.com/");
if (url.contains("imgbb:")) url = url.replace("imgbb:", "https://i.ibb.co/");
if (!url.contains("https://") && !url.contains("http://")) {
url = "https://" + url;
}
if (!url.contains(".png") && !url.contains(".jpg") && !url.contains(".jpeg")) return;
String lastLine = signBlockEntity.getTextOnRow(3, false).getString();
if (!lastLine.matches("(.*\\d:.*\\d:.*\\d:.*\\d:.*\\d)")) return;
List<String> scale = Arrays.stream(lastLine.split(":")).toList();
float width = 0;
float height = 0;
float x = 0;
float y = 0;
float z = 0;
try {
width = Float.parseFloat(scale.get(0));
height = Float.parseFloat(scale.get(1));
x = Float.parseFloat(scale.get(2));
y = Float.parseFloat(scale.get(3));
z = Float.parseFloat(scale.get(4));
}
catch (NumberFormatException ignored) {}
// Download the picture data
PictureDownloader.PictureData data = PictureDownloader.getInstance().getPicture(url);
if (data == null || data.identifier == null) {
return;
}
float xOffset = 0.0F;
float zOffset = 0.0F;
Quaternion yRotation = Vec3f.POSITIVE_Y.getDegreesQuaternion(0F);
if (signBlockEntity.getCachedState().contains(Properties.HORIZONTAL_FACING)) {
Direction direction = signBlockEntity.getCachedState().get(Properties.HORIZONTAL_FACING);
switch (direction) {
case NORTH -> {
zOffset = 1.01F;
xOffset = 1.0F;
yRotation = Vec3f.POSITIVE_Y.getDegreesQuaternion(180.0F);
}
case SOUTH -> zOffset = 0.010F;
case EAST -> {
zOffset = 1.01F;
yRotation = Vec3f.POSITIVE_Y.getDegreesQuaternion(90.0F);
}
case WEST -> {
yRotation = Vec3f.POSITIVE_Y.getDegreesQuaternion(-90.0F);
xOffset = 1.01F;
}
}
}
else if (signBlockEntity.getCachedState().contains(Properties.ROTATION)) {
yRotation = Vec3f.POSITIVE_Y.getDegreesQuaternion(signBlockEntity.getCachedState().get(Properties.ROTATION) * -22.5f);
}
else return;
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
matrixStack.push();
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, data.identifier);
RenderSystem.disableBlend();
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
matrixStack.translate(xOffset + x, 0.00F + y, zOffset + z);
matrixStack.multiply(yRotation);
Matrix4f matrix4f = matrixStack.peek().getModel();
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
buffer.vertex(matrix4f, width, 0.0F, 1.0F).texture(1.0F, 1.0F).color(255, 255, 255, 255)
.light(light).overlay(overlay).next();
buffer.vertex(matrix4f, width, height, 1.0F).texture(1.0F, 0.0F).color(255, 255, 255, 255)
.light(light).overlay(overlay).next();
buffer.vertex(matrix4f, 0.0F, height, 1.0F).texture(0.0F, 0.0F).color(255, 255, 255, 255)
.light(light).overlay(overlay).next();
buffer.vertex(matrix4f, 0.0F, 0.0F, 1.0F).texture(0.0F, 1.0F).color(255, 255, 255, 255)
.light(light).overlay(overlay).next();
tessellator.draw();
matrixStack.pop();
RenderSystem.disableDepthTest();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,7 @@
{
"picturesign.midnightconfig.title":"PictureSign Config",
"picturesign.midnightconfig.enabled":"Enabled",
"picturesign.midnightconfig.debug":"Send debug info",
"picturesign.midnightconfig.maxThreads":"Max download threads"
}

View File

@@ -0,0 +1,34 @@
{
"schemaVersion": 1,
"id": "picturesign",
"version": "${version}",
"name": "PictureSign",
"description": "Use signs to display custom images completely client-side!",
"authors": [
"Motschen",
"TeamMidnightDust"
],
"contact": {
"homepage": "https://www.midnightdust.eu/",
"sources": "https://github.com/TeamMidnightDust/PictureSign",
"issues": "https://github.com/TeamMidnightDust/PictureSign/issues"
},
"license": "MIT",
"icon": "assets/picturesign/icon.png",
"environment": "client",
"entrypoints": {
"client": [
"eu.midnightdust.picturesign.PictureSignClient"
]
},
"depends": {
"midnightlib": "*"
},
"mixins": [
"picturesign.mixins.json"
]
}

View File

@@ -0,0 +1,11 @@
{
"required": true,
"package": "eu.midnightdust.picturesign.mixin",
"compatibilityLevel": "JAVA_16",
"client": [
"MixinSignBlockEntityRenderer"
],
"injectors": {
"defaultRequire": 1
}
}