mouseEvent, VirtualSubticks subticks) {
- this.tick = tick;
- this.keyboardevent = keyboardevent;
- this.mouseEvent = mouseEvent;
- this.subticks = subticks;
+ /**
+ * @return The x coordinate of the cursor of {@link #currentMouseEvent}
+ */
+ public int getNormalizedCursorX() {
+ return currentMouseEvent.getCursorX();
+ }
+
+ /**
+ * @return The scaled y coordinate of the cursor of {@link #currentMouseEvent}
+ */
+ public int getEventCursorY() {
+ return PointerNormalizer.reapplyScalingY(getNormalizedCursorY());
}
+
+ /**
+ * @return The y coordinate of the cursor of {@link #currentMouseEvent}
+ */
+ public int getNormalizedCursorY() {
+ return currentMouseEvent.getCursorY();
+ }
+
+ /**
+ * If the key is currently down and recognised by Minecraft
+ * @param keycode The keycode of the key in question
+ * @return Whether the key of the {@link #currentMouse} is down
+ */
+ public boolean isKeyDown(int keycode) {
+ return currentMouse.isKeyDown(keycode);
+ }
+
+ /**
+ * If the key will be down and recognised in the next tick by Minecraft.
+ * This is equal to checking if a key on the physical mouse is pressed
+ * @param keycode The keycode of the key in question
+ * @return Whether the key of the {@link #nextMouse} is down3
+ */
+ public boolean willKeyBeDown(int keycode) {
+ return nextMouse.isKeyDown(keycode);
+ }
+
}
/**
- * Gets all InputEvents from the current container.
+ * Subclass of {@link VirtualInput} handling camera angle logic.
*
- * Container and input events differ in that input events are the events that
- * get accepted by Minecraft in the runTickKeyboard.
- * The container however stores the current inputs and can calculate the
- * corresponding input events from that, but it does it only when you are
- * playing back or recording.
+ * Normally, the camera angle is updated every frame.
+ * This meant that inputs on mouse and keyboard, updated every tick,
+ * had to sync with the camera angle, updated every frame, which desynced in some edgecases.
*
- * This however runs through the {@link VirtualInput#container} and generates
- * the input events on for debug purposes.
+ * After extensive testing, the decision was made to conform the camera to update every tick,
+ * instead of every frame. By itself, this meant that moving the camera felt really laggy
+ *
+ * Therefore, an interpolation system was created that seperated the camera angle from the player head rotation,
+ * which are usually the synced.
+ *
+ * While the camera is the angle displayed on screen, the player rotation is responsible for the logic,
+ * e.g. at which block the player is currently aiming.
+ * This calls for a similar architecture as the {@link VirtualKeyboardInput} and the {@link VirtualMouseInput}.
+ *
+ * The {@link VirtualCameraAngleInput#currentCameraAngle} represents the player rotation, updated every tick
+ * and the {@link VirtualCameraAngleInput#nextCameraAngle} represents the camera angle, updated every frame.
+ *
+ * In pseudocode, the tick conformed camera looks like this:
+ *
+ * public void runGameLoop() {// The main update loop, every frame
+ * for(int i;i
+ * Note that the camera is updated after the tick function.
+ * Now in this pseudocode, the following methods from this class are injected like so:
+ *
+ * public void runGameLoop() {// The main update loop, every frame
+ * for(int i;i
*
- * @return
+ * While there is "subtick" behavior in this implementation,
+ * it is only used for the interpolation of the camera angle and not for the player rotation.
+ * This way you can customize the interpolated frames during playback.
*/
- public List getAllInputEvents() {
+ public class VirtualCameraAngleInput {
+ /**
+ * The current camera angle in game.
+ *
+ * Updated every tick in {@link #nextCameraTick()}
+ */
+ private final VirtualCameraAngle currentCameraAngle;
+ /**
+ * The new camera angle
+ *
+ * updated every frame in {@link #updateNextCameraAngle(float, float)}
+ * and updates {@link #currentCameraAngle} in {@link #nextCameraTick()}
+ */
+ private final VirtualCameraAngle nextCameraAngle = new VirtualCameraAngle();
+ /**
+ * States of the {@link #nextCameraAngle} made during the tick.
+ * Is updated in {@link #nextCameraTick()}
+ */
+ 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;
+ }
- List main = new ArrayList<>();
+ /**
+ * Update the camera angle.
+ *
+ * Runs every frame
+ *
+ * @see com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer#runUpdate(float);
+ * @param pitchDelta Relative rotationPitch delta from LWJGLs mouse delta.
+ * @param yawDelta Relative rotationYaw delta from LWJGLs mouse delta.
+ */
+ public void updateNextCameraAngle(float pitchDelta, float yawDelta) {
+// LOGGER.debug("Pitch: {}, Yaw: {}", pitch, yaw);
+ nextCameraAngle.update(pitchDelta, yawDelta);
+ }
+
+ /**
+ * Updates the {@link #currentCameraAngle} and {@link #cameraAngleInterpolationStates}.
+ * Runs every tick.
+ *
+ * @see MixinEntityRenderer#runUpdate(float)
+ */
+ public void nextCameraTick() {
+ nextCameraAngle.getStates(cameraAngleInterpolationStates);
+ currentCameraAngle.copyFrom(nextCameraAngle);
+ }
- for (int i = 0; i < container.size(); i++) {
+ /**
+ * Sets the camera coordinates directly.
+ *
+ * The camera angle is stored in absolute coordinates,
+ * which should match the vanilla coordinates displayed in F3
+ *
+ * This creates a problem when initializing the world, we don't know the camera angle the player has.
+ * Without this, the player would always start facing the 0;0 coordinate.
+ *
+ * To fix this, the camera is initialized with pitch and yaw being null. If that is the case,
+ * then the current player rotation is set with this method in {@link MixinEntityRenderer#runUpdate(float)}
+ * @param pitch The absolute player pitch
+ * @param yaw The absolute player yaw
+ */
+ public void setCamera(Float pitch, Float yaw) {
+ nextCameraAngle.set(pitch, yaw);
+ }
- TickInputContainer tick = container.get(i);
- TickInputContainer nextTick = container.get(i + 1);
+ /**
+ * @return The absolute pitch coordinate of the player. May be null when it's initialized
+ */
+ public Float getCurrentPitch() {
+ return currentCameraAngle.getPitch();
+ }
+
+ /**
+ * @return The absolute yaw coordinate of the player. May be null when it's initialized
+ */
+ public Float getCurrentYaw() {
+ return currentCameraAngle.getYaw();
+ }
- if (nextTick == null) {
- nextTick = new TickInputContainer(i + 1); // Fills the last tick in the container with an empty TickInputContainer
+ /**
+ * Gets the absolute coordinates of the camera angle
+ *
+ * @param partialTick The partial ticks of the timer
+ * @param pitch The original pitch of the camera
+ * @param yaw The original yaw of the camera
+ * @param enable Whether the custom interpolation is enabled. Enabled during playback.
+ * @return A triple of pitch, yaw and roll, as left, middle and right respectively
+ */
+ public Triple getInterpolatedState(float partialTick, float pitch, float yaw, boolean enable) {
+ if (!enable) { // If interpolation is not enabled, return the values of nextCameraAngle
+ return Triple.of(nextCameraAngle.getPitch() == null ? pitch : nextCameraAngle.getPitch(), nextCameraAngle.getYaw() == null ? pitch : nextCameraAngle.getYaw() + 180, 0f);
}
- VirtualKeyboard keyboard = tick.getKeyboard();
- List keyboardEventsList = keyboard.getDifference(nextTick.getKeyboard());
+ float interpolatedPitch = 0f;
+ float interpolatedYaw = 0f;
- VirtualMouse mouse = tick.getMouse();
- List mouseEventsList = mouse.getDifference(nextTick.getMouse());
+ if (cameraAngleInterpolationStates.size() == 1) { // If no interpolation data was specified, interpolate over 2 values
+ interpolatedPitch = (float) MathHelper.clampedLerp(cameraAngleInterpolationStates.get(0).getPitch(), currentCameraAngle.getPitch(), partialTick);
+ interpolatedYaw = (float) MathHelper.clampedLerp(currentCameraAngle.getYaw(), cameraAngleInterpolationStates.get(0).getYaw() + 180, partialTick);
+ } else {
- main.add(new InputEvent(tick.getTick(), keyboardEventsList, mouseEventsList, tick.getSubticks()));
- }
- return main;
- }
+ int index = (int) MathHelper.clampedLerp(0, cameraAngleInterpolationStates.size(), partialTick); // Get interpolate index
- @Override
- public void onPlayerJoinedClientSide(EntityPlayerSP player) {
- unpressNext();
+ interpolatedPitch = cameraAngleInterpolationStates.get(index).getPitch();
+ interpolatedYaw = cameraAngleInterpolationStates.get(index).getYaw();
+ }
+
+ return Triple.of(interpolatedPitch, interpolatedYaw, 0f);
+ }
}
}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java
index 67cd7323..98e2d273 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java
@@ -1,60 +1,194 @@
package com.minecrafttas.tasmod.virtual;
-import java.io.Serializable;
-
/**
- * Class to store which keys on the keyboard have been pressed, similar to how Keybindings work.
- *
- * @author Scribble
+ * List of all names and keycodes from the keyboard and the mouse.
*
+ * @author Scribble
*/
-public class VirtualKey implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 2186369910433056224L;
- private String name;
- private int keycode;
- private boolean isKeyDown=false;
-
- public VirtualKey(String name, int keycode) {
- this.name = name;
- this.keycode = keycode;
- }
-
- private VirtualKey(String name, int keycode, boolean isKeyDown) {
- this.name = name;
- this.keycode = keycode;
- this.isKeyDown = isKeyDown;
- }
-
- public String getName() {
- return name;
- }
-
- public int getKeycode() {
- return keycode;
- }
-
- public boolean isKeyDown() {
- return isKeyDown;
- }
-
- public void setPressed(boolean pressed) {
- isKeyDown=pressed;
- }
-
- @Override
- public boolean equals(Object obj) {
- VirtualKey key = (VirtualKey) obj;
- if(key.isKeyDown!=isKeyDown) return false;
- if(key.keycode!=keycode) return false;
- return true;
- }
-
- @Override
- public VirtualKey clone() {
- return new VirtualKey(name, keycode, isKeyDown);
- }
+public enum VirtualKey {
+ // Keyboard
+ ZERO(0),
+ ESC(1),
+ KEY_1(2),
+ KEY_2(3),
+ KEY_3(4),
+ KEY_4(5),
+ KEY_5(6),
+ KEY_6(7),
+ KEY_7(8),
+ KEY_8(9),
+ KEY_9(10),
+ KEY_0(11),
+ MINUS(12),
+ EQUALS(13),
+ BACK(14),
+ TAB(15),
+ Q(16),
+ W(17),
+ E(18),
+ R(19),
+ T(20),
+ Y(21),
+ U(22),
+ I(23),
+ O(24),
+ P(25),
+ LBRACKET(26),
+ RBRACKET(27),
+ RETURN(28),
+ LCONTROL(29),
+ A(30),
+ S(31),
+ D(32),
+ F(33),
+ G(34),
+ H(35),
+ J(36),
+ K(37),
+ L(38),
+ SEMICOLON(39),
+ APOSTROPHE(40),
+ GRAVE(41),
+ LSHIFT(42),
+ BACKSLASH(43),
+ Z(44),
+ X(45),
+ C(46),
+ V(47),
+ B(48),
+ N(49),
+ M(50),
+ COMMA(51),
+ PERIOD(52),
+ SLASH(53),
+ RSHIFT(54),
+ MULTIPLY(55),
+ ALT(56),
+ SPACE(57),
+ CAPSLOCK(58),
+ F1(59),
+ F2(60),
+ F3(61),
+ F4(62),
+ F5(63),
+ F6(64),
+ F7(65),
+ F8(66),
+ F9(67),
+ F10(68),
+ NUMLOCK(69),
+ SCROLL(70),
+ NUMPAD7(71),
+ NUMPAD8(72),
+ NUMPAD9(73),
+ SUBTRACT(74),
+ NUMPAD4(75),
+ NUMPAD5(76),
+ NUMPAD6(77),
+ ADD(78),
+ NUMPAD1(79),
+ NUMPAD2(80),
+ NUMPAD3(81),
+ NUMPAD0(82),
+ DECIMAL(83),
+ F11(87),
+ F12(88),
+ F13(100),
+ F14(101),
+ F15(102),
+ F16(103),
+ F17(104),
+ F18(105),
+ KANA(112),
+ F19(113),
+ CONVERT(121),
+ NOCONVERT(123),
+ YEN(125),
+ NUMPADEQUALS(141),
+ CIRCUMFLEX(144),
+ AT(145),
+ COLON(146),
+ UNDERLINE(147),
+ KANJI(148),
+ STOP(149),
+ NUMPADENTER(156),
+ RCONTROL(157),
+ NUMPADCOMMA(179),
+ DIVIDE(181),
+ PRINT(183),
+ ALT_GR(184),
+ PAUSE(197),
+ HOME(199),
+ UP(200),
+ PRIOR(201),
+ LEFT(203),
+ RIGHT(205),
+ END(207),
+ DOWN(208),
+ NEXT(209),
+ INSERT(210),
+ DELETE(211),
+ WIN(219),
+ APPS(221),
+
+ // Mouse
+ MOUSEMOVED(-101),
+ LC(-100),
+ RC(-99),
+ MC(-98),
+ MBUTTON4(-97),
+ MBUTTON5(-96),
+ MBUTTON6(-95),
+ MBUTTON7(-94),
+ MBUTTON8(-93),
+ MBUTTON9(-92),
+ MBUTTON10(-91),
+ MBUTTON11(-90),
+ MBUTTON12(-89),
+ MBUTTON13(-88),
+ MBUTTON14(-87),
+ MBUTTON15(-86),
+ MBUTTON16(-85);
+
+ private final int keycode;
+
+ private VirtualKey(int keycode) {
+ this.keycode = keycode;
+ }
+
+ public int getKeycode() {
+ return keycode;
+ }
+
+ public static Integer getKeycode(String keyname) {
+ VirtualKey key = get(keyname);
+ if (key != null)
+ return key.getKeycode();
+ return null;
+ }
+
+ public static String getName(int keycode) {
+ VirtualKey key = get(keycode);
+ if (key != null)
+ return key.name();
+ return Integer.toString(keycode);
+ }
+
+ public static VirtualKey get(int keycode) {
+ for (VirtualKey key : values()) {
+ if (key.getKeycode() == keycode) {
+ return key;
+ }
+ }
+ return null;
+ }
+
+ public static VirtualKey get(String keyname) {
+ for (VirtualKey key : values()) {
+ if (key.name().equalsIgnoreCase(keyname)) {
+ return key;
+ }
+ }
+ return null;
+ }
}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java
index c2130f56..d79cc738 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java
@@ -20,7 +20,7 @@
* Applies special rules to vanilla keybindings.
*
* Using {@link #isKeyDown(KeyBinding)}, the registered keybindings will work
- * inside of gui screens
+ * inside gui screens
*
* {@link #isKeyDownExceptTextfield(KeyBinding)} does the same, but excludes
* textfields, certain guiscreens, and the keybinding options
@@ -32,18 +32,33 @@
*
*/
public class VirtualKeybindings {
- private static Minecraft mc = Minecraft.getMinecraft();
- private static long cooldown = 50*5;
- private static HashMap cooldownHashMap = Maps.newHashMap();
- private static List blockedKeys = new ArrayList<>();
+ /**
+ * The Minecraft instance
+ */
+ private static final Minecraft mc = Minecraft.getMinecraft();
+ /**
+ * The standard cooldown for a keybinding in milliseconds
+ */
+ private static final long cooldown = 50*5;
+ /**
+ * Stores the start time of a keybinding, used for cooldown calculation
+ */
+ private static final HashMap cooldownHashMap = new HashMap<>();
+ /**
+ * A list of keybindings which will not be recorded or pressed during recording or playback.
+ */
+ private static final List blockedKeys = new ArrayList<>();
+ /**
+ * True when a text field is currently focused in a gui, like the creative search tab
+ */
public static boolean focused = false;
/**
* Checks whether the keycode is pressed, regardless of any gui screens
*
- * @param keybind
- * @return
+ * @param keybind The keybind to check
+ * @return If the keybind is down
*/
public static boolean isKeyDown(KeyBinding keybind) {
@@ -77,10 +92,12 @@ public static boolean isKeyDown(KeyBinding keybind) {
}
/**
- * Checks whether the key is down, but stops when certain conditions apply
+ * Checks whether the key is down, but returns false if a text field is focused in a gui.
+ *
+ * Always returns false if GuiChat and GuiEditSign is open.
*
- * @param keybind
- * @return
+ * @param keybind The keybinding to check
+ * @return If a keybind is pressed. Returns false if a text field in a gui is focused
*/
public static boolean isKeyDownExceptTextfield(KeyBinding keybind) {
if (mc.currentScreen instanceof GuiChat || mc.currentScreen instanceof GuiEditSign || (focused && mc.currentScreen != null)) {
@@ -92,7 +109,7 @@ public static boolean isKeyDownExceptTextfield(KeyBinding keybind) {
/**
* Registers keybindings that should not be recorded or played back in a TAS
*
- * @param keybind
+ * @param keybind The keybinding to block
*/
public static void registerBlockedKeyBinding(KeyBinding keybind) {
blockedKeys.add(keybind);
@@ -111,5 +128,4 @@ public static boolean isKeyCodeAlwaysBlocked(int keycode) {
}
return false;
}
-
}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java
index 4bbcbccf..80adab57 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java
@@ -2,380 +2,323 @@
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.stream.Collectors;
+import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent;
import org.apache.commons.lang3.StringUtils;
-import org.lwjgl.input.Keyboard;
-import com.google.common.collect.Maps;
-import com.minecrafttas.tasmod.playback.PlaybackSerialiser;
-
-public class VirtualKeyboard implements Serializable {
-
- /**
- *
- */
- private static final long serialVersionUID = 4694772261313303998L;
-
- private Map keyList;
-
- private List charList;
-
- /**
- * Creates a copy of the virtual keyboard with the given key list
- *
- * @param keyListIn
- */
- public VirtualKeyboard(Map keyListIn, List charListIn) {
- Map copy = new HashMap();
-
- keyListIn.forEach((key, value) -> {
- copy.put(key, value.clone());
- });
- keyList = copy;
-
- List charCopy = new ArrayList();
-
- charListIn.forEach(charAction -> {
- charCopy.add(charAction);
- });
- charList = charCopy;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Stores keyboard specific values in a given timeframe.
+ *
+ * This keyboard mimics the {@link org.lwjgl.input.Keyboard} Minecraft is using.
+ * KeyboardEvent
+ * {@link org.lwjgl.input.Keyboard} has the following outputs, when a key is pressed or unpressed on the physical keyboard:
+ *
+ * - int KeyCode: The unique keycode of the key
+ * - boolean KeyState: The new state of the key. True for pressed, false for unpressed
+ * - char KeyCharacter: The character associated for each key
+ *
+ * While the keycode is the same between physical keyboards, the key character might differ.
+ * It is also common that one keycode has multiple characters associated with it, e.g.
+ * holding shift results in a capitalised character.
+ *
+ * These three outputs together are what we call a "KeyboardEvent" and might look like this:
+ *
+ * 17, true, w
+ *
+ * For keycode, keystate, keycharacter
+ * Updating the keyboard
+ * This keyboard stores it's values in "states".
+ * That means that all the keys that are currently pressed are stored in {@link #pressedKeys}.
+ * And this list is updated via a keyboard event in {@link #update(int, boolean, char)}.
+ * Difference
+ * When comparing 2 keyboard states, we can generate a list of differences from them in form of {@link VirtualKeyboardEvent}s.
+ *
+ * this: W A S
+ * next: W S D
+ *
+ * Since there are 2 differences between this and the next keyboard,
+ * this will result in 2 {@link VirtualKeyboardEvent}s. And combined with the {@link #charList} we can also get the associated characters:
+ *
+ * 30, false, null // A is unpressed
+ * 32, true, d // D is pressed
+ *
+ * Subticks
+ * Minecraft updates its keyboard every tick. All the key events that occur inbetween are stored,
+ * then read out when a new tick has started.
We call these "inbetween" ticks subticks.
+ * Parent->Subtick
+ * In a previous version of this keyboard, subticks were bundeled and flattened into one keyboard state.
+ * After all, Minecraft updates only occur once every tick, storing subticks seemed unnecessary.
+ *
+ * However, this posed some usability issues when playing in a low game speed via {@link com.minecrafttas.tasmod.tickratechanger.TickrateChangerClient}.
+ * Now you had to hold the key until the next tick to get it recognised by the game.
+ *
+ * To fix this, now every subtick is stored as a keyboard state as well.
+ * When updating the keyboard in {@link #update(int, boolean, char)}, a clone of itself is created and stored in {@link #subtickList},
+ * with the difference that the subtick state has no {@link #subtickList}.
+ * In a nutshell, the keyboard stores it's past changes in {@link #subtickList} with the first being the oldest change.
+ *
+ * @author Scribble
+ * @see VirtualInput.VirtualKeyboardInput
+ */
+public class VirtualKeyboard extends VirtualPeripheral implements Serializable {
+
+ /**
+ * The list of characters that were pressed on this keyboard.
+ */
+ private final List charList;
+
+ /**
+ * A queue of characters used in {@link #getDifference(VirtualKeyboard, Queue)}.
+ * Used for distributing characters to {@link VirtualKeyboardEvent}s in an order.
+ */
+ private final ConcurrentLinkedQueue charQueue = new ConcurrentLinkedQueue<>();
+
+ /**
+ * Creates an empty parent keyboard with all keys unpressed
+ */
+ public VirtualKeyboard() {
+ this(new LinkedHashSet<>(), new ArrayList<>(), new ArrayList<>(), true);
+ }
+
+ /**
+ * Creates a subtick keyboard with {@link VirtualPeripheral#subtickList} uninitialized
+ * @param pressedKeys The new list of pressed keycodes for this subtickKeyboard
+ * @param charList A list of characters for this subtickKeyboard
+ */
+ public VirtualKeyboard(Set pressedKeys, List charList){
+ this(pressedKeys, charList, null, false);
+ }
+
+ /**
+ * Creates a keyboard from existing variables
+ * @param pressedKeys The existing list of pressed keycodes
+ * @param charList The existing list of characters
+ */
+ public VirtualKeyboard(Set pressedKeys, List charList, boolean ignoreFirstUpdate) {
+ this(pressedKeys, charList, null, ignoreFirstUpdate);
}
-
+
/**
- * Creates a Keyboard, where the keys are all unpressed
+ * Creates a keyboard from existing variables
+ * @param pressedKeys The existing list of {@link VirtualPeripheral#pressedKeys}
+ * @param charList The {@link #charList}
+ * @param subtickList {@link VirtualPeripheral#subtickList}
+ * @param ignoreFirstUpdate The {@link VirtualPeripheral#ignoreFirstUpdate}
*/
- public VirtualKeyboard() {
- charList = new ArrayList();
-
- keyList = Maps.newHashMap();
- keyList.put(0, new VirtualKey("0", 0));
- keyList.put(1, new VirtualKey("ESC", 1));
- keyList.put(2, new VirtualKey("KEY_1", 2));
- keyList.put(3, new VirtualKey("KEY_2", 3));
- keyList.put(4, new VirtualKey("KEY_3", 4));
- keyList.put(5, new VirtualKey("KEY_4", 5));
- keyList.put(6, new VirtualKey("KEY_5", 6));
- keyList.put(7, new VirtualKey("KEY_6", 7));
- keyList.put(8, new VirtualKey("KEY_7", 8));
- keyList.put(9, new VirtualKey("KEY_8", 9));
- keyList.put(10, new VirtualKey("KEY_9", 10));
- keyList.put(11, new VirtualKey("KEY_0", 11));
- keyList.put(12, new VirtualKey("MINUS", 12));
- keyList.put(13, new VirtualKey("EQUALS", 13));
- keyList.put(14, new VirtualKey("BACK", 14));
- keyList.put(15, new VirtualKey("TAB", 15));
- keyList.put(16, new VirtualKey("Q", 16));
- keyList.put(17, new VirtualKey("W", 17));
- keyList.put(18, new VirtualKey("E", 18));
- keyList.put(19, new VirtualKey("R", 19));
- keyList.put(20, new VirtualKey("T", 20));
- keyList.put(21, new VirtualKey("Y", 21));
- keyList.put(22, new VirtualKey("U", 22));
- keyList.put(23, new VirtualKey("I", 23));
- keyList.put(24, new VirtualKey("O", 24));
- keyList.put(25, new VirtualKey("P", 25));
- keyList.put(26, new VirtualKey("LBRACKET", 26));
- keyList.put(27, new VirtualKey("RBRACKET", 27));
- keyList.put(28, new VirtualKey("RETURN", 28));
- keyList.put(29, new VirtualKey("LCONTROL", 29));
- keyList.put(30, new VirtualKey("A", 30));
- keyList.put(31, new VirtualKey("S", 31));
- keyList.put(32, new VirtualKey("D", 32));
- keyList.put(33, new VirtualKey("F", 33));
- keyList.put(34, new VirtualKey("G", 34));
- keyList.put(35, new VirtualKey("H", 35));
- keyList.put(36, new VirtualKey("J", 36));
- keyList.put(37, new VirtualKey("K", 37));
- keyList.put(38, new VirtualKey("L", 38));
- keyList.put(39, new VirtualKey("SEMICOLON", 39));
- keyList.put(40, new VirtualKey("APOSTROPHE", 40));
- keyList.put(41, new VirtualKey("GRAVE", 41));
- keyList.put(42, new VirtualKey("LSHIFT", 42));
- keyList.put(43, new VirtualKey("BACKSLASH", 43));
- keyList.put(44, new VirtualKey("Z", 44));
- keyList.put(45, new VirtualKey("X", 45));
- keyList.put(46, new VirtualKey("C", 46));
- keyList.put(47, new VirtualKey("V", 47));
- keyList.put(48, new VirtualKey("B", 48));
- keyList.put(49, new VirtualKey("N", 49));
- keyList.put(50, new VirtualKey("M", 50));
- keyList.put(51, new VirtualKey("COMMA", 51));
- keyList.put(52, new VirtualKey("PERIOD", 52));
- keyList.put(53, new VirtualKey("SLASH", 53));
- keyList.put(54, new VirtualKey("RSHIFT", 54));
- keyList.put(55, new VirtualKey("MULTIPLY", 55));
- keyList.put(56, new VirtualKey("ALT", 56));
- keyList.put(57, new VirtualKey("SPACE", 57));
- keyList.put(58, new VirtualKey("CAPSLOCK", 58));
- keyList.put(59, new VirtualKey("F1", 59));
- keyList.put(60, new VirtualKey("F2", 60));
- keyList.put(61, new VirtualKey("F3", 61));
- keyList.put(62, new VirtualKey("F4", 62));
- keyList.put(63, new VirtualKey("F5", 63));
- keyList.put(64, new VirtualKey("F6", 64));
- keyList.put(65, new VirtualKey("F7", 65));
- keyList.put(66, new VirtualKey("F8", 66));
- keyList.put(67, new VirtualKey("F9", 67));
- keyList.put(68, new VirtualKey("F10", 68));
- keyList.put(69, new VirtualKey("NUMLOCK", 69));
- keyList.put(70, new VirtualKey("SCROLL", 70));
- keyList.put(71, new VirtualKey("NUMPAD7", 71));
- keyList.put(72, new VirtualKey("NUMPAD8", 72));
- keyList.put(73, new VirtualKey("NUMPAD9", 73));
- keyList.put(74, new VirtualKey("SUBTRACT", 74));
- keyList.put(75, new VirtualKey("NUMPAD4", 75));
- keyList.put(76, new VirtualKey("NUMPAD5", 76));
- keyList.put(77, new VirtualKey("NUMPAD6", 77));
- keyList.put(78, new VirtualKey("ADD", 78));
- keyList.put(79, new VirtualKey("NUMPAD1", 79));
- keyList.put(80, new VirtualKey("NUMPAD2", 80));
- keyList.put(81, new VirtualKey("NUMPAD3", 81));
- keyList.put(82, new VirtualKey("NUMPAD0", 82));
- keyList.put(83, new VirtualKey("DECIMAL", 83));
- keyList.put(87, new VirtualKey("F11", 87));
- keyList.put(88, new VirtualKey("F12", 88));
- keyList.put(100, new VirtualKey("F13", 100));
- keyList.put(101, new VirtualKey("F14", 101));
- keyList.put(102, new VirtualKey("F15", 102));
- keyList.put(103, new VirtualKey("F16", 103));
- keyList.put(104, new VirtualKey("F17", 104));
- keyList.put(105, new VirtualKey("F18", 105));
- keyList.put(112, new VirtualKey("KANA", 112));
- keyList.put(113, new VirtualKey("F19", 113));
- keyList.put(121, new VirtualKey("CONVERT", 121));
- keyList.put(123, new VirtualKey("NOCONVERT", 123));
- keyList.put(125, new VirtualKey("YEN", 125));
- keyList.put(141, new VirtualKey("NUMPADEQUALS", 141));
- keyList.put(144, new VirtualKey("CIRCUMFLEX", 144));
- keyList.put(145, new VirtualKey("AT", 145));
- keyList.put(146, new VirtualKey("COLON", 146));
- keyList.put(147, new VirtualKey("UNDERLINE", 147));
- keyList.put(148, new VirtualKey("KANJI", 148));
- keyList.put(149, new VirtualKey("STOP", 149));
- keyList.put(156, new VirtualKey("NUMPADENTER", 156));
- keyList.put(157, new VirtualKey("RCONTROL", 157));
- keyList.put(179, new VirtualKey("NUMPADCOMMA", 179));
- keyList.put(181, new VirtualKey("DIVIDE", 181));
- keyList.put(183, new VirtualKey("PRINT", 183));
- keyList.put(184, new VirtualKey("ALT_GR", 184));
- keyList.put(197, new VirtualKey("PAUSE", 197));
- keyList.put(199, new VirtualKey("HOME", 199));
- keyList.put(200, new VirtualKey("UP", 200));
- keyList.put(201, new VirtualKey("PRIOR", 201));
- keyList.put(203, new VirtualKey("LEFT", 203));
- keyList.put(205, new VirtualKey("RIGHT", 205));
- keyList.put(207, new VirtualKey("END", 207));
- keyList.put(208, new VirtualKey("DOWN", 208));
- keyList.put(209, new VirtualKey("NEXT", 209));
- keyList.put(210, new VirtualKey("INSERT", 210));
- keyList.put(211, new VirtualKey("DELETE", 211));
- keyList.put(219, new VirtualKey("WIN", 219));
- keyList.put(221, new VirtualKey("APPS", 221));
- }
-
- public void add(int keycode) {
- String keyString = Integer.toString(keycode);
-
- keyList.put(keycode, new VirtualKey(keyString, keycode));
- }
-
- public VirtualKey get(int keycode) {
- VirtualKey key = keyList.get(keycode);
- if (key == null) {
- add(keycode);
- return keyList.get(keycode);
- } else
- return key;
- }
-
- public VirtualKey get(String keyname) {
- Collection list = keyList.values();
- VirtualKey out = null;
-
- for (VirtualKey key : list) {
- if (key.getName().equalsIgnoreCase(keyname)) {
- out = key;
- }
- }
- return out;
- }
-
- public List getCurrentPresses() {
- List out = new ArrayList();
-
- keyList.forEach((keycodes, virtualkeys) -> {
- if (keycodes >= 0) {
- if (virtualkeys.isKeyDown()) {
- out.add(virtualkeys.getName());
- }
- }
- });
-
- return out;
- }
-
- public Map getKeyList() {
- return this.keyList;
- }
-
- public void addChar(char charin) {
- charList.add(charin);
- }
-
- public List getCharList() {
- return charList;
- }
-
- public String getCharListAsString() {
- String out="";
- for(char chars: charList) {
- out=out.concat(Character.toString(chars));
+ public VirtualKeyboard(Set pressedKeys, List charList, List subtickList, boolean ignoreFirstUpdate) {
+ super(pressedKeys, subtickList, ignoreFirstUpdate);
+ this.charList = charList;
+ }
+
+ /**
+ * Updates the keyboard, adds a new subtick to this keyboard
+ * @param keycode The keycode of this key
+ * @param keystate The keystate of this key, true for pressed
+ * @param keycharacter The character that is associated with that key. Can change between keyboards or whenever shift is held in combination.
+ */
+ public void update(int keycode, boolean keystate, char keycharacter, boolean repeatEventsEnabled) {
+ if(isParent() && !ignoreFirstUpdate()) {
+ addSubtick(clone());
+ }
+ charList.clear();
+ if (keystate) {
+ addChar(keycharacter, repeatEventsEnabled);
}
- return out;
- }
-
- public void clearCharList() {
- charList.clear();
- }
-
- public void clear() {
- keyList.forEach((keycode, key) -> {
- key.setPressed(false);
- });
- charList.clear();
- }
-
- public List getDifference(VirtualKeyboard keyboardToCompare) {
-
- List eventList = new ArrayList();
-
- keyList.forEach((keycodes, virtualkeys) -> {
-
- VirtualKey keyToCompare = keyboardToCompare.get(keycodes);
+ setPressed(keycode, keystate);
+ }
+
+ public void update(int keycode, boolean keystate, char keycharacter) {
+ update(keycode, keystate, keycharacter, false);
+ }
+
+ @Override
+ public void setPressed(int keycode, boolean keystate) {
+ if (keycode >= 0) { // Keyboard keys always have a keycode larger or equal than 0
+ super.setPressed(keycode, keystate);
+ }
+ }
- if (!virtualkeys.equals(keyToCompare)) {
- if (Keyboard.areRepeatEventsEnabled()) {
- if (isUnicodeInList(keycodes)) {
- return;
- }
- }
- eventList.add(new VirtualKeyboardEvent(keycodes, keyToCompare.isKeyDown(), Character.MIN_VALUE));
- }
-
- });
- keyboardToCompare.charList.forEach(action -> {
- if (Keyboard.areRepeatEventsEnabled()) {
- eventList.add(decodeUnicode(action));
- } else {
- eventList.add(new VirtualKeyboardEvent(0, true, action));
+ /**
+ * Calculates a list of {@link VirtualKeyboardEvent}s to the next peripheral,
+ * including the subticks.
+ *
+ * @see VirtualKeyboard#getDifference(VirtualKeyboard, Queue)
+ *
+ * @param nextKeyboard The keyboard that comes after this one.
+ * If this one is loaded at tick 15, the nextKeyboard should
+ * be the one from tick 16
+ * @param reference The queue to fill. Passed in by reference.
+ */
+ public void getVirtualEvents(VirtualKeyboard nextKeyboard, Queue reference) {
+ if (isParent()) {
+ VirtualKeyboard currentSubtick = this;
+ for(VirtualKeyboard subtick : nextKeyboard.getAll()) {
+ currentSubtick.getDifference(subtick, reference);
+ currentSubtick = subtick;
}
- });
- return eventList;
- }
-
- public char encodeUnicode(int keycode, char character) {
- switch (keycode) {
- case 15: // Tab
- return '\u21A6';
- case 199: // Pos1
- return '\u21E4';
- case 200: // Arrow Up
- return '\u2191';
- case 201: // Next
- return '\u21E7';
- case 203: // Arrow Left
- return '\u2190';
- case 205: // Arrow Right
- return '\u2192';
- case 207: // End
- return '\u21E5';
- case 208: // Arrow Down
- return '\u2193';
- case 209: // Next
- return '\u21E9';
- default:
- return character;
- }
- }
-
- public VirtualKeyboardEvent decodeUnicode(char character) {
- switch (character) {
- case '\b':
- return new VirtualKeyboardEvent(14, true, character);
- case '\u21A6':
- return new VirtualKeyboardEvent(15, true, Character.MIN_VALUE);
- case '\u2907':
- return new VirtualKeyboardEvent(15, false, Character.MIN_VALUE);
- case '\u21E4':
- return new VirtualKeyboardEvent(199, true, Character.MIN_VALUE);
- case '\u2191':
- return new VirtualKeyboardEvent(200, true, Character.MIN_VALUE);
- case '\u21E7':
- return new VirtualKeyboardEvent(201, true, Character.MIN_VALUE);
- case '\u2190':
- return new VirtualKeyboardEvent(203, true, Character.MIN_VALUE);
- case '\u2192':
- return new VirtualKeyboardEvent(205, true, Character.MIN_VALUE);
- case '\u21E5':
- return new VirtualKeyboardEvent(207, true, Character.MIN_VALUE);
- case '\u2193':
- return new VirtualKeyboardEvent(208, true, Character.MIN_VALUE);
- case '\u21E9':
- return new VirtualKeyboardEvent(209, true, Character.MIN_VALUE);
- default:
- return new VirtualKeyboardEvent(0, true, character);
}
}
- public boolean isUnicodeInList(int keycode) {
- switch (keycode) {
- case 14:
- case 15:
- case 199:
- case 200:
- case 201:
- case 203:
- case 205:
- case 207:
- case 208:
- case 209:
- return true;
- default:
- return false;
+ /**
+ * Calculates the difference between 2 keyboards via symmetric difference
+ * and returns a list of the changes between them in form of
+ * {@link VirtualKeyboardEvent}s
+ *
+ * @param nextKeyboard The keyboard that comes after this one.
+ * If this one is loaded at tick 15, the nextKeyboard should
+ * be the one from tick 16
+ * @param reference The queue to fill. Passed in by reference.
+ */
+ public void getDifference(VirtualKeyboard nextKeyboard, Queue reference) {
+ charQueue.addAll(nextKeyboard.charList);
+
+ /* Calculate symmetric difference of keycodes */
+
+ /*
+ Calculate unpressed keys
+ this: W A S
+ next: W S D
+ -------------
+ A <- unpressed
+ */
+ for(int key : pressedKeys) {
+ if (!nextKeyboard.getPressedKeys().contains(key)) {
+ reference.add(new VirtualKeyboardEvent(key, false, Character.MIN_VALUE));
+ }
+ }
+
+ /*
+ Calculate pressed keys
+ next: W S D
+ this: W A S
+ -------------
+ D <- pressed
+ */
+ int lastKey = 0;
+ for(int key : nextKeyboard.getPressedKeys()) {
+ lastKey = key;
+ if (!this.pressedKeys.contains(key)) {
+ reference.add(new VirtualKeyboardEvent(key, true, getOrMinChar(charQueue.poll())));
+ }
+ }
+
+ /*
+ Add the rest of the characters as keyboard events.
+ Also responsible for holding the key and adding a lot of characters in chat.
+
+ The LWJGL Keyboard has a method called "areRepeatEventsEnabled" which returns true, when the user is in a gui.
+ Additionally when a key is held, the Keyboard resends the same keyboard event, in this case to the update method of the VirtualKeyboard.
+
+ What ends up happening is, that the subtickList is filled with multiple characters, which are then converted to keyboard events
+ here.
+
+ However, some functionality like \b or the arrow keys have no associated character, Minecraft instead listens for the keycode.
+ Thats where the "lastKey" comes in. Since we are using a LinkedHashSet, as pressedKeys, we can get the last pressed keycode.
+
+ So, to get the repeat events working, one needs a pressed key and any character.
+
+ */
+ while (!charQueue.isEmpty()) {
+ reference.add(new VirtualKeyboardEvent(lastKey, true, getOrMinChar(charQueue.poll())));
+ }
+
+ }
+
+ private char getOrMinChar(Character charr){
+ if(charr==null){
+ charr = Character.MIN_VALUE;
+ }
+ return charr;
+ }
+
+ /**
+ * Add a character to the {@link #charList}
+ * Null characters will be discarded;
+ * @param character The character to add
+ */
+ public void addChar(char character, boolean repeatEventsEnabled) {
+ if(character != Character.MIN_VALUE || repeatEventsEnabled) {
+ charList.add(character);
+ }
+ }
+
+ @Override
+ public void clear(){
+ super.clear();
+ charList.clear();
+ }
+
+ @Override
+ public String toString() {
+ if (isParent()) {
+ return getAll().stream().map(VirtualKeyboard::toString2).collect(Collectors.joining("\n"));
+ } else {
+ return toString2();
}
}
- @Override
- public VirtualKeyboard clone() {
- return new VirtualKeyboard(keyList, charList);
+ private String toString2(){
+ return String.format("%s;%s", super.toString(), charListToString(charList));
}
- @Override
- public String toString() {
- List stringy = getCurrentPresses();
- String keyString = "";
- if (!stringy.isEmpty()) {
- String seperator = ",";
- for (int i = 0; i < stringy.size(); i++) {
- if (i == stringy.size() - 1) {
- seperator = "";
- }
- keyString = keyString.concat(stringy.get(i) + seperator);
- }
- }
+ private String charListToString(List charList) {
String charString = "";
if (!charList.isEmpty()) {
- for (int i = 0; i < charList.size(); i++) {
- charString = charString.concat(Character.toString(charList.get(i)));
- }
+ charString = charList.stream().map(Object::toString).collect(Collectors.joining());
charString = StringUtils.replace(charString, "\r", "\\n");
charString = StringUtils.replace(charString, "\n", "\\n");
}
-
- return PlaybackSerialiser.SectionsV1.KEYBOARD.getName()+":"+keyString + ";" + charString;
+ return charString;
}
+
+ /**
+ * Clones this VirtualKeyboard without subticks
+ */
+ @Override
+ public VirtualKeyboard clone() {
+ return new VirtualKeyboard(new HashSet<>(this.pressedKeys), new ArrayList<>(this.charList), isIgnoreFirstUpdate());
+ }
+
+ @Override
+ public void copyFrom(VirtualKeyboard keyboard) {
+ super.copyFrom(keyboard);
+ charList.clear();
+ charList.addAll(keyboard.charList);
+ keyboard.charList.clear();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof VirtualKeyboard) {
+ VirtualKeyboard keyboard = (VirtualKeyboard) obj;
+
+ if(charList.size() != keyboard.charList.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < charList.size(); i++) {
+ if(charList.get(i)!=keyboard.charList.get(i)) {
+ return false;
+ }
+ }
+ return super.equals(obj);
+ }
+ return super.equals(obj);
+ }
+
+ /**
+ * @return An immutable {@link #charList}
+ */
+ public List getCharList() {
+ return ImmutableList.copyOf(charList);
+ }
}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboardEvent.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboardEvent.java
deleted file mode 100644
index 90d2a9e0..00000000
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboardEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.minecrafttas.tasmod.virtual;
-
-public class VirtualKeyboardEvent {
- private int keycode;
- private boolean keystate;
- private char character;
- public VirtualKeyboardEvent(int keycode, boolean keystate, char character) {
- this.keycode=keycode;
- this.keystate=keystate;
- this.character=character;
- }
- public int getKeyCode() {
- return keycode;
- }
- public boolean isState() {
- return keystate;
- }
- public char getCharacter() {
- return character;
- }
-
- @Override
- public String toString() {
- return keycode+", "+keystate+", "+character;
- }
-}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java
index 8d3ea7cb..4329fc05 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java
@@ -1,347 +1,265 @@
package com.minecrafttas.tasmod.virtual;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.google.common.collect.Maps;
-import com.minecrafttas.tasmod.playback.PlaybackSerialiser;
+import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent;
-public class VirtualMouse implements Serializable {
+import java.io.Serializable;
+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 {
/**
- *
+ * The direction of the scrollWheel
+ *
+ * If the number is positive or negative depending on scroll direction.
*/
- private static final long serialVersionUID = -5389661329436686190L;
-
- private Map keyList = Maps.newHashMap();
-
- private int scrollwheel = 0;
-
- private int cursorX = 0;
-
- private int cursorY = 0;
-
- public VirtualMouse(Map keyListIn, int scrollwheel, int cursorX, int cursorY, List path) {
- Map copy = new HashMap();
-
- keyListIn.forEach((key, value) -> {
- copy.put(key, value.clone());
- });
- keyList = copy;
-
- this.scrollwheel = scrollwheel;
-
- this.cursorX = cursorX;
-
- this.cursorY = cursorY;
-
- List pathCopy = new ArrayList();
- path.forEach(pathNode -> {
- pathCopy.add(pathNode);
- });
- this.path = pathCopy;
-
- }
-
+ private int scrollWheel;
/**
- * Creates a Keyboard, where the keys are all unpressed
+ * X coordinate of the on-screen cursor, used in GUI screens.
+ * When null, no change to the cursor is applied.
*/
- public VirtualMouse() {
- keyList.put(-101, new VirtualKey("MOUSEMOVED", -101));
- keyList.put(-100, new VirtualKey("LC", -100));
- keyList.put(-99, new VirtualKey("RC", -99));
- keyList.put(-98, new VirtualKey("MC", -98));
- keyList.put(-97, new VirtualKey("MBUTTON4", -97));
- keyList.put(-96, new VirtualKey("MBUTTON5", -96));
- keyList.put(-95, new VirtualKey("MBUTTON6", -95));
- keyList.put(-94, new VirtualKey("MBUTTON7", -94));
- keyList.put(-93, new VirtualKey("MBUTTON8", -93));
- keyList.put(-92, new VirtualKey("MBUTTON9", -92));
- keyList.put(-91, new VirtualKey("MBUTTON10", -91));
- keyList.put(-90, new VirtualKey("MBUTTON11", -90));
- keyList.put(-89, new VirtualKey("MBUTTON12", -89));
- keyList.put(-88, new VirtualKey("MBUTTON13", -88));
- keyList.put(-87, new VirtualKey("MBUTTON14", -87));
- keyList.put(-86, new VirtualKey("MBUTTON15", -86));
- keyList.put(-85, new VirtualKey("MBUTTON16", -85));
-
- this.scrollwheel = 0;
-
- this.cursorX = 0;
-
- this.cursorY = 0;
-
- addPathNode();
- }
-
- public void add(int keycode) {
- String keyString = Integer.toString(keycode);
+ private int cursorX;
+ /**
+ * Y coordinate of the on-screen cursor, used in GUI screens.
+ * When null, no change to the cursor is applied.
+ */
+ private int cursorY;
- keyList.put(keycode, new VirtualKey(keyString, keycode));
+ /**
+ * Creates a mouse with no buttons pressed and no data
+ */
+ public VirtualMouse(){
+ this(new LinkedHashSet<>(), 0, 0, 0, new ArrayList<>(), true);
}
- public VirtualKey get(int keycode) {
- return keyList.get(keycode);
+ /**
+ * Creates a subtick mouse with {@link Subtickable#subtickList} uninitialized
+ * @param pressedKeys The new list of pressed keycodes for this subtickMouse
+ * @param scrollWheel The scroll wheel direction for this subtickMouse
+ * @param cursorX The X coordinate of the cursor for this subtickMouse
+ * @param cursorY The Y coordinate of the cursor for this subtickMouse
+ */
+ public VirtualMouse(Set pressedKeys, int scrollWheel, int cursorX, int cursorY) {
+ this(pressedKeys, scrollWheel, cursorX, cursorY, null);
}
- public VirtualKey get(String keyname) {
- Collection list = keyList.values();
- VirtualKey out = null;
-
- for (VirtualKey key : list) {
- if (key.getName().equalsIgnoreCase(keyname)) {
- out = key;
- }
- }
- return out;
+ /**
+ * Creates a mouse from existing values with
+ * {@link VirtualPeripheral#ignoreFirstUpdate} set to false
+ *
+ * @param pressedKeys The list of {@link #pressedKeys}
+ * @param scrollWheel The {@link #scrollWheel}
+ * @param cursorX The {@link #cursorX}
+ * @param cursorY The {@link #cursorY}
+ * @param subtickList The {@link VirtualPeripheral#subtickList}
+ */
+ public VirtualMouse(Set pressedKeys, int scrollWheel, Integer cursorX, Integer cursorY, List subtickList) {
+ this(pressedKeys, scrollWheel, cursorX, cursorY, subtickList, false);
}
- public List getCurrentPresses() {
- List out = new ArrayList();
-
- keyList.forEach((keycodes, virtualkeys) -> {
- if (keycodes <= 0) {
- if (virtualkeys.isKeyDown()) {
- out.add(virtualkeys.getName());
- }
- }
- });
-
- return out;
+ /**
+ * Creates a mouse from existing values
+ *
+ * @param pressedKeys The list of {@link #pressedKeys}
+ * @param scrollWheel The {@link #scrollWheel}
+ * @param cursorX The {@link #cursorX}
+ * @param cursorY The {@link #cursorY}
+ * @param subtickList The {@link VirtualPeripheral#subtickList}
+ * @param ignoreFirstUpdate Whether the first call to {@link #update(int, boolean, int, Integer, Integer)} should create a new subtick
+ */
+ public VirtualMouse(Set pressedKeys, int scrollWheel, Integer cursorX, Integer cursorY, List subtickList, boolean ignoreFirstUpdate) {
+ super(pressedKeys, subtickList, ignoreFirstUpdate);
+ this.scrollWheel = scrollWheel;
+ this.cursorX = cursorX;
+ this.cursorY = cursorY;
}
- public Map getKeyList() {
- return this.keyList;
+ public void update(int keycode, boolean keystate, int scrollwheel, Integer cursorX, Integer cursorY) {
+ if(isParent() && !ignoreFirstUpdate()) {
+ addSubtick(clone());
+ }
+ setPressed(keycode, keystate);
+ this.scrollWheel = scrollwheel;
+ this.cursorX = cursorX;
+ this.cursorY = cursorY;
}
- public List getDifference(VirtualMouse mouseToCompare) {
- List eventList = new ArrayList();
-
- List path = mouseToCompare.getPath();
-
- if (path.size() != 1) {
- for (int i = 0; i < path.size() - 1; i++) {
- PathNode currentNode = path.get(i);
- PathNode nextNode = path.get(i + 1);
-
- boolean flag = false;
-
- for (VirtualKey key : nextNode.keyList.values()) {
- if (!key.equals(currentNode.keyList.get(key.getKeycode()))) {
- eventList.add(new VirtualMouseEvent(key.getKeycode(), key.isKeyDown(), nextNode.scrollwheel, nextNode.cursorX, nextNode.cursorY));
- flag = true;
- break;
- }
- }
- if (!flag) {
- eventList.add(new VirtualMouseEvent(-101, false, nextNode.scrollwheel, nextNode.cursorX, nextNode.cursorY));
- }
-
- }
- } else {
- keyList.forEach((keycodes, virtualkeys) -> {
-
- VirtualKey keyToCompare = mouseToCompare.get(keycodes);
-
- if (!virtualkeys.equals(keyToCompare)) {
- eventList.add(new VirtualMouseEvent(keycodes, keyToCompare.isKeyDown(), scrollwheel, cursorX, cursorY));
- }
-
- });
+ @Override
+ public void setPressed(int keycode, boolean keystate) {
+ if (keycode < 0) { // Mouse buttons always have a keycode smaller than 0
+ super.setPressed(keycode, keystate);
}
- return eventList;
}
- public boolean isSomethingDown() {
- for (VirtualKey key : keyList.values()) {
- if (key.isKeyDown()) {
- return true;
+ /**
+ * Calculates a list of {@link VirtualMouseEvent}s, when comparing this mouse to
+ * the next mouse in the sequence,
+ * which also includes the subticks.
+ *
+ * @see VirtualMouse#getDifference(VirtualMouse, Queue)
+ *
+ * @param nextMouse The mouse that comes after this one.
+ * If this one is loaded at tick 15, the nextMouse should be
+ * the one from tick 16
+ * @param reference The queue to fill. Passed in by reference.
+ */
+ public void getVirtualEvents(VirtualMouse nextMouse, Queue reference) {
+ if (isParent()) {
+ VirtualMouse currentSubtick = this;
+ for(VirtualMouse subtick : nextMouse.getAll()) {
+ currentSubtick.getDifference(subtick, reference);
+ currentSubtick = subtick;
}
}
- return false;
}
- public boolean isSomethingDown(Map keyList) {
- for (VirtualKey key : keyList.values()) {
- if (key.isKeyDown()) {
- return true;
+ /**
+ * Calculates the difference between 2 mice via symmetric difference
+ * and returns a list of the changes between them in form of
+ * {@link VirtualMouseEvent}s
+ *
+ * @param nextMouse The mouse that comes after this one.
+ * If this one is loaded at tick 15, the nextMouse should be
+ * the one from tick 16
+ * @param reference The queue to fill. Passed in by reference.
+ */
+ public void getDifference(VirtualMouse nextMouse, Queue reference) {
+
+ /*
+ * Checks if pressedKeys are the same...
+ */
+ 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(nextMouse)) {
+ reference.add(new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, nextMouse.scrollWheel, nextMouse.cursorX, nextMouse.cursorY));
}
+ return;
}
- return false;
+ int scrollWheelCopy = nextMouse.scrollWheel;
+ int cursorXCopy = nextMouse.cursorX;
+ int cursorYCopy = nextMouse.cursorY;
+
+ /* Calculate symmetric difference of keycodes */
+
+ /*
+ Calculate unpressed keys
+ this: LC RC
+ next: LC MC
+ -------------
+ RC <- unpressed
+ */
+ for(int keycode : pressedKeys) {
+ if (!nextMouse.getPressedKeys().contains(keycode)) {
+ reference.add(new VirtualMouseEvent(keycode, false, scrollWheelCopy, cursorXCopy, cursorYCopy));
+ scrollWheelCopy = 0;
+ cursorXCopy = 0;
+ cursorYCopy = 0;
+ }
+ };
+
+ /*
+ Calculate pressed keys
+ next: LC MC
+ this: LC RC
+ -------------
+ MC <- pressed
+ */
+ for(int keycode : nextMouse.getPressedKeys()) {
+ if (!this.pressedKeys.contains(keycode)) {
+ reference.add(new VirtualMouseEvent(keycode, true, scrollWheelCopy, cursorXCopy, cursorYCopy));
+ }
+ };
}
@Override
- public VirtualMouse clone() {
- return new VirtualMouse(keyList, scrollwheel, cursorX, cursorY, path);
- }
-
- public void setCursor(int x, int y) {
- cursorX = x;
- cursorY = y;
- }
-
- public void setScrollWheel(int scrollwheel) {
- this.scrollwheel = scrollwheel;
+ public void clear() {
+ super.clear();
+ clearMouseData();
}
-
- List path = new ArrayList();
-
- public List getPath() {
- return path;
+
+ /**
+ * Resets mouse specific data to it's defaults
+ */
+ private void clearMouseData() {
+ scrollWheel = 0;
+ cursorX = 0;
+ cursorY = 0;
}
- public String getPathAsString() {
- String out="";
- for(PathNode node: path) {
- out=out.concat(node.toString());
+
+ @Override
+ public String toString() {
+ if (isParent()) {
+ return getAll().stream().map(VirtualMouse::toString2).collect(Collectors.joining("\n"));
+ } else {
+ return toString2();
}
- return out;
}
-
- public void addPathNode() {
- path.add(new PathNode(keyList, scrollwheel, cursorX, cursorY));
- }
-
- public void resetPath() {
- path.clear();
- addPathNode();
+
+ private String toString2(){
+ return String.format("%s;%s,%s,%s", super.toString(), scrollWheel, cursorX, cursorY);
}
- public class PathNode implements Serializable {
- /**
- *
- */
- private static final long serialVersionUID = -2221602955260299028L;
-
- private Map keyList = Maps.newHashMap();
-
- public int scrollwheel = 0;
-
- public int cursorX = 0;
-
- public int cursorY = 0;
-
- public PathNode(Map keyList, int scrollwheel, int cursorX, int cursorY) {
- Map copy = new HashMap();
-
- keyList.forEach((key, value) -> {
- copy.put(key, value.clone());
- });
- this.keyList = copy;
- this.scrollwheel = scrollwheel;
- this.cursorX = cursorX;
- this.cursorY = cursorY;
- }
-
- public PathNode() {
- keyList.put(-101, new VirtualKey("MOUSEMOVED", -101));
- keyList.put(-100, new VirtualKey("LC", -100));
- keyList.put(-99, new VirtualKey("RC", -99));
- keyList.put(-98, new VirtualKey("MC", -98));
- keyList.put(-97, new VirtualKey("MBUTTON3", -97));
- keyList.put(-96, new VirtualKey("MBUTTON4", -96));
- keyList.put(-95, new VirtualKey("MBUTTON5", -95));
- keyList.put(-94, new VirtualKey("MBUTTON6", -94));
- keyList.put(-93, new VirtualKey("MBUTTON7", -93));
- keyList.put(-92, new VirtualKey("MBUTTON8", -92));
- keyList.put(-91, new VirtualKey("MBUTTON9", -91));
- keyList.put(-90, new VirtualKey("MBUTTON10", -90));
- keyList.put(-89, new VirtualKey("MBUTTON11", -89));
- keyList.put(-88, new VirtualKey("MBUTTON12", -88));
- keyList.put(-87, new VirtualKey("MBUTTON13", -87));
- keyList.put(-86, new VirtualKey("MBUTTON14", -86));
- keyList.put(-85, new VirtualKey("MBUTTON15", -85));
-
- this.scrollwheel = 0;
-
- this.cursorX = 0;
-
- this.cursorY = 0;
- }
-
- @Override
- public String toString() {
- String keyString = "";
- List strings = new ArrayList();
-
- keyList.forEach((keycodes, virtualkeys) -> {
- if (virtualkeys.isKeyDown()) {
- strings.add(virtualkeys.getName());
- }
- });
- if (!strings.isEmpty()) {
- String seperator = ",";
- for (int i = 0; i < strings.size(); i++) {
- if (i == strings.size() - 1) {
- seperator = "";
- }
- keyString = keyString.concat(strings.get(i) + seperator);
- }
- }
- if (keyString.isEmpty()) {
- return "MOUSEMOVED," + scrollwheel + "," + cursorX + "," + cursorY;
- } else {
- return keyString + "," + scrollwheel + "," + cursorX + "," + cursorY;
- }
- }
-
- public VirtualKey get(String keyname) {
- Collection list = keyList.values();
- VirtualKey out = null;
-
- for (VirtualKey key : list) {
- if (key.getName().equalsIgnoreCase(keyname)) {
- out = key;
- }
- }
- return out;
- }
-
+ /**
+ * Clones this VirtualMouse without subticks
+ */
+ @Override
+ public VirtualMouse clone() {
+ return new VirtualMouse(new HashSet<>(this.pressedKeys), scrollWheel, cursorX, cursorY, null, ignoreFirstUpdate());
}
- public void clear() {
- keyList.forEach((keycode, key) -> {
- key.setPressed(false);
- });
- resetPath();
+ @Override
+ public void copyFrom(VirtualMouse mouse) {
+ super.copyFrom(mouse);
+ this.scrollWheel = mouse.scrollWheel;
+ this.cursorX = mouse.cursorX;
+ this.cursorY = mouse.cursorY;
+ mouse.clearMouseData();
}
-
+
@Override
- public String toString() {
- List stringy = getCurrentPresses();
- String keyString = "";
- if (!stringy.isEmpty()) {
- String seperator = ",";
- for (int i = 0; i < stringy.size(); i++) {
- if (i == stringy.size() - 1) {
- seperator = "";
- }
- keyString = keyString.concat(stringy.get(i) + seperator);
- }
- }
- String pathString = "";
- if (!path.isEmpty()) {
- String seperator = "->";
- for (int i = 0; i < path.size(); i++) {
- if (i == path.size() - 1) {
- seperator = "";
- }
- pathString = pathString.concat("[" + path.get(i).toString() + "]" + seperator);
- }
+ public boolean equals(Object obj) {
+ if (obj instanceof VirtualMouse) {
+ VirtualMouse mouse = (VirtualMouse) obj;
+ return super.equals(obj) &&
+ scrollWheel == mouse.scrollWheel &&
+ cursorX == mouse.cursorX &&
+ cursorY == mouse.cursorY;
}
- return PlaybackSerialiser.SectionsV1.MOUSE.getName()+":"+keyString + ";" + pathString;
+ return super.equals(obj);
}
- public void setPath(List path) {
- List pathCopy = new ArrayList();
- path.forEach(pathNode -> {
- pathCopy.add(pathNode);
- });
- this.path = pathCopy;
+ /**
+ * @return {@link #scrollWheel}
+ */
+ public int getScrollWheel() {
+ return scrollWheel;
+ }
+
+ /**
+ * @return {@link #cursorX}
+ */
+ public int getCursorX() {
+ return cursorX;
+ }
+
+ /**
+ * @return {@link #cursorY}
+ */
+ public int getCursorY() {
+ return cursorY;
}
-
}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouseEvent.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouseEvent.java
deleted file mode 100644
index ba912793..00000000
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouseEvent.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.minecrafttas.tasmod.virtual;
-
-/**
- * Template for recording Mouse.next() events.
- *
- * @author ScribbleLP
- *
- */
-public class VirtualMouseEvent {
- private int keycode;
- private boolean state;
- private int scrollwheel;
- private int mouseX;
- private int mouseY;
-
- public VirtualMouseEvent(int keycode, boolean state, int scrollwheel, int mouseX, int mouseY) {
- this.keycode = keycode;
- this.state = state;
- this.scrollwheel = scrollwheel;
- this.mouseX = mouseX;
- this.mouseY = mouseY;
- }
-
- public int getKeyCode() {
- return keycode;
- }
-
- public boolean isState() {
- return state;
- }
-
- public int getScrollwheel() {
- return scrollwheel;
- }
-
- public int getMouseX() {
- return mouseX;
- }
-
- public int getMouseY() {
- return mouseY;
- }
-
- @Override
- public String toString() {
- return keycode+", "+state+", "+scrollwheel+", "+mouseX+", "+mouseY;
- }
-}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualPeripheral.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualPeripheral.java
new file mode 100644
index 00000000..1a5a1633
--- /dev/null
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualPeripheral.java
@@ -0,0 +1,140 @@
+package com.minecrafttas.tasmod.virtual;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import com.minecrafttas.tasmod.virtual.event.VirtualEvent;
+
+/**
+ * Base class for {@link VirtualKeyboard} and {@link VirtualMouse}
+ *
+ * Contains the shared code for keeping track of which buttons are pressed.
+ * This works by storing the keycodes of the buttons in a set, as keycodes are supposed to be unique
+ *
+ * Generating {@link VirtualEvent}s is handled in the child classes.
+ *
+ * @author Scribble
+ */
+public abstract class VirtualPeripheral> extends Subtickable implements Serializable {
+
+ /**
+ * The list of keycodes that are currently pressed on this peripheral.
+ */
+ protected final Set pressedKeys;
+
+ /**
+ * Creates a VirtualPeripheral
+ * @param pressedKeys The {@link #pressedKeys}
+ * @param subtickList The {@link #subtickList}
+ * @param ignoreFirstUpdate The {@link #ignoreFirstUpdate} state
+ */
+ protected VirtualPeripheral(Set pressedKeys, List subtickList, boolean ignoreFirstUpdate) {
+ super(subtickList, ignoreFirstUpdate);
+ this.pressedKeys = pressedKeys;
+ }
+
+ /**
+ * Set the specified keycode to pressed
+ * @param keycode The keycode to check
+ * @param keystate The keystate of the keycode
+ */
+ protected void setPressed(int keycode, boolean keystate) {
+ if (VirtualKeybindings.isKeyCodeAlwaysBlocked(keycode)) { //TODO Maybe a better system?
+ return;
+ }
+ if (keystate)
+ pressedKeys.add(keycode);
+ else
+ pressedKeys.remove(keycode);
+ }
+
+ /**
+ * Set the specified keyname to pressed
+ * @param keyname The keyname to check
+ * @param keystate The keystate of the keyname
+ */
+ public void setPressed(String keyname, boolean keystate) {
+ Integer keycode = VirtualKey.getKeycode(keyname);
+ if (keycode != null) {
+ setPressed(keycode, keystate);
+ }
+ }
+
+ /**
+ * @return A list of all currently pressed keynames
+ */
+ public List getCurrentPresses() {
+ List out = new ArrayList<>();
+ pressedKeys.forEach(keycode -> {
+ out.add(VirtualKey.getName(keycode));
+ });
+ return out;
+ }
+
+ @Override
+ public String toString() {
+ return String.join(",", getCurrentPresses());
+ }
+
+ /**
+ * @return An immutable set of pressed keycodes
+ */
+ public Set getPressedKeys() {
+ return ImmutableSet.copyOf(pressedKeys);
+ }
+
+ /**
+ * If the key is available in {@link #pressedKeys}
+ * @param keycode The keycode in question
+ * @return If the key is pressed
+ */
+ public boolean isKeyDown(int keycode) {
+ return pressedKeys.contains(keycode);
+ }
+
+ /**
+ * If the key is available in {@link #pressedKeys}
+ * @param keyname The keyname in question
+ * @return If the key is pressed
+ */
+ public boolean isKeyDown(String keyname) {
+ return pressedKeys.contains(VirtualKey.getKeycode(keyname));
+ }
+
+ /**
+ * Clears pressed keys and subticks
+ */
+ @Override
+ protected void clear() {
+ pressedKeys.clear();
+ super.clear();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof VirtualPeripheral) {
+ VirtualPeripheral> peripheral = (VirtualPeripheral>) obj;
+ for (Integer keycode : pressedKeys) {
+ if(!peripheral.pressedKeys.contains(keycode)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return super.equals(obj);
+ }
+
+ /**
+ * Copies the data from another virtual peripheral into this peripheral without creating a new object.
+ * @param peripheral The peripheral to move from
+ */
+ protected void copyFrom(T peripheral) {
+ this.pressedKeys.clear();
+ this.pressedKeys.addAll(peripheral.pressedKeys);
+ peripheral.subtickList.clear();
+ peripheral.resetFirstUpdate();
+ }
+}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualSubticks.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualSubticks.java
deleted file mode 100644
index 29efd22a..00000000
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualSubticks.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.minecrafttas.tasmod.virtual;
-
-import java.io.Serializable;
-
-import com.minecrafttas.tasmod.playback.PlaybackSerialiser;
-
-public class VirtualSubticks implements Serializable{
- /**
- *
- */
- private static final long serialVersionUID = -2038332459318568985L;
- private float pitch;
- private float yaw;
-
- public VirtualSubticks() {
- pitch = 0;
- yaw = 0;
- }
-
- public VirtualSubticks(float pitch, float yaw) {
- this.pitch = pitch;
- this.yaw = yaw;
- }
-
- public float getPitch() {
- return pitch;
- }
-
- public float getYaw() {
- return yaw;
- }
-
- @Override
- public VirtualSubticks clone() {
- return new VirtualSubticks(pitch, yaw);
- }
-
- @Override
- public String toString() {
- return PlaybackSerialiser.SectionsV1.CAMERA.getName()+":"+pitch+";"+yaw;
- }
-}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualEvent.java b/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualEvent.java
new file mode 100644
index 00000000..0c2909fd
--- /dev/null
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualEvent.java
@@ -0,0 +1,29 @@
+package com.minecrafttas.tasmod.virtual.event;
+
+public class VirtualEvent {
+ protected final int keycode;
+ protected final boolean keystate;
+
+ public VirtualEvent(int keycode, boolean keystate) {
+ this.keycode = keycode;
+ this.keystate = keystate;
+ }
+
+ public VirtualEvent(VirtualEvent event) {
+ this.keycode = event.keycode;
+ this.keystate = event.keystate;
+ }
+
+ public int getKeyCode() {
+ return keycode;
+ }
+
+ public boolean isState() {
+ return keystate;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s, %s", keycode, keystate);
+ }
+}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualKeyboardEvent.java b/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualKeyboardEvent.java
new file mode 100644
index 00000000..3ad0e7c0
--- /dev/null
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualKeyboardEvent.java
@@ -0,0 +1,37 @@
+package com.minecrafttas.tasmod.virtual.event;
+
+/**
+ * Template for recording {@link org.lwjgl.input.Keyboard#next()} events.
+ *
+ * @author Scribble
+ */
+public class VirtualKeyboardEvent extends VirtualEvent {
+ private final char character;
+
+ public VirtualKeyboardEvent(){
+ this(0, false, Character.MIN_VALUE);
+ }
+
+ public VirtualKeyboardEvent(int keycode, boolean keystate, char character) {
+ super(keycode, keystate);
+ this.character = character;
+ }
+
+ public char getCharacter() {
+ return character;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s, %s", super.toString(), character);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof VirtualKeyboardEvent){
+ VirtualKeyboardEvent e = (VirtualKeyboardEvent) obj;
+ return keycode == e.keycode && keystate == e.keystate && character == e.character;
+ }
+ return super.equals(obj);
+ }
+}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualMouseEvent.java b/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualMouseEvent.java
new file mode 100644
index 00000000..ba57ec4c
--- /dev/null
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/event/VirtualMouseEvent.java
@@ -0,0 +1,49 @@
+package com.minecrafttas.tasmod.virtual.event;
+
+/**
+ * Template for recording {@link org.lwjgl.input.Mouse#next()} events.
+ *
+ * @author Scribble
+ */
+public class VirtualMouseEvent extends VirtualEvent {
+ private final int scrollwheel;
+ private final int cursorX;
+ private final int cursorY;
+
+ public VirtualMouseEvent() {
+ this(0, false, 0, 0, 0);
+ }
+
+ public VirtualMouseEvent(int keycode, boolean state, int scrollwheel, int cursorX, int cursorY) {
+ super(keycode, state);
+ this.scrollwheel = scrollwheel;
+ this.cursorX = cursorX;
+ this.cursorY = cursorY;
+ }
+
+ public int getScrollwheel() {
+ return scrollwheel;
+ }
+
+ public Integer getCursorX() {
+ return cursorX;
+ }
+
+ public Integer getCursorY() {
+ return cursorY;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s, %s, %s, %s", super.toString(), scrollwheel, cursorX, cursorY);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof VirtualMouseEvent) {
+ VirtualMouseEvent e = (VirtualMouseEvent) obj;
+ return keycode == e.keycode && keystate == e.keystate && scrollwheel == e.scrollwheel && cursorX == e.cursorX && cursorY == e.cursorY;
+ }
+ return super.equals(obj);
+ }
+}
diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml
index 36586568..10071c0a 100644
--- a/src/main/resources/log4j.xml
+++ b/src/main/resources/log4j.xml
@@ -11,6 +11,10 @@
onMatch="${sys:tasmod.marker.tickrate:-ACCEPT}" onMismatch="NEUTRAL" />
+
+
diff --git a/src/main/resources/mctcommon.mixin.json b/src/main/resources/mctcommon.mixin.json
index f070f007..ef08c74d 100644
--- a/src/main/resources/mctcommon.mixin.json
+++ b/src/main/resources/mctcommon.mixin.json
@@ -2,7 +2,6 @@
"required": true,
"minVersion": "0.8.5",
"package": "com.minecrafttas.mctcommon.mixin",
- "refmap": "tasmod.refmap.json",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MixinMinecraftServer",
@@ -13,7 +12,6 @@
"MixinMinecraft",
"MixinNetHandlerPlayClient",
"MixinWorldClient",
- "MixinEntityRenderer",
"MixinLocale"
]
}
\ No newline at end of file
diff --git a/src/main/resources/tasmod.mixin.json b/src/main/resources/tasmod.mixin.json
index b91afa5c..efd2d938 100644
--- a/src/main/resources/tasmod.mixin.json
+++ b/src/main/resources/tasmod.mixin.json
@@ -2,7 +2,6 @@
"required": true,
"minVersion": "0.8.5",
"package": "com.minecrafttas.tasmod.mixin",
- "refmap": "tasmod.refmap.json",
"compatibilityLevel": "JAVA_8",
"mixins": [
@@ -27,8 +26,6 @@
//General
"MixinMinecraft",
"MixinTimer",
- "MixinEntityRenderer",
- "MixinGuiScreen",
"MixinInGameHud",
//Savestates
@@ -40,13 +37,14 @@
//Keybinding
"MixinTextfield",
- "fixes.MixinMinecraftFullscreen",
-
//Join and leave game event on the client
"events.MixinGuiMainMenu",
"events.MixinGuiIngame",
//Playbackhooks
+ "playbackhooks.MixinMinecraft",
+ "playbackhooks.MixinEntityRenderer",
+ "playbackhooks.MixinGuiScreen",
"playbackhooks.MixinGameSettings",
"playbackhooks.MixinGuiChat",
"playbackhooks.MixinGuiClickableScrolledSelectionListProxy",
@@ -62,6 +60,7 @@
"shields.MixinTileEntityItemStackRenderer",
//Fixes
+ "fixes.MixinMinecraftFullscreen",
"fixes.MixinNetworkManager"
]
}
\ No newline at end of file
diff --git a/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java b/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java
new file mode 100644
index 00000000..a6aa974b
--- /dev/null
+++ b/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java
@@ -0,0 +1,275 @@
+package tasmod.virtual;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import com.minecrafttas.tasmod.virtual.VirtualCameraAngle;
+
+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 updating a camera but pitch and yaw are null. The update should fail and
+ * pitch and yaw should still be null
+ */
+ @Test
+ void testUpdateWithNull() {
+ float x = 1f;
+ float y = 2f;
+
+ VirtualCameraAngle actual = new VirtualCameraAngle();
+
+ actual.update(x, y);
+
+ assertEquals(null, actual.getPitch());
+ assertEquals(null, actual.getYaw());
+
+ VirtualCameraAngle actual2 = new VirtualCameraAngle(1f, null);
+ actual2.update(x, y);
+
+ assertEquals(null, actual.getPitch());
+ assertEquals(null, actual.getYaw());
+ }
+
+ /**
+ * Test setting the camera
+ */
+ @Test
+ void testSet() {
+ VirtualCameraAngle actual = new VirtualCameraAngle();
+ actual.set(1f, 2f);
+
+ actual.update(1f, 2f);
+
+ assertEquals(2f, actual.getPitch());
+ assertEquals(4f, actual.getYaw());
+ }
+
+ /**
+ * Test getting all states
+ */
+ @Test
+ void testGetStates() {
+ VirtualCameraAngle test = new VirtualCameraAngle();
+ test.set(0f, 0f);
+ test.update(1f, 1f);
+ test.update(1f, 1f);
+ test.update(1f, 1f);
+
+ List actual = new ArrayList<>();
+
+ // Test get states on a subtick, should result in an empty array
+ VirtualCameraAngle test2 = new VirtualCameraAngle(0f, 0f);
+
+ test2.getStates(actual);
+
+ assertTrue(actual.isEmpty());
+
+ actual.clear();
+
+ test.getStates(actual);
+
+ List expected = new ArrayList<>();
+
+ expected.add(new VirtualCameraAngle(1f, 1f));
+ expected.add(new VirtualCameraAngle(2f, 2f));
+ expected.add(new VirtualCameraAngle(3f, 3f));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Test copyfrom method
+ */
+ @Test
+ void copyFrom() {
+ VirtualCameraAngle expected = new VirtualCameraAngle(0f, 0f, true);
+ expected.update(1f, 2f);
+ expected.update(3f, 4f);
+
+ VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true);
+
+ actual.copyFrom(expected);
+
+ // Test pitch and yaw
+ assertEquals(expected.getPitch(), actual.getPitch());
+ assertEquals(expected.getYaw(), actual.getYaw());
+
+ // Test subticks
+ List expected2 = new ArrayList<>();
+ expected2.add(new VirtualCameraAngle(1f, 2f));
+ expected2.add(new VirtualCameraAngle(4f, 6f));
+
+ assertIterableEquals(expected2, actual.getAll());
+ // Test expected subticks being cleared
+
+ assertTrue(expected.getSubticks().isEmpty());
+ }
+
+ /**
+ * Test clearing the camera angle
+ */
+ @Test
+ void testClear() {
+ VirtualCameraAngle actual = new VirtualCameraAngle();
+ actual.set(0f, 0f);
+ actual.update(1f, 1f);
+ actual.update(1f, 1f);
+ actual.update(1f, 1f);
+
+ actual.clear();
+
+ assertNull(actual.getPitch());
+ assertNull(actual.getYaw());
+ assertTrue(actual.getSubticks().isEmpty());
+ }
+
+ /**
+ * 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() {
+
+ // Test pitch being different
+ VirtualCameraAngle test = new VirtualCameraAngle(1f, 4f);
+ VirtualCameraAngle test2 = new VirtualCameraAngle(3f, 4f);
+ assertNotEquals(test, test2);
+
+ // Test yaw being different
+ test = new VirtualCameraAngle(1f, 2f);
+ test2 = new VirtualCameraAngle(1f, 3f);
+ assertNotEquals(test, test2);
+
+ // Test pitch being null
+ test = new VirtualCameraAngle(null, 2f);
+ test2 = new VirtualCameraAngle(1f, 2f);
+ assertNotEquals(test, test2);
+
+ // Test yaw being null
+ test = new VirtualCameraAngle(1f, null);
+ test2 = new VirtualCameraAngle(1f, 2f);
+ assertNotEquals(test, test2);
+
+ // Test mismatched types
+ assertNotEquals(test, new Object());
+ }
+}
diff --git a/src/test/java/tasmod/virtual/VirtualInputTest.java b/src/test/java/tasmod/virtual/VirtualInputTest.java
new file mode 100644
index 00000000..060ce206
--- /dev/null
+++ b/src/test/java/tasmod/virtual/VirtualInputTest.java
@@ -0,0 +1,355 @@
+package tasmod.virtual;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import com.minecrafttas.tasmod.virtual.VirtualCameraAngle;
+import com.minecrafttas.tasmod.virtual.VirtualInput;
+import com.minecrafttas.tasmod.virtual.VirtualKey;
+import com.minecrafttas.tasmod.virtual.VirtualKeyboard;
+import com.minecrafttas.tasmod.virtual.VirtualMouse;
+
+class VirtualInputTest {
+
+ private final Logger LOGGER = LogManager.getLogger("TASmod");
+
+ /**
+ * Test constructor initializing keyboard, mouse and camera_angle
+ */
+ @Test
+ void testConstructor() {
+ VirtualInput virtual = new VirtualInput(LOGGER);
+
+ assertNotNull(virtual.KEYBOARD);
+ assertNotNull(virtual.MOUSE);
+ assertNotNull(virtual.CAMERA_ANGLE);
+ }
+
+ /**
+ * Testing isKeyDown
+ */
+ @Test
+ void testIsKeyDown() {
+ VirtualKeyboard preloadedKeyboard = new VirtualKeyboard();
+ VirtualMouse preloadedMouse = new VirtualMouse();
+ VirtualCameraAngle preloadedCameraAngle = new VirtualCameraAngle(0f, 0f);
+
+ preloadedKeyboard.update(VirtualKey.W.getKeycode(), true, 'w');
+ preloadedMouse.update(VirtualKey.LC.getKeycode(), true, 15, 0, 0);
+ preloadedCameraAngle.update(1f, 2f);
+
+ VirtualInput input = new VirtualInput(LOGGER, preloadedKeyboard, preloadedMouse, preloadedCameraAngle);
+
+ assertTrue(input.isKeyDown(VirtualKey.W.getKeycode()));
+ assertTrue(input.isKeyDown(VirtualKey.LC.getKeycode()));
+ }
+
+ /**
+ * Testing willKeyBeDown
+ */
+ @Test
+ void testWillKeyBeDown() {
+ VirtualInput input = new VirtualInput(LOGGER);
+
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.W.getKeycode(), true, 'w');
+ input.MOUSE.updateNextMouse(VirtualKey.LC.getKeycode(), true, 15, 0, 0);
+
+ assertTrue(input.willKeyBeDown(VirtualKey.W.getKeycode()));
+ assertTrue(input.willKeyBeDown(VirtualKey.LC.getKeycode()));
+ }
+
+ /**
+ * Tests if a keyboard can be preloaded
+ */
+ @Test
+ void testPreloadedConstructor() {
+ VirtualKeyboard preloadedKeyboard = new VirtualKeyboard();
+ VirtualMouse preloadedMouse = new VirtualMouse();
+ VirtualCameraAngle preloadedCameraAngle = new VirtualCameraAngle(0f, 0f);
+
+ preloadedKeyboard.update(VirtualKey.W.getKeycode(), true, 'w');
+ preloadedMouse.update(VirtualKey.LC.getKeycode(), true, 15, 0, 0);
+ preloadedCameraAngle.update(1f, 2f);
+
+
+ VirtualInput virtual = new VirtualInput(LOGGER, preloadedKeyboard, preloadedMouse, preloadedCameraAngle);
+
+ virtual.KEYBOARD.nextKeyboardTick();
+ assertTrue(virtual.KEYBOARD.nextKeyboardSubtick());
+ assertEquals(VirtualKey.W.getKeycode(), virtual.KEYBOARD.getEventKeyboardKey());
+
+ virtual.MOUSE.nextMouseTick();
+ assertTrue(virtual.MOUSE.nextMouseSubtick());
+ assertEquals(VirtualKey.LC.getKeycode(), virtual.MOUSE.getEventMouseKey());
+
+ assertEquals(1f, virtual.CAMERA_ANGLE.getCurrentPitch());
+ assertEquals(2f, virtual.CAMERA_ANGLE.getCurrentYaw());
+ }
+
+ /**
+ * Simulate key presses
+ */
+ @Test
+ void testKeyboardAddPresses() {
+ VirtualInput virtual = new VirtualInput(LOGGER);
+
+ // Simulate pressing keys WAS on the keyboard
+ virtual.KEYBOARD.updateNextKeyboard(VirtualKey.W.getKeycode(), true, 'w');
+ virtual.KEYBOARD.updateNextKeyboard(VirtualKey.A.getKeycode(), true, 'a');
+ virtual.KEYBOARD.updateNextKeyboard(VirtualKey.S.getKeycode(), true, 's');
+
+ // Load the next keyboard events
+ virtual.KEYBOARD.nextKeyboardTick();
+
+ // W
+
+ // Load new subtick
+ assertTrue(virtual.KEYBOARD.nextKeyboardSubtick());
+
+ // Read out values from the subtick
+ assertEquals(VirtualKey.W.getKeycode(), virtual.KEYBOARD.getEventKeyboardKey());
+ assertTrue(virtual.KEYBOARD.getEventKeyboardState());
+ assertEquals('w', virtual.KEYBOARD.getEventKeyboardCharacter());
+
+ // A
+
+ // Load new subtick
+ assertTrue(virtual.KEYBOARD.nextKeyboardSubtick());
+
+ // Read out values from the subtick
+ assertEquals(VirtualKey.A.getKeycode(), virtual.KEYBOARD.getEventKeyboardKey());
+ assertTrue(virtual.KEYBOARD.getEventKeyboardState());
+ assertEquals('a', virtual.KEYBOARD.getEventKeyboardCharacter());
+
+ // S
+
+ // Load new subtick
+ assertTrue(virtual.KEYBOARD.nextKeyboardSubtick());
+
+ // Read out values from the subtick
+ assertEquals(VirtualKey.S.getKeycode(), virtual.KEYBOARD.getEventKeyboardKey());
+ assertTrue(virtual.KEYBOARD.getEventKeyboardState());
+ assertEquals('s', virtual.KEYBOARD.getEventKeyboardCharacter());
+
+ // Check if subtick list is empty
+ assertFalse(virtual.KEYBOARD.nextKeyboardSubtick());
+ }
+
+ /**
+ * Test simulating button removals
+ */
+ @Test
+ void testKeyboardRemovePresses() {
+ VirtualKeyboard preloadedKeyboard = new VirtualKeyboard();
+
+ preloadedKeyboard.update(VirtualKey.W.getKeycode(), true, 'w');
+ VirtualInput virtual = new VirtualInput(LOGGER, preloadedKeyboard, new VirtualMouse(), new VirtualCameraAngle());
+
+ virtual.KEYBOARD.updateNextKeyboard(VirtualKey.W.getKeycode(), false, Character.MIN_VALUE);
+
+ // Load the next keyboard events
+ virtual.KEYBOARD.nextKeyboardTick();
+
+ // Load a new subtick
+ assertTrue(virtual.KEYBOARD.nextKeyboardSubtick());
+
+ // Read out values from the subtick
+ assertEquals(VirtualKey.W.getKeycode(), virtual.KEYBOARD.getEventKeyboardKey());
+ assertFalse(virtual.KEYBOARD.getEventKeyboardState());
+ assertEquals(Character.MIN_VALUE, virtual.KEYBOARD.getEventKeyboardCharacter());
+
+ // Check if subtick list is empty
+ assertFalse(virtual.KEYBOARD.nextKeyboardSubtick());
+ }
+
+ /**
+ * Test simulating mouse presses
+ */
+ @Test
+ void testMousePresses() {
+ VirtualInput virtual = new VirtualInput(LOGGER);
+
+ // Simulate mouse presses
+ virtual.MOUSE.updateNextMouse(VirtualKey.LC.getKeycode(), true, 15, 10, 20);
+ virtual.MOUSE.updateNextMouse(VirtualKey.MC.getKeycode(), true, -15, 30, 21);
+
+ // Load the next mouse events
+ virtual.MOUSE.nextMouseTick();
+
+ // LC
+
+ // Load the new subtick
+ assertTrue(virtual.MOUSE.nextMouseSubtick());
+
+ //Read out the values from the subtick
+ assertEquals(VirtualKey.LC.getKeycode(), virtual.MOUSE.getEventMouseKey());
+ assertTrue(virtual.MOUSE.getEventMouseState());
+ assertEquals(15, virtual.MOUSE.getEventMouseScrollWheel());
+ assertEquals(10, virtual.MOUSE.getNormalizedCursorX());
+ assertEquals(20, virtual.MOUSE.getNormalizedCursorY());
+
+ // MC
+
+ // Load new subtick
+ assertTrue(virtual.MOUSE.nextMouseSubtick());
+
+ //Read out the values from the subtick
+ assertEquals(VirtualKey.MC.getKeycode(), virtual.MOUSE.getEventMouseKey());
+ assertTrue(virtual.MOUSE.getEventMouseState());
+ assertEquals(-15, virtual.MOUSE.getEventMouseScrollWheel());
+ assertEquals(30, virtual.MOUSE.getNormalizedCursorX());
+ assertEquals(21, virtual.MOUSE.getNormalizedCursorY());
+
+ // Check if subtick list is empty
+ assertFalse(virtual.MOUSE.nextMouseSubtick());
+ }
+
+ /**
+ * Test removing mouse presses
+ */
+ @Test
+ void testMouseRemovePresses() {
+ VirtualMouse preloadedMouse = new VirtualMouse();
+ preloadedMouse.update(VirtualKey.LC.getKeycode(), true, 15, 10, 20);
+
+ // Load preloaded mouse
+ VirtualInput virtual = new VirtualInput(LOGGER, new VirtualKeyboard(), preloadedMouse, new VirtualCameraAngle());
+
+ // Unpress LC
+ virtual.MOUSE.updateNextMouse(VirtualKey.LC.getKeycode(), false, 10, 20, 30);
+
+ // Load the next mouse events
+ virtual.MOUSE.nextMouseTick();
+
+ // Load new subtick
+ assertTrue(virtual.MOUSE.nextMouseSubtick());
+
+ assertEquals(VirtualKey.LC.getKeycode(), virtual.MOUSE.getEventMouseKey());
+ assertFalse(virtual.MOUSE.getEventMouseState());
+ assertEquals(10, virtual.MOUSE.getEventMouseScrollWheel());
+ assertEquals(20, virtual.MOUSE.getNormalizedCursorX());
+ assertEquals(30, virtual.MOUSE.getNormalizedCursorY());
+
+ // Check if subtick list is empty
+ assertFalse(virtual.MOUSE.nextMouseSubtick());
+ }
+
+ /**
+ * Test camera angle on tick
+ */
+ @Test
+ void testCurrentCameraAngles() {
+ VirtualInput virtual = new VirtualInput(LOGGER);
+
+ virtual.CAMERA_ANGLE.setCamera(0f, 0f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 20f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(20f, 30f);
+
+ virtual.CAMERA_ANGLE.nextCameraTick();
+
+ assertEquals(30f, virtual.CAMERA_ANGLE.getCurrentPitch());
+ assertEquals(50f, virtual.CAMERA_ANGLE.getCurrentYaw());
+ }
+
+ /**
+ * Test interpolation but with no playback running. Returns the valuies of nextCameraAngle
+ */
+ @Test
+ void testInterpolationDisabled(){
+ VirtualInput virtual = new VirtualInput(LOGGER);
+
+ virtual.CAMERA_ANGLE.setCamera(0f, 0f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 20f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(20f, 30f);
+
+ Triple expected = Triple.of(30f, 50f+180f, 0f);
+ Triple actual = virtual.CAMERA_ANGLE.getInterpolatedState(0f, 1f, 2f, false);
+
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Test interpolation but with playback running.
+ */
+ @Test
+ void testInterpolationEnabled(){
+ VirtualInput virtual = new VirtualInput(LOGGER);
+
+ virtual.CAMERA_ANGLE.setCamera(0f, 0f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(0f, 0f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+
+ virtual.CAMERA_ANGLE.nextCameraTick();
+
+ Triple expected = Triple.of(0f, 0f, 0f);
+ Triple actual = virtual.CAMERA_ANGLE.getInterpolatedState(0f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(10f, 10f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.1f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(10f, 10f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.199f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(20f, 20f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.2f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(30f, 30f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.3f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(40f, 40f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.4f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(50f, 50f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.5f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(60f, 60f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.6f, 0f, 0f, true);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Test interpolation but with playback running, but there are only 2 values
+ */
+ @Test
+ @Disabled
+ void testInterpolationEnabledLegacy(){
+ VirtualInput virtual = new VirtualInput(LOGGER);
+
+ virtual.CAMERA_ANGLE.setCamera(0f, 0f);
+
+ virtual.CAMERA_ANGLE.updateNextCameraAngle(10f, 10f);
+
+ virtual.CAMERA_ANGLE.nextCameraTick();
+
+ Triple expected = Triple.of(0f, 0f, 0f);
+ Triple actual = virtual.CAMERA_ANGLE.getInterpolatedState(0f, 0f, 0f, true);
+ assertEquals(expected, actual);
+
+ expected = Triple.of(10f, 10f, 0f);
+ actual = virtual.CAMERA_ANGLE.getInterpolatedState(0.3f, 0f, 0f, true);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/src/test/java/tasmod/virtual/VirtualKeyboardTest.java b/src/test/java/tasmod/virtual/VirtualKeyboardTest.java
new file mode 100644
index 00000000..2ce164b7
--- /dev/null
+++ b/src/test/java/tasmod/virtual/VirtualKeyboardTest.java
@@ -0,0 +1,410 @@
+package tasmod.virtual;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.junit.jupiter.api.Test;
+
+import com.minecrafttas.tasmod.virtual.VirtualKey;
+import com.minecrafttas.tasmod.virtual.VirtualKeyboard;
+import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent;
+
+class VirtualKeyboardTest {
+
+ /**
+ * Test the empty constructor
+ */
+ @Test
+ void testEmptyConstructor(){
+ VirtualKeyboard actual = new VirtualKeyboard();
+ assertTrue(actual.getPressedKeys().isEmpty());
+ assertTrue(actual.getCharList().isEmpty());
+ assertTrue(actual.isParent());
+ }
+
+ /**
+ * Test constructor with premade keycode sets
+ */
+ @Test
+ void testSubtickConstructor(){
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+
+ List testCharList = new ArrayList<>();
+ testCharList.add('w');
+ testCharList.add('s');
+
+ VirtualKeyboard actual = new VirtualKeyboard(testKeycodeSet, testCharList);
+
+ assertIterableEquals(testKeycodeSet, actual.getPressedKeys());
+ assertIterableEquals(testCharList, actual.getCharList());
+ assertFalse(actual.isParent());
+ }
+
+ /**
+ * Test setting the keycodes via setPressed to "pressed"
+ */
+ @Test
+ void testSetPressedByKeycode(){
+ VirtualKeyboard actual = new VirtualKeyboard();
+ actual.setPressed(VirtualKey.W.getKeycode(), true);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.W.getKeycode()), actual.getPressedKeys());
+ assertTrue(actual.isParent());
+ }
+
+ /**
+ * Test setting the keycodes via setPressed to "pressed"
+ */
+ @Test
+ void testFailingSetPressedByKeycode(){
+ VirtualKeyboard actual = new VirtualKeyboard();
+ actual.setPressed(VirtualKey.LC.getKeycode(), true);
+
+ assertTrue(actual.getPressedKeys().isEmpty());
+ assertTrue(actual.isParent());
+ }
+
+ /**
+ * Test setting the keynames via setPressed to "pressed"
+ */
+ @Test
+ void testSetPressedByKeyname(){
+ VirtualKeyboard actual = new VirtualKeyboard();
+ actual.setPressed("W", true);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.W.getKeycode()), actual.getPressedKeys());
+ assertTrue(actual.isParent());
+ }
+
+ /**
+ * Test setting the keycodes via setPressed to "unpressed"
+ */
+ @Test
+ void testSetUnPressedByKeycode(){
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+ VirtualKeyboard actual = new VirtualKeyboard(testKeycodeSet, new ArrayList<>());
+ actual.setPressed(VirtualKey.W.getKeycode(), false);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.S.getKeycode()), actual.getPressedKeys());
+ }
+
+ /**
+ * Test setting the keynames via setPressed to "unpressed"
+ */
+ @Test
+ void testSetUnPressedByKeyname(){
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+ VirtualKeyboard actual = new VirtualKeyboard(testKeycodeSet, new ArrayList<>());
+ actual.setPressed("S", false);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.W.getKeycode()), actual.getPressedKeys());
+ }
+
+ /**
+ * Test adding a character to the keyboard
+ */
+ @Test
+ void testAddCharacter(){
+ VirtualKeyboard actual = new VirtualKeyboard();
+ actual.addChar('w', false);
+
+ assertIterableEquals(Arrays.asList('w'), actual.getCharList());
+ }
+
+ /**
+ * Test the toString method without subticks
+ */
+ @Test
+ void testToString(){
+ Set testKeycodeSet = new LinkedHashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+
+ List testCharList = new ArrayList<>();
+ testCharList.add('w');
+ testCharList.add('s');
+
+ VirtualKeyboard actual = new VirtualKeyboard(testKeycodeSet, testCharList);
+ VirtualKeyboard actual2 = new VirtualKeyboard(testKeycodeSet, new ArrayList<>());
+
+ assertEquals("W,S;ws", actual.toString());
+ assertEquals("W,S;", actual2.toString());
+ }
+
+ /**
+ * Test the toString method with subticks
+ */
+ @Test
+ void testToStringSubticks(){
+ VirtualKeyboard actual = new VirtualKeyboard();
+
+ actual.update(VirtualKey.W.getKeycode(), true, 'w');
+ actual.update(VirtualKey.S.getKeycode(), true, 's');
+
+ assertEquals("W;w\nW,S;s", actual.toString());
+ }
+
+ /**
+ * Test equals method
+ */
+ @Test
+ void testEquals() {
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+
+ List testCharList = new ArrayList<>();
+ testCharList.add('w');
+ testCharList.add('s');
+
+ VirtualKeyboard actual = new VirtualKeyboard(testKeycodeSet, testCharList);
+ VirtualKeyboard actual2 = new VirtualKeyboard(testKeycodeSet, testCharList);
+
+ assertEquals(actual, actual2);
+ }
+
+ /**
+ * Test where equals will fail
+ */
+ @Test
+ void testNotEquals() {
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+
+ List testCharList = new ArrayList<>();
+ testCharList.add('w');
+ testCharList.add('s');
+
+ List testCharList2 = new ArrayList<>();
+ testCharList2.add('w');
+ testCharList2.add('S');
+
+ List testCharList3 = new ArrayList<>();
+ testCharList3.add('w');
+
+ VirtualKeyboard actual = new VirtualKeyboard(testKeycodeSet, testCharList);
+ VirtualKeyboard test2 = new VirtualKeyboard(testKeycodeSet, testCharList2);
+ VirtualKeyboard test3 = new VirtualKeyboard(testKeycodeSet, testCharList3);
+
+ assertNotEquals(actual, test2);
+ assertNotEquals(actual, test3);
+ assertNotEquals(actual, null);
+ }
+
+ /**
+ * Test cloning the keyboard
+ */
+ @Test
+ void testClone() {
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+
+ List testCharList = new ArrayList<>();
+ testCharList.add('w');
+ testCharList.add('s');
+
+ VirtualKeyboard actual = new VirtualKeyboard(testKeycodeSet, testCharList);
+ VirtualKeyboard test2 = actual.clone();
+
+ assertEquals(actual, test2);
+ }
+
+ /**
+ * Test copy from method
+ */
+ @Test
+ void testCopyFrom(){
+ VirtualKeyboard copyFrom = new VirtualKeyboard();
+ VirtualKeyboard actual = new VirtualKeyboard();
+
+ copyFrom.update(VirtualKey.W.getKeycode(), true, 'w');
+ copyFrom.update(VirtualKey.A.getKeycode(), true, 'a');
+
+ VirtualKeyboard expected = copyFrom.clone();
+
+ actual.update(VirtualKey.S.getKeycode(), true, 's');
+ actual.update(VirtualKey.D.getKeycode(), true, 'd');
+
+ actual.copyFrom(copyFrom);
+
+ assertIterableEquals(expected.getPressedKeys(), actual.getPressedKeys());
+ assertIterableEquals(expected.getCharList(), actual.getCharList());
+
+ assertTrue(copyFrom.getSubticks().isEmpty());
+ assertTrue(copyFrom.getCharList().isEmpty());
+ }
+
+ /**
+ * Test subtick list being filled via update
+ */
+ @Test
+ void testUpdate(){
+ VirtualKeyboard actual = new VirtualKeyboard();
+ actual.update(VirtualKey.W.getKeycode(), true, 'w');
+ actual.update(VirtualKey.A.getKeycode(), true, 'A');
+
+ List expected = new ArrayList<>();
+ expected.add(new VirtualKeyboard(new HashSet(Arrays.asList(VirtualKey.W.getKeycode())), Arrays.asList('w')));
+ expected.add(new VirtualKeyboard(new HashSet(Arrays.asList(VirtualKey.W.getKeycode(), VirtualKey.A.getKeycode())), Arrays.asList('A')));
+
+ assertIterableEquals(expected, actual.getAll());
+ }
+
+ /**
+ * Tests update method on a subtick. Should not add a subtick
+ */
+ @Test
+ void testUpdateOnSubtick() {
+ VirtualKeyboard actual = new VirtualKeyboard(new LinkedHashSet<>(), new ArrayList<>(), null, false);
+
+ actual.update(VirtualKey.W.getKeycode(), true, 'w');
+ }
+
+ /**
+ * Tests getDifference
+ */
+ @Test
+ void testGetDifference(){
+ VirtualKeyboard test = new VirtualKeyboard(new HashSet<>(Arrays.asList(VirtualKey.W.getKeycode())), Arrays.asList('w'));
+ VirtualKeyboard test2 = new VirtualKeyboard(new HashSet<>(Arrays.asList(VirtualKey.W.getKeycode(), VirtualKey.S.getKeycode())), Arrays.asList('S'));
+ Queue actual = new ConcurrentLinkedQueue<>();
+ test.getDifference(test2, actual);
+ Queue expected = new ConcurrentLinkedQueue<>(Arrays.asList(new VirtualKeyboardEvent(VirtualKey.S.getKeycode(), true, 'S')));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests generating virtual events going from an unpressed keyboard to a pressed keyboard state
+ */
+ @Test
+ void testGetVirtualEventsPress() {
+ VirtualKeyboard unpressed = new VirtualKeyboard();
+
+ VirtualKeyboard pressed = new VirtualKeyboard();
+ pressed.update(VirtualKey.W.getKeycode(), true, 'w');
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ unpressed.getVirtualEvents(pressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(new VirtualKeyboardEvent(VirtualKey.W.getKeycode(), true, 'w'));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests generating virtual events going from a pressed keyboard to an unpressed keyboard state
+ */
+ @Test
+ void testGetVirtualEventsUnpress() {
+ VirtualKeyboard unpressed = new VirtualKeyboard();
+
+ VirtualKeyboard pressed = new VirtualKeyboard();
+ pressed.update(VirtualKey.W.getKeycode(), true, 'w');
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ pressed.getVirtualEvents(unpressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(new VirtualKeyboardEvent(VirtualKey.W.getKeycode(), false, Character.MIN_VALUE));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Test clearing the keyboard
+ */
+ @Test
+ void testClear(){
+ VirtualKeyboard pressed = new VirtualKeyboard();
+ pressed.update(VirtualKey.W.getKeycode(), true, 'w');
+ pressed.update(VirtualKey.S.getKeycode(), true, 's');
+ pressed.update(VirtualKey.A.getKeycode(), true, 'a');
+
+ pressed.clear();
+
+ assertTrue(pressed.getPressedKeys().isEmpty());
+ assertTrue(pressed.getSubticks().isEmpty());
+ assertTrue(pressed.getCharList().isEmpty());
+ }
+
+ /**
+ * Tests virtualEvents behaviour on a subtick, should fail
+ */
+ @Test
+ void testGetVirtualEventsOnSubtick() {
+
+ VirtualKeyboard pressed = new VirtualKeyboard(new HashSet<>(), new ArrayList<>(), null, false);
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ pressed.getVirtualEvents(pressed, actual);
+
+ assertTrue(actual.isEmpty());
+ }
+
+ /**
+ * Test repeat events enabled
+ */
+ @Test
+ void testRepeatEvents(){
+ VirtualKeyboard testKb = new VirtualKeyboard();
+
+ int keycode = VirtualKey.BACK.getKeycode();
+
+ // Update the keyboard multiple times with the same value
+ testKb.update(keycode, true, Character.MIN_VALUE, true);
+ testKb.update(keycode, true, Character.MIN_VALUE, true);
+ testKb.update(keycode, true, Character.MIN_VALUE, true);
+
+ Queue actual = new ConcurrentLinkedQueue<>();
+ // Fill "actual" with VirtualKeyboardEvents
+ new VirtualKeyboard().getVirtualEvents(testKb, actual);
+
+ List expected = new ArrayList<>();
+ // Add expected VirtualKeyboardEvents
+ expected.add(new VirtualKeyboardEvent(keycode, true, Character.MIN_VALUE));
+ expected.add(new VirtualKeyboardEvent(keycode, true, Character.MIN_VALUE));
+ expected.add(new VirtualKeyboardEvent(keycode, true, Character.MIN_VALUE));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Same as {@link #testRepeatEvents()} but with repeat events disabled
+ */
+ @Test
+ void testRepeatEventsFail(){
+ VirtualKeyboard testKb = new VirtualKeyboard();
+
+ int keycode = VirtualKey.BACK.getKeycode();
+ // Update the keyboard multiple times with the same value.
+ testKb.update(keycode, true, Character.MIN_VALUE, false);
+ testKb.update(keycode, true, Character.MIN_VALUE, false);
+ testKb.update(keycode, true, Character.MIN_VALUE, false);
+
+ Queue actual = new ConcurrentLinkedQueue<>();
+ // Fill "actual" with VirtualKeyboardEvents
+ new VirtualKeyboard().getVirtualEvents(testKb, actual);
+
+ List expected = new ArrayList<>();
+
+ // Only one keyboard event should be added
+ expected.add(new VirtualKeyboardEvent(keycode, true, Character.MIN_VALUE));
+
+ assertIterableEquals(expected, actual);
+ }
+}
diff --git a/src/test/java/tasmod/virtual/VirtualMouseTest.java b/src/test/java/tasmod/virtual/VirtualMouseTest.java
new file mode 100644
index 00000000..e7dc4b13
--- /dev/null
+++ b/src/test/java/tasmod/virtual/VirtualMouseTest.java
@@ -0,0 +1,387 @@
+package tasmod.virtual;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import com.minecrafttas.tasmod.virtual.VirtualMouse;
+import org.junit.jupiter.api.Test;
+
+import com.minecrafttas.tasmod.virtual.VirtualKey;
+import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent;
+
+class VirtualMouseTest {
+
+ /**
+ * Test the empty constructor
+ */
+ @Test
+ void testEmptyConstructor() {
+ VirtualMouse actual = new VirtualMouse();
+ assertTrue(actual.getPressedKeys().isEmpty());
+ assertEquals(0, actual.getScrollWheel());
+ assertEquals(0, actual.getCursorX());
+ assertEquals(0, actual.getCursorY());
+ assertTrue(actual.isParent());
+ }
+
+ /**
+ * Test constructor with premade keycode sets
+ */
+ @Test
+ void testSubtickConstructor() {
+ Set expected = new HashSet<>();
+ expected.add(VirtualKey.LC.getKeycode());
+ expected.add(VirtualKey.RC.getKeycode());
+
+ VirtualMouse actual = new VirtualMouse(expected, -15, 0, 2);
+
+ assertIterableEquals(expected, actual.getPressedKeys());
+ assertEquals(-15, actual.getScrollWheel());
+ assertEquals(0, actual.getCursorX());
+ assertEquals(2, actual.getCursorY());
+ assertFalse(actual.isParent());
+ }
+
+ /**
+ * Test setting the keycodes via setPressed to "pressed"
+ */
+ @Test
+ void testSetPressedByKeycode(){
+ VirtualMouse actual = new VirtualMouse();
+ actual.setPressed(VirtualKey.LC.getKeycode(), true);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.LC.getKeycode()), actual.getPressedKeys());
+ assertTrue(actual.isParent());
+ }
+
+ /**
+ * Test setting the keynames via setPressed to "pressed"
+ */
+ @Test
+ void testSetPressedByKeyname(){
+ VirtualMouse actual = new VirtualMouse();
+ actual.setPressed("LC", true);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.LC.getKeycode()), actual.getPressedKeys());
+ assertTrue(actual.isParent());
+ }
+
+ /**
+ * Test setting the keycodes via setPressed to "unpressed"
+ */
+ @Test
+ void testSetUnPressedByKeycode(){
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.LC.getKeycode());
+ testKeycodeSet.add(VirtualKey.MBUTTON9.getKeycode());
+ VirtualMouse actual = new VirtualMouse(testKeycodeSet, 0, 0, 0);
+ actual.setPressed(VirtualKey.MBUTTON9.getKeycode(), false);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.LC.getKeycode()), actual.getPressedKeys());
+ }
+
+ /**
+ * Test setting the keynames via setPressed to "unpressed"
+ */
+ @Test
+ void testSetUnPressedByKeyname(){
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.LC.getKeycode());
+ testKeycodeSet.add(VirtualKey.MBUTTON9.getKeycode());
+ VirtualMouse actual = new VirtualMouse(testKeycodeSet, 0, 0, 0);
+ actual.setPressed("MBUTTON9", false);
+
+ assertIterableEquals(Arrays.asList(VirtualKey.LC.getKeycode()), actual.getPressedKeys());
+ }
+
+ /**
+ * Test the toString method without subticks
+ */
+ @Test
+ void testToString(){
+ Set testKeycodeSet = new LinkedHashSet<>();
+ testKeycodeSet.add(VirtualKey.LC.getKeycode());
+ testKeycodeSet.add(VirtualKey.MC.getKeycode());
+
+ VirtualMouse actual = new VirtualMouse(testKeycodeSet, 10, 100, 120);
+
+ assertEquals("LC,MC;10,100,120", actual.toString());
+ }
+
+ /**
+ * Test the toString method with subticks
+ */
+ @Test
+ void testToStringSubtick(){
+ VirtualMouse actual = new VirtualMouse();
+ actual.update(VirtualKey.LC.getKeycode(), true, 10, 100, 120);
+ actual.update(VirtualKey.MC.getKeycode(), true, 0, 12, 3);
+
+ assertEquals("LC;10,100,120\nLC,MC;0,12,3", actual.toString());
+ }
+
+ /**
+ * Test equals method
+ */
+ @Test
+ void testEquals() {
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.W.getKeycode());
+ testKeycodeSet.add(VirtualKey.S.getKeycode());
+
+
+ VirtualMouse actual = new VirtualMouse(testKeycodeSet, -15, 129, 340);
+ VirtualMouse actual2 = new VirtualMouse(testKeycodeSet, -15, 129, 340);
+
+ assertEquals(actual, actual2);
+ }
+
+ /**
+ * Test where equals will fail
+ */
+ @Test
+ void testNotEquals() {
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.LC.getKeycode());
+
+ VirtualMouse actual = new VirtualMouse(testKeycodeSet, -15, 1, 1);
+
+ Set testKeycodeSet2 = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.RC.getKeycode());
+ VirtualMouse test2 = new VirtualMouse(testKeycodeSet2, -15, 1, 1);
+
+
+
+ VirtualMouse test3 = new VirtualMouse(testKeycodeSet, -16, 1, 1);
+
+ VirtualMouse test4 = new VirtualMouse(testKeycodeSet, -15, 2, 1);
+ VirtualMouse test5 = new VirtualMouse(testKeycodeSet, -15, 1, 2);
+
+ assertNotEquals(actual, test2);
+ assertNotEquals(actual, test3);
+ assertNotEquals(actual, test4);
+ assertNotEquals(actual, test5);
+ assertNotEquals(actual, null);
+ }
+
+ /**
+ * Test cloning the mouse
+ */
+ @Test
+ void testClone() {
+ Set testKeycodeSet = new HashSet<>();
+ testKeycodeSet.add(VirtualKey.LC.getKeycode());
+ testKeycodeSet.add(VirtualKey.MC.getKeycode());
+
+ VirtualMouse actual = new VirtualMouse(testKeycodeSet, 10, 3, 2);
+ VirtualMouse test2 = actual.clone();
+
+ assertEquals(actual, test2);
+ }
+
+ /**
+ * Test copyFrom method
+ */
+ @Test
+ void testCopyFrom() {
+ VirtualMouse copyFrom = new VirtualMouse();
+ VirtualMouse actual = new VirtualMouse();
+
+ copyFrom.update(VirtualKey.LC.getKeycode(), true, 0, 0, 0);
+ copyFrom.update(VirtualKey.MOUSEMOVED.getKeycode(), false, 120, 10, 20);
+
+ VirtualMouse expected = copyFrom.clone();
+
+ actual.update(VirtualKey.MBUTTON12.getKeycode(), true, 0,0,0);
+ actual.update(VirtualKey.MOUSEMOVED.getKeycode(), true, -120, -10, -10);
+
+ actual.copyFrom(copyFrom);
+
+ assertIterableEquals(expected.getPressedKeys(), actual.getPressedKeys());
+ assertEquals(expected.getScrollWheel(), actual.getScrollWheel());
+ assertEquals(expected.getCursorX(), actual.getCursorX());
+ assertEquals(expected.getCursorY(), actual.getCursorY());
+
+ assertTrue(copyFrom.getSubticks().isEmpty());
+ assertEquals(0, copyFrom.getScrollWheel());
+ assertEquals(0, copyFrom.getCursorX());
+ assertEquals(0, copyFrom.getCursorY());
+ }
+
+ /**
+ * Test subtick list being filled via update
+ */
+ @Test
+ void testUpdate(){
+ VirtualMouse actual = new VirtualMouse();
+ actual.update(VirtualKey.LC.getKeycode(), true, -30, 118, 42);
+ actual.update(VirtualKey.MOUSEMOVED.getKeycode(), false, 0, 23, 144);
+
+ List expected = new ArrayList<>();
+ expected.add(new VirtualMouse(new HashSet(Arrays.asList(VirtualKey.LC.getKeycode())), -30, 118, 42));
+ expected.add(new VirtualMouse(new HashSet(Arrays.asList(VirtualKey.LC.getKeycode())), 0, 23, 144));
+
+ assertIterableEquals(expected, actual.getAll());
+ }
+
+ /**
+ * Tests getDifference
+ */
+ @Test
+ void testGetDifference(){
+ VirtualMouse test = new VirtualMouse(new HashSet<>(Arrays.asList(VirtualKey.LC.getKeycode())), 15, 0, 0);
+ VirtualMouse test2 = new VirtualMouse(new HashSet<>(Arrays.asList(VirtualKey.LC.getKeycode(), VirtualKey.RC.getKeycode())), 30, 1, 2);
+ Queue actual = new ConcurrentLinkedQueue<>();
+ test.getDifference(test2, actual);
+ Queue expected = new ConcurrentLinkedQueue<>(Arrays.asList(new VirtualMouseEvent(VirtualKey.RC.getKeycode(), true, 30, 1, 2)));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests generating virtual events going from an unpressed mouse to a pressed mouse state
+ */
+ @Test
+ void testGetVirtualEventsPress() {
+ VirtualMouse unpressed = new VirtualMouse();
+
+ VirtualMouse pressed = new VirtualMouse();
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12);
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ unpressed.getVirtualEvents(pressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(new VirtualMouseEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests generating virtual events going from a pressed mouse to an unpressed mouse state
+ */
+ @Test
+ void testGetVirtualEventsUnpress() {
+ VirtualMouse unpressed = new VirtualMouse();
+
+ VirtualMouse pressed = new VirtualMouse();
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12);
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ pressed.getVirtualEvents(unpressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(new VirtualMouseEvent(VirtualKey.LC.getKeycode(), false, 0, 0, 0));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests 2 updates having the same value. Should return no additional button events
+ */
+ @Test
+ void testSameUpdate() {
+ VirtualMouse unpressed = new VirtualMouse();
+
+ VirtualMouse pressed = new VirtualMouse();
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12);
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12);
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ unpressed.getVirtualEvents(pressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(
+ new VirtualMouseEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12) // Should only have one keyboard event
+ );
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests 2 updates having the same pressed keys, but scrollWheel is different
+ */
+ @Test
+ void testScrollWheelDifferent() {
+ VirtualMouse unpressed = new VirtualMouse();
+
+ VirtualMouse pressed = new VirtualMouse();
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12);
+ pressed.update(VirtualKey.LC.getKeycode(), true, -30, 10, 12);
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ unpressed.getVirtualEvents(pressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(
+ new VirtualMouseEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12),
+ new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, -30, 10, 12) // Adds an additional "MOUSEMOVED" event with the scroll wheel
+ );
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests 2 updates having the same pressed keys, but scrollWheel is different
+ */
+ @Test
+ void testCursorXDifferent() {
+ VirtualMouse unpressed = new VirtualMouse();
+
+ VirtualMouse pressed = new VirtualMouse();
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12);
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 11, 12);
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ unpressed.getVirtualEvents(pressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(
+ new VirtualMouseEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12),
+ new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 15, 11, 12) // Adds an additional "MOUSEMOVED" event with the cursorX
+ );
+
+ assertIterableEquals(expected, actual);
+ }
+
+ /**
+ * Tests 2 updates having the same pressed keys, but scrollWheel is different
+ */
+ @Test
+ void testCursorYDifferent() {
+ VirtualMouse unpressed = new VirtualMouse();
+
+ VirtualMouse pressed = new VirtualMouse();
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12);
+ pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 120);
+
+ // Load actual with the events
+ Queue actual = new ConcurrentLinkedQueue<>();
+ unpressed.getVirtualEvents(pressed, actual);
+
+ // Load expected
+ List expected = Arrays.asList(
+ new VirtualMouseEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12),
+ new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 15, 10, 120) // Adds an additional "MOUSEMOVED" event with the cursorY
+ );
+
+ assertIterableEquals(expected, actual);
+ }
+}