Skip to content

Commit

Permalink
Merge branch 'main' of github.com:SpiritGameStudios/Snapper
Browse files Browse the repository at this point in the history
  • Loading branch information
CallMeEchoCodes committed Sep 9, 2024
2 parents ccb69aa + b92b84b commit 8b8a9c5
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 10 deletions.
7 changes: 6 additions & 1 deletion src/client/java/dev/spiritstudios/snapper/Snapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ public void onInitializeClient() {
client.player.sendMessage(Text.translatable("text.snapper.panorama_success", SCREENSHOT_MENU_KEY.getBoundKeyLocalizedText()), true);
}
while (RECENT_SCREENSHOT_KEY.wasPressed()) {
File latestScreenshot = ScreenshotActions.getScreenshots(client).getFirst();
List<File> screenshots = ScreenshotActions.getScreenshots(client);
if (screenshots.size() == 0) {
if (client.player != null) client.player.sendMessage(Text.translatable("text.snapper.screenshot_failure_open"), true);
continue;
}
File latestScreenshot = screenshots.getFirst();

client.setScreen(new ScreenshotViewerScreen(
ScreenshotImage.of(latestScreenshot, client.getTextureManager()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,17 @@ protected PanoramaViewerScreen(String title, Screen parent) {
super(Text.translatable("menu.snapper.viewermenu"));
this.title = title;
this.parent = parent;
this.load();
}

private void load() {
List<File> panorama = this.loadPanorama();
if (panorama == null || client == null) return;
if (panorama == null) return;

panorama.parallelStream().map(face -> {
ScreenshotImage icon = ScreenshotImage.forPanoramaFace(this.client.getTextureManager(), face.getName());
this.loadIcon(icon, face.getName(), Path.of(face.getPath()));
return icon;
}).forEach(ScreenshotImage::joinLoad);
}).toList().forEach(ScreenshotImage::joinLoad); // MUST be joined & called on render thread!

this.loaded = true;
}
Expand All @@ -74,8 +73,6 @@ private void loadIcon(ScreenshotImage icon, String fileName, Path filePath) {

@Nullable
private List<File> loadPanorama() {
if (client == null) return null;

File panoramaDir = new File(this.client.runDirectory, "screenshots/panorama");
List<File> panoramaFaces;
if (!Files.exists(panoramaDir.toPath())) return null;
Expand Down Expand Up @@ -103,22 +100,21 @@ private List<File> loadPanorama() {

@Override
public void close() {
if (client == null) return;
client.setScreen(this.parent);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
@Override
protected void init() {
if (client == null) return;

if (client == null) throw new RuntimeException("Attempted loading panorama screen without client set.");
File panoramaDirectory = new File(client.runDirectory, "screenshots/panorama");
addDrawableChild(ButtonWidget.builder(Text.translatable("button.snapper.folder"), button -> {
if (!panoramaDirectory.exists()) new File(String.valueOf(panoramaDirectory)).mkdirs();
Util.getOperatingSystem().open(panoramaDirectory);
}).dimensions(width / 2 - 150 - 4, height - 32, 150, 20).build());

addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, button -> this.close()).dimensions(width / 2 + 4, height - 32, 150, 20).build());
this.load();
}

@Override
Expand Down
20 changes: 20 additions & 0 deletions src/client/java/dev/spiritstudios/snapper/mixin/CameraMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dev.spiritstudios.snapper.mixin;

import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.entity.Entity;
import net.minecraft.world.BlockView;
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(Camera.class)
public class CameraMixin {
@Inject(method = "update", at = @At("HEAD"), cancellable = true)
private void blockUpdateDuringPanoRender(BlockView area, Entity focusedEntity, boolean thirdPerson, boolean inverseView, float tickDelta, CallbackInfo ci) {
if (MinecraftClient.getInstance().gameRenderer.isRenderingPanorama() && thirdPerson) {
ci.cancel();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Share;
import com.llamalad7.mixinextras.sugar.ref.LocalFloatRef;
import dev.spiritstudios.snapper.mixin.accessor.CameraAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.RunArgs;
import net.minecraft.client.gl.Framebuffer;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.option.GameOptions;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
Expand All @@ -25,6 +31,14 @@ public class MinecraftClientMixin {
@Shadow
public static boolean IS_SYSTEM_MAC;

@Final
@Shadow
public GameOptions options;

@Final
@Shadow
public GameRenderer gameRenderer;

@WrapOperation(
method = "takePanorama",
at = @At(
Expand All @@ -46,4 +60,28 @@ private void init(RunArgs args, CallbackInfo ci) {
}
}

@WrapOperation(
method = "takePanorama",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;setYaw(F)V")
)
private void captureSetYaw(ClientPlayerEntity player, float value, Operation<Void> op, @Share("yaw")LocalFloatRef yaw) {
if (!this.options.getPerspective().isFirstPerson()) {
yaw.set(value);
} else {
op.call(player, value);
}
}

@WrapOperation(
method = "takePanorama",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;setPitch(F)V")
)
private void applyThirdPersonCameraRotation(ClientPlayerEntity player, float value, Operation<Void> op, @Share("yaw")LocalFloatRef yaw) {
if (!this.options.getPerspective().isFirstPerson()) {
((CameraAccessor)this.gameRenderer.getCamera()).invokeSetRotation(yaw.get(), value);
} else {
op.call(player, value);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dev.spiritstudios.snapper.mixin;

import dev.spiritstudios.snapper.mixinsupport.ImageTransferable;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.util.ScreenshotRecorder;
import net.minecraft.text.Text;
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 javax.imageio.ImageIO;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.io.File;
import java.io.IOException;
import java.util.function.Consumer;

@Mixin(ScreenshotRecorder.class)
public class ScreenshotRecorderMixin {
/**
* @author hama
* @reason check if pano file exists before writing to it
*/
@Inject(
method = "method_1661",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/texture/NativeImage;writeTo(Ljava/io/File;)V")
)
private static void lookBeforeYouLeap(NativeImage nativeImage, File screenshotFile, Consumer<Text> messageReceiver, CallbackInfo ci) throws IOException {
screenshotFile.getParentFile().mkdirs();
screenshotFile.createNewFile();
}

@Inject(
method = "method_1661",
at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;literal(Ljava/lang/String;)Lnet/minecraft/text/MutableText;", shift = At.Shift.AFTER)
)
private static void saveWrittenFileToClipboard(NativeImage nativeImage, File screenshotFile, Consumer<Text> messageReceiver, CallbackInfo ci) throws IOException {
if (!screenshotFile.getAbsolutePath().contains("/panorama/")) {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(new ImageTransferable(ImageIO.read(screenshotFile)), null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.spiritstudios.snapper.mixin.accessor;

import net.minecraft.client.render.Camera;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;

@Mixin(Camera.class)
public interface CameraAccessor {
@Invoker
void invokeSetRotation(float yaw, float pitch);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package dev.spiritstudios.snapper.mixinsupport;

import org.jetbrains.annotations.NotNull;

import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

public class ImageTransferable implements Transferable {
private static final DataFlavor[] flavors = {DataFlavor.imageFlavor};

private final Image image;

public ImageTransferable(Image im) {
this.image = im;
}

@Override
public DataFlavor[] getTransferDataFlavors() {
return flavors;
}

@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(DataFlavor.imageFlavor);
}

@NotNull
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (!isDataFlavorSupported(flavor)) throw new UnsupportedFlavorException(flavor);
return image;
}
}
1 change: 1 addition & 0 deletions src/client/resources/assets/snapper/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"text.snapper.rename_invalid": "New name for screenshot invalid",
"text.snapper.rename_invalid_png": "Name must end with '.png'",
"text.snapper.screenshot_instructions": "%s. View by pressing %s",
"text.snapper.screenshot_failure_open": "Take a screenshot in order to view it",
"panorama.snapper.failure": "Couldn't save panorama: %s",
"panorama.snapper.success": "Saved screenshot as %s"
}
5 changes: 4 additions & 1 deletion src/client/resources/snapper.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
"package": "dev.spiritstudios.snapper.mixin",
"compatibilityLevel": "JAVA_21",
"client": [
"accessor.CameraAccessor",
"CameraMixin",
"GameMenuMixin",
"KeyboardMixin",
"MinecraftClientMixin",
"TitleScreenMixin"
"TitleScreenMixin",
"ScreenshotRecorderMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down

0 comments on commit 8b8a9c5

Please sign in to comment.