diff --git a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java index 2296e035..7cc413c3 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java @@ -350,18 +350,18 @@ public boolean checkInit() { // return String.format("Mouse Cursor: " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorX + " " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorY); // })); TODO Remove? - title = "trajectories"; - y += 14; - if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); - lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { - if (Minecraft.getMinecraft().currentScreen == this) return "Trajectories"; - String message = "Invalid Item"; - Vec3d vec = TrajectoriesCalculator.calculate(); - if (vec != null) { - message = String.format("%.3f %.3f %.3f", vec.x, vec.y, vec.z); - } - return String.format("Trajectories: " + message); - })); +// title = "trajectories"; +// y += 14; +// if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); +// lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { +// if (Minecraft.getMinecraft().currentScreen == this) return "Trajectories"; +// String message = "Invalid Item"; +// Vec3d vec = TrajectoriesCalculator.calculate(); +// if (vec != null) { +// message = String.format("%.3f %.3f %.3f", vec.x, vec.y, vec.z); +// } +// return String.format("Trajectories: " + message); +// })); title = "velocity"; y += 14; diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java index 47e983e9..92bcb00b 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java @@ -63,7 +63,7 @@ public void onDoneLoadingWorld() { LOGGER.debug(LoggerMarkers.Event, "Finished loading the world on the client"); loadingScreenDelay = 1; - TASmodClient.virtual.unpress(); + TASmodClient.virtual.clear(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java index ee4263b0..7fff25e5 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java @@ -1,27 +1,33 @@ package com.minecrafttas.tasmod.mixin.playbackhooks; +import org.apache.commons.lang3.tuple.Triple; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.sugar.ref.LocalFloatRef; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; import com.minecrafttas.tasmod.virtual.VirtualInput; + import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.renderer.EntityRenderer; import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.math.MathHelper; -import org.apache.commons.lang3.tuple.Triple; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** * Redirects the camera to use {@link VirtualInput.VirtualCameraAngleInput}.
- * Also conforms the camera to 20tps as + * To support handling the camera in TASes and to avoid desyncs via lag,
+ * it was decided to only update the camera every tick.
+ *
+ * To achieve this, some parts of the vanilla code were disabled, but get called every tick in {@link #runUpdate(float)} + * + * @author Scribble, Pancake */ @Mixin(EntityRenderer.class) public class MixinEntityRenderer implements SubtickDuck { @@ -45,7 +51,7 @@ public void playback_injectAtStartSection(float partialTicks, long nanoTime, Cal float f = this.mc.gameSettings.mouseSensitivity * 0.6F + 0.2F; float f1 = f * f * f * 8.0F; - if (this.mc.currentScreen == null) { // No Gui + if (this.mc.currentScreen == null && !TASmodClient.controller.isPlayingback() && mc.player != null) { // No Gui mc.mouseHelper.mouseXYChange(); mc.getTutorial().handleMouse(mc.mouseHelper); TASmodClient.virtual.CAMERA_ANGLE.updateNextCameraAngle((float) -(mc.mouseHelper.deltaY * f1 * 0.15D), (float) (mc.mouseHelper.deltaX * f1 * 0.15D)); @@ -64,11 +70,19 @@ public void runUpdate(float partialTicks) { if(mc.player == null){ return; } + TASmodClient.virtual.CAMERA_ANGLE.nextCameraTick(); + float prevPitch = mc.player.rotationPitch; float prevYaw = mc.player.rotationYaw; - TASmodClient.virtual.CAMERA_ANGLE.nextCameraTick(); - mc.player.rotationPitch = TASmodClient.virtual.CAMERA_ANGLE.getCurrentPitch(); - mc.player.rotationYaw = TASmodClient.virtual.CAMERA_ANGLE.getCurrentYaw(); + Float newPitch = TASmodClient.virtual.CAMERA_ANGLE.getCurrentPitch(); + Float newYaw = TASmodClient.virtual.CAMERA_ANGLE.getCurrentYaw(); + + if(newPitch == null) { + TASmodClient.virtual.CAMERA_ANGLE.setCamera(prevPitch, prevYaw); + return; + } + mc.player.rotationPitch = newPitch; + mc.player.rotationYaw = newYaw; mc.player.prevRotationPitch = prevPitch; mc.player.prevRotationYaw = prevYaw; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index b734f08f..64247e25 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -232,7 +232,7 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { return verbose ? TextFormatting.GREEN + "Pausing a recording" : ""; case NONE: LOGGER.debug(LoggerMarkers.Playback, "Stopping a recording"); - TASmodClient.virtual.unpress(); + TASmodClient.virtual.clear(); state = TASstate.NONE; return verbose ? TextFormatting.GREEN + "Stopping the recording" : ""; } @@ -246,12 +246,12 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { LOGGER.debug(LoggerMarkers.Playback, "Pausing a playback"); state = TASstate.PAUSED; tempPause = TASstate.PLAYBACK; - TASmodClient.virtual.unpress(); + TASmodClient.virtual.clear(); return verbose ? TextFormatting.GREEN + "Pausing a playback" : ""; case NONE: LOGGER.debug(LoggerMarkers.Playback, "Stopping a playback"); Minecraft.getMinecraft().gameSettings.chatLinks = true; - TASmodClient.virtual.unpress(); + TASmodClient.virtual.clear(); state = TASstate.NONE; return verbose ? TextFormatting.GREEN + "Stopping the playback" : ""; } diff --git a/src/main/java/com/minecrafttas/tasmod/util/Ducks.java b/src/main/java/com/minecrafttas/tasmod/util/Ducks.java index 5b785a6a..533affea 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/Ducks.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Ducks.java @@ -81,6 +81,10 @@ public static interface GuiScreenDuck { * Quacks the subtick */ public static interface SubtickDuck { + /** + * Custom updating method for EntityRenderer, updating the player rotation + * @param partialTicks The partial ticks from the vanilla Minecraft timer + */ void runUpdate(float partialTicks); } diff --git a/src/main/java/com/minecrafttas/tasmod/util/FileThread.java b/src/main/java/com/minecrafttas/tasmod/util/FileThread.java index cdf61425..759c5af1 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/FileThread.java +++ b/src/main/java/com/minecrafttas/tasmod/util/FileThread.java @@ -9,12 +9,17 @@ import java.util.ArrayList; import java.util.List; +/** + * Thread for writing files to disc + * + * @author Pancake + */ public class FileThread extends Thread { - private PrintWriter stream; + private final PrintWriter stream; private boolean end = false; - private List output = new ArrayList(); + private final List output = new ArrayList<>(); public FileThread(File fileLocation, boolean append) throws FileNotFoundException { stream = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileLocation, append), StandardCharsets.UTF_8)); diff --git a/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java b/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java index e9136f0d..12a6d2a2 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java +++ b/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java @@ -5,37 +5,41 @@ import net.minecraft.client.gui.GuiWorldSelection; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.gui.inventory.GuiContainerCreative; /** - * Adjusts the pointer/cursor of the playback to different gui scalings. - * - * This was the work of many hours of trial and error. - * - * Out of despair I reached out to Darkmoon to help me with this problem... - * - * @author ScribbleLP, Darkmoon + * Normalizes the cursor to be independent of gui scalings.
+ * That way, a TAS recorded in e.g. Gui Scale "Large" can also be played back on Gui Scale "Small" * + * @author Scribble, Darkmoon */ public class PointerNormalizer { + /** + * Mathematically removes scaling from the x coordinate + * @param pointerX The current pointer coordinate + * @return The normalized x coordinate + */ public static int getNormalizedX(int pointerX) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); - int out = (int) (pointerX - (scaled.getScaledWidth() / 2D)); - return out; + return (int) (pointerX - (scaled.getScaledWidth() / 2D)); } + /** + * Mathematically removes scaling from the y coordinate + * @param pointerY The current pointer coordinate + * @return The normalized y coordinate + */ public static int getNormalizedY(int pointerY) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); int out = pointerY; - if (mc.currentScreen instanceof GuiContainer || mc.currentScreen instanceof GuiContainerCreative) { + if (mc.currentScreen instanceof GuiContainer) { out = (int) (pointerY - (scaled.getScaledHeight() / 2D)); } else if (mc.currentScreen instanceof GuiWorldSelection|| mc.currentScreen instanceof GuiMultiplayer) { - + // TODO Figure out what to do here } else { out = (int) (pointerY - (scaled.getScaledHeight() / 4 + 72 + -16)); } @@ -43,62 +47,62 @@ public static int getNormalizedY(int pointerY) { return out; } - public static int getCoordsX(int normalizedX) { + /** + * Reapplies gui scaling to the normalized pointer x coordinate + * @param normalizedX The normalized pointer coordinate + * @return The scaled coordinate + */ + public static int reapplyScalingX(int normalizedX) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); int out = (int) Math.round(normalizedX + (scaled.getScaledWidth() / 2D)); - return limiterX(out, scaled); + return clamp(out, 0, scaled.getScaledWidth()); } - public static int getCoordsY(int normalizedY) { + /** + * Reapplies gui scaling to the normalized pointer y coordinate + * @param normalizedY The normalized pointer coordinate + * @return The scaled coordinate + */ + public static int reapplyScalingY(int normalizedY) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); int out = normalizedY; - if (mc.currentScreen instanceof GuiContainer || mc.currentScreen instanceof GuiContainerCreative) { + if (mc.currentScreen instanceof GuiContainer) { out = (int) Math.round(normalizedY + (scaled.getScaledHeight() / 2D)); } else if (mc.currentScreen instanceof GuiWorldSelection || mc.currentScreen instanceof GuiMultiplayer) { - + // TODO Figure out what to do here } else { out = (int) (normalizedY + (scaled.getScaledHeight() / 4 + 72 + -16)); } - return limiterY(out, scaled); - } - - private static int limiterX(int out, ScaledResolution scaled) { - int width = scaled.getScaledWidth(); - if (out > width) { - out = width; - } else if (out < 0) - out = 0; - return out; - } - - private static int limiterY(int out, ScaledResolution scaled) { - int height = scaled.getScaledHeight(); - if (out > height) { - out = height; - } else if (out < 0) - out = 0; - return out; + return clamp(out, 0, scaled.getScaledHeight()); } - private static int gcd(int a, int b) { - return (b == 0) ? a : gcd(b, a % b); + private static int clamp(int value, int lower, int upper) { + if (value < lower) { + return lower; + } else { + return Math.min(value, upper); + } } public static void printAspectRatio() { int height = Minecraft.getMinecraft().displayHeight; int width = Minecraft.getMinecraft().displayWidth; - int gcd = gcd(width, height); + int gcd = greatestCommonDivisor(width, height); if (gcd == 0) { System.out.println(gcd); } else { System.out.println(width / gcd + ":" + height / gcd); } } - + + private static int greatestCommonDivisor(int a, int b) { + return (b == 0) ? a : greatestCommonDivisor(b, a % b); + } + /* * Here lies 10 hours of work for something I didn't even use. This code * normalizes the pointers coordinates and scales it depending on the screen diff --git a/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java index fd13454b..9e462b3a 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java @@ -7,7 +7,6 @@ * A simple scheduling interface * * @author Scribble - * */ public class Scheduler { diff --git a/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java b/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java index 4f514c1e..2588c4e1 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java +++ b/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java @@ -30,6 +30,11 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.ResourceLocation; +/** + * Downloads shield textures from + *
+ * Similar to {@link VirtualKeyboard} and {@link VirtualMouse} with the difference,
+ * that no difference calculation is applied and only the absolute camera coordinates are used.
+ * This makes the playback desync proof to different mouse sensitivity across PCs.
+ * + */ public class VirtualCameraAngle extends Subtickable implements Serializable { - private float pitch; - private float yaw; - + /** + * Controls the up/down coordinate of the camera. Clamped between -90 and +90 + */ + private Float pitch; + /** + * Controls the left/right coordinate of the camera. In this case the camera is clamped between -180 and +180 + */ + private Float yaw; + + /** + * Creates an empty camera angle with pitch and yaw = 0 + */ public VirtualCameraAngle() { - this(0, 0, new ArrayList<>(), true); + this(null, null, new ArrayList<>(), true); } - public VirtualCameraAngle(float pitch, float yaw) { + /** + * Creates a subtick camera angle with {@link Subtickable#subtickList} uninitialized + * @param pitch {@link #pitch} + * @param yaw {@link #yaw} + */ + public VirtualCameraAngle(Float pitch, Float yaw) { this(pitch, yaw, null); } - public VirtualCameraAngle(float pitch, float yaw, List subtickList) { + /** + * Creates a parent camera angle + * @param pitch {@link #pitch} + * @param yaw {@link #yaw} + * @param ignoreFirstUpdate {@link Subtickable#ignoreFirstUpdate} + */ + public VirtualCameraAngle(Float pitch, Float yaw, boolean ignoreFirstUpdate) { + this(pitch, yaw, new ArrayList<>(), ignoreFirstUpdate); + } + + /** + * Creates a camera angle with existing values + * @param pitch {@link #pitch} + * @param yaw {@link #yaw} + * @param subtickList {@link Subtickable#subtickList} + */ + public VirtualCameraAngle(Float pitch, Float yaw, List subtickList) { this(pitch, yaw, subtickList, false); } - - public VirtualCameraAngle(float pitch, float yaw, List subtickList, boolean ignoreFirstUpdate) { + + /** + * Creates a camera angle with initialized values + * @param pitch {@link VirtualCameraAngle#pitch} + * @param yaw {@link VirtualCameraAngle#yaw} + * @param subtickList {@link Subtickable#subtickList} + * @param ignoreFirstUpdate {@link Subtickable#ignoreFirstUpdate} + */ + public VirtualCameraAngle(Float pitch, Float yaw, List subtickList, boolean ignoreFirstUpdate) { super(subtickList, ignoreFirstUpdate); this.pitch = pitch; this.yaw = yaw; } - public void update(float pitch, float yaw) { + /** + * Updates the camera angle. + * @param pitchDelta The difference between absolute coordinates of the pitch, is added to {@link VirtualCameraAngle#pitch} + * @param yawDelta The difference between absolute coordinates of the yaw, is added to {@link VirtualCameraAngle#yaw} + */ + public void update(float pitchDelta, float yawDelta) { + if(pitch==null || yaw == null) { + return; + } if(isParent() && !ignoreFirstUpdate()) { addSubtick(clone()); } - this.pitch += pitch; - this.pitch = MathHelper.clamp(this.pitch, -90.0F, 90.0F); - this.yaw += yaw; + this.pitch = MathHelper.clamp(this.pitch + pitchDelta, -90.0F, 90.0F); + this.yaw += yawDelta; } + public void set(float pitch, float yaw) { + this.pitch = pitch; + this.yaw = yaw; + } + + /** + * A list of all camera states in this VirtualCameraAngle. + * It consists of: {@link Subtickable#subtickList} + this + * @param reference A list of VirtualCameraAngles with the newest being the current camera angle + */ public void getStates(List reference) { if (isParent()) { reference.addAll(subtickList); @@ -44,6 +107,10 @@ public void getStates(List reference) { } } + /** + * Copies the data from another camera angle into this camera without creating a new object. + * @param camera The camera to move from + */ public void copyFrom(VirtualCameraAngle camera) { this.pitch = camera.pitch; this.yaw = camera.yaw; @@ -52,6 +119,14 @@ public void copyFrom(VirtualCameraAngle camera) { camera.subtickList.clear(); } + public void clear() { + this.pitch = null; + this.yaw = null; + } + + /** + * Creates a clone of this object as a subtick + */ @Override public VirtualCameraAngle clone() { return new VirtualCameraAngle(pitch, yaw); @@ -61,21 +136,36 @@ public VirtualCameraAngle clone() { public boolean equals(Object obj) { if (obj instanceof VirtualCameraAngle) { VirtualCameraAngle angle = (VirtualCameraAngle) obj; - return pitch == angle.pitch && yaw == angle.yaw; + return (pitch != null && pitch.equals(angle.pitch)) && (yaw != null && yaw.equals(angle.yaw)); } return super.equals(obj); } @Override public String toString() { + if(isParent()) { + return getAll().stream().map(VirtualCameraAngle::toString2).collect(Collectors.joining("\n")); + } else { + return toString2(); + } + } + + private String toString2() { return String.format("%s;%s", pitch, yaw); } - public float getPitch() { + /** + * @return {@link #pitch} + */ + public Float getPitch() { return pitch; } - public float getYaw() { + /** + * @return {@link #yaw} + */ + public Float getYaw() { return yaw; } + } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java index 7ed018c0..9d55fe88 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java @@ -5,7 +5,6 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import com.minecrafttas.tasmod.TASmodClient; import org.apache.commons.lang3.tuple.Triple; import org.apache.logging.log4j.Logger; import org.lwjgl.input.Keyboard; @@ -16,7 +15,6 @@ import com.minecrafttas.tasmod.util.Ducks; import com.minecrafttas.tasmod.util.LoggerMarkers; import com.minecrafttas.tasmod.util.PointerNormalizer; -import com.minecrafttas.tasmod.virtual.event.VirtualCameraAngleEvent; import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent; @@ -130,9 +128,10 @@ public boolean willKeyBeDown(int keycode) { /** * Unpresses all keys in {@link VirtualKeyboardInput#nextKeyboard} and {@link VirtualMouseInput#nextMouse} */ - public void unpress() { + public void clear() { KEYBOARD.nextKeyboard.clear(); MOUSE.nextMouse.clear(); + CAMERA_ANGLE.nextCameraAngle.clear(); } /** @@ -385,7 +384,7 @@ public VirtualMouseInput(VirtualMouse preloadedMouse) { * @see VirtualInput#update(GuiScreen) * @param keycode The keycode of this event * @param keystate The keystate of this event - * @param scrollwheel The scrollWheel of this event + * @param scrollwheel The scrollwheel direction of this event * @param cursorX The x coordinate of the cursor of this event * @param cursorY The y coordinate of the cursot of this event */ @@ -443,14 +442,14 @@ public int getEventMouseScrollWheel() { * @return The x coordinate of the cursor of {@link #currentMouseEvent} */ public int getEventCursorX() { - return PointerNormalizer.getCoordsX(currentMouseEvent.getCursorX()); + return PointerNormalizer.reapplyScalingX(currentMouseEvent.getCursorX()); } /** * @return The y coordinate of the cursor of {@link #currentMouseEvent} */ public int getEventCursorY() { - return PointerNormalizer.getCoordsY(currentMouseEvent.getCursorY()); + return PointerNormalizer.reapplyScalingY(currentMouseEvent.getCursorY()); } /** @@ -474,15 +473,37 @@ public boolean willKeyBeDown(int keycode) { } + /** + * Subclass of {@link VirtualInput} handling camera angle logic.
+ *
+ * Unlike {@link VirtualKeyboardInput} or {@link VirtualMouseInput} no subtick behaviour is implemented,
+ * making this a simple pitch and yaw storing class, allowing for redirection. + *
+ * In theory, subtick behaviour is possible, but only useful for interpolation,
+ * as the camera angle is only updated every tick (see {@link com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer}). + */ public class VirtualCameraAngleInput { + /** + * The current camera angle + */ private final VirtualCameraAngle currentCameraAngle; private final VirtualCameraAngle nextCameraAngle = new VirtualCameraAngle(); private final List cameraAngleInterpolationStates = new ArrayList<>(); - + + /** + * Constructor to preload the {@link #currentCameraAngle} with an existing camera angle + * @param preloadedCamera The new {@link #currentCameraAngle} + */ public VirtualCameraAngleInput(VirtualCameraAngle preloadedCamera) { currentCameraAngle = preloadedCamera; } - + + /** + * Update the camera angle + * @see com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer#runUpdate(float); + * @param pitch Absolute rotationPitch of the player + * @param yaw Absolute rotationYaw of the player + */ public void updateNextCameraAngle(float pitch, float yaw) { // LOGGER.debug("Pitch: {}, Yaw: {}", pitch, yaw); nextCameraAngle.update(pitch, yaw); @@ -493,17 +514,21 @@ public void nextCameraTick() { currentCameraAngle.copyFrom(nextCameraAngle); } - public float getCurrentPitch() { + public void setCamera(Float pitch, Float yaw) { + nextCameraAngle.set(pitch, yaw); + } + + public Float getCurrentPitch() { return currentCameraAngle.getPitch(); } - public float getCurrentYaw() { + public Float getCurrentYaw() { return currentCameraAngle.getYaw(); } public Triple getInterpolatedState(float partialTick, float pitch, float yaw, boolean enable){ if(!enable) { - return Triple.of(TASmodClient.virtual.CAMERA_ANGLE.nextCameraAngle.getPitch(), TASmodClient.virtual.CAMERA_ANGLE.nextCameraAngle.getYaw()+180, 0f); + return Triple.of(nextCameraAngle.getPitch()==null ? pitch : nextCameraAngle.getPitch(), nextCameraAngle.getYaw()==null? pitch : nextCameraAngle.getYaw()+180, 0f); } float interpolatedPitch = 0f; diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java index ae96365d..d12bea00 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java @@ -252,7 +252,7 @@ public void addChar(char character, boolean repeatEventsEnabled) { } @Override - protected void clear(){ + public void clear(){ super.clear(); charList.clear(); } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java index fe1cca43..4329fc05 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java @@ -6,6 +6,15 @@ import java.util.*; import java.util.stream.Collectors; +/** + * Stores the mouse specific values in a given timeframe
+ *
+ * Similar to {@link VirtualKeyboard}, but instead of a list of characters,
+ * it stores the state of the scroll wheel and the cursors x and y coordinate on screen. + * + * @author Scribble + * @see VirtualInput.VirtualMouseInput + */ public class VirtualMouse extends VirtualPeripheral implements Serializable { /** @@ -123,25 +132,25 @@ public void getVirtualEvents(VirtualMouse nextMouse, Queue re * the one from tick 16 * @param reference The queue to fill. Passed in by reference. */ - public void getDifference(VirtualMouse nextPeripheral, Queue reference) { + public void getDifference(VirtualMouse nextMouse, Queue reference) { /* * Checks if pressedKeys are the same... */ - if(pressedKeys.equals(nextPeripheral.pressedKeys)){ + if(pressedKeys.equals(nextMouse.pressedKeys)){ - /** + /* * ...but scrollWheel, cursorX or cursorY are different. * Without this, the scrollWheel would only work if a mouse button is pressed at the same time. */ - if(!equals(nextPeripheral)) { - reference.add(new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, nextPeripheral.scrollWheel, nextPeripheral.cursorX, nextPeripheral.cursorY)); + if(!equals(nextMouse)) { + reference.add(new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, nextMouse.scrollWheel, nextMouse.cursorX, nextMouse.cursorY)); } return; } - int scrollWheelCopy = nextPeripheral.scrollWheel; - int cursorXCopy = nextPeripheral.cursorX; - int cursorYCopy = nextPeripheral.cursorY; + int scrollWheelCopy = nextMouse.scrollWheel; + int cursorXCopy = nextMouse.cursorX; + int cursorYCopy = nextMouse.cursorY; /* Calculate symmetric difference of keycodes */ @@ -153,7 +162,7 @@ public void getDifference(VirtualMouse nextPeripheral, Queue RC <- unpressed */ for(int keycode : pressedKeys) { - if (!nextPeripheral.getPressedKeys().contains(keycode)) { + if (!nextMouse.getPressedKeys().contains(keycode)) { reference.add(new VirtualMouseEvent(keycode, false, scrollWheelCopy, cursorXCopy, cursorYCopy)); scrollWheelCopy = 0; cursorXCopy = 0; @@ -168,7 +177,7 @@ public void getDifference(VirtualMouse nextPeripheral, Queue ------------- MC <- pressed */ - for(int keycode : nextPeripheral.getPressedKeys()) { + for(int keycode : nextMouse.getPressedKeys()) { if (!this.pressedKeys.contains(keycode)) { reference.add(new VirtualMouseEvent(keycode, true, scrollWheelCopy, cursorXCopy, cursorYCopy)); } @@ -176,7 +185,7 @@ public void getDifference(VirtualMouse nextPeripheral, Queue } @Override - protected void clear() { + public void clear() { super.clear(); clearMouseData(); } diff --git a/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java b/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java new file mode 100644 index 00000000..1809c867 --- /dev/null +++ b/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java @@ -0,0 +1,144 @@ +package tasmod.virtual; + +import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +public class VirtualCameraAngleTest { + + /** + * Test the empty constructor + */ + @Test + void testEmptyConstructor(){ + VirtualCameraAngle actual = new VirtualCameraAngle(); + assertEquals(null, actual.getPitch()); + assertEquals(null, actual.getYaw()); + assertTrue(actual.isParent()); + } + + /** + * Test subtick constructor with premade pitch and value + */ + @Test + void testSubtickConstructor(){ + float x = 1f; + float y = 2f; + + VirtualCameraAngle actual = new VirtualCameraAngle(x, y); + assertEquals(1f, actual.getPitch()); + assertEquals(2f, actual.getYaw()); + assertFalse(actual.isParent()); + } + + /** + * Testing update function + */ + @Test + void testUpdate(){ + float x = 1f; + float y = 2f; + + VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true); + + actual.update(x, y); + + assertEquals(1f, actual.getPitch()); + assertEquals(2f, actual.getYaw()); + } + + /** + * Testing update function, but with a pitch higher/lower than 90/-90 + */ + @Test + void testUpdateWithBadPitch() { + VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true); + + actual.update(-100f, 0f); + + assertEquals(-90f, actual.getPitch()); + + actual.update(360f, 0f); + + assertEquals(90f, actual.getPitch()); + } + + /** + * Test copyfrom method + */ + @Test + void copyFrom() { + VirtualCameraAngle actual = new VirtualCameraAngle(); + actual.update(1f, 2f); + actual.update(3f, 4f); + } + + /** + * Test the toString method without subticks + */ + @Test + void testToString() { + float x = 1f; + float y = 2f; + + VirtualCameraAngle actual = new VirtualCameraAngle(x, y); + + assertEquals("1.0;2.0", actual.toString()); + } + + /** + * Test the toString method with subticks + */ + @Test + void testToStringSubticks() { + + VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true); + actual.update(1f, 2f); + actual.update(3f, 4f); + actual.update(5f, 6f); + + assertEquals("1.0;2.0\n4.0;6.0\n9.0;12.0", actual.toString()); + } + + /** + * Test cloning the camera angle + */ + @Test + void testClone(){ + float x = 1f; + float y = 2f; + + VirtualCameraAngle test = new VirtualCameraAngle(x, y); + + VirtualCameraAngle actual = test.clone(); + + assertEquals(1f, actual.getPitch()); + assertEquals(2f, actual.getYaw()); + } + + /** + * Test equals + */ + @Test + void testEquals(){ + float x = 1f; + float y = 2f; + + VirtualCameraAngle test = new VirtualCameraAngle(x, y); + VirtualCameraAngle test2 = new VirtualCameraAngle(x, y); + + assertEquals(test, test2); + } + + /** + * Test where equals will fail + */ + @Test + void testNotEquals(){ + + VirtualCameraAngle test = new VirtualCameraAngle(1f, 2f); + VirtualCameraAngle test2 = new VirtualCameraAngle(3f, 4f); + + assertNotEquals(test, test2); + } +} diff --git a/src/test/java/tasmod/virtual/VirtualInputTest.java b/src/test/java/tasmod/virtual/VirtualInputTest.java index bf184b57..30ce0947 100644 --- a/src/test/java/tasmod/virtual/VirtualInputTest.java +++ b/src/test/java/tasmod/virtual/VirtualInputTest.java @@ -35,7 +35,7 @@ void testConstructor() { void testPreloadedConstructor() { VirtualKeyboard preloadedKeyboard = new VirtualKeyboard(); VirtualMouse preloadedMouse = new VirtualMouse(); - VirtualCameraAngle preloadedCameraAngle = new VirtualCameraAngle(); + VirtualCameraAngle preloadedCameraAngle = new VirtualCameraAngle(0f, 0f); preloadedKeyboard.update(VirtualKey.W.getKeycode(), true, 'w'); preloadedMouse.update(VirtualKey.LC.getKeycode(), true, 15, 0, 0);