diff --git a/build.gradle b/build.gradle index aa03ed92..9a6b90ea 100644 --- a/build.gradle +++ b/build.gradle @@ -19,8 +19,6 @@ loom { // add log4jconfig log4jConfigs.from(file('src/main/resources/log4j.xml')) - // default refmap name - mixin.defaultRefmapName = "tasmod.refmap.json" } // dependency repositories @@ -44,8 +42,8 @@ configurations { dependencies { // tasmod dependencies embed group: 'com.dselent', name: 'bigarraylist', version: '1.1' - compileOnly group: 'com.minecrafttas', name: 'killtherng', version: '2.0' - downloadMod group: 'com.minecrafttas', name: 'killtherng-full', version: '2.0' // for downloadKTRNG task + //compileOnly group: 'com.minecrafttas', name: 'killtherng', version: '2.0' + //downloadMod group: 'com.minecrafttas', name: 'killtherng-full', version: '2.0' // for downloadKTRNG task // loom dependencies minecraft "com.mojang:minecraft:${project.minecraft_version}" @@ -54,12 +52,18 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' } + + // task for downloading KillTheRng -task downloadKTRNG(type: Copy) { - group 'tasmod' - description 'Download KillTheRNG to the run/mods/ folder of the project' - from configurations.downloadMod - into 'run/mods/' +//task downloadKTRNG(type: Copy) { + //group 'tasmod' + //description 'Download KillTheRNG to the run/mods/ folder of the project' + //from configurations.downloadMod + //into 'run/mods/' +//} + +compileJava{ + options.release = 8 } // process fabric mod json diff --git a/gradle.properties b/gradle.properties index 593c074c..59afe362 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,8 +3,8 @@ org.gradle.jvmargs=-Xmx3G # Fabric properties minecraft_version=1.12.2 -loader_version=0.15.3 -loom_version=1.4-SNAPSHOT +loader_version=0.15.6 +loom_version=1.5-SNAPSHOT # Mod properties mod_name=Tool-Assisted Speedrun Mod @@ -16,4 +16,4 @@ mod_email=scribble@minecrafttas.com # TASmod properties group=com.minecrafttas artifact=TASmod-1.12.2 -version=Beta1-SNAPSHOT +version=Beta1.0-SNAPSHOT diff --git a/src/main/java/com/minecrafttas/mctcommon/Configuration.java b/src/main/java/com/minecrafttas/mctcommon/Configuration.java index 8969a073..5d461f89 100644 --- a/src/main/java/com/minecrafttas/mctcommon/Configuration.java +++ b/src/main/java/com/minecrafttas/mctcommon/Configuration.java @@ -10,7 +10,6 @@ import java.util.Map; import java.util.Properties; -import org.apache.commons.io.FileUtils; /** * A very simple configuration class diff --git a/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java b/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java index 2964a517..d1d70caa 100644 --- a/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java +++ b/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java @@ -14,15 +14,19 @@ /** * Keybind manager + * * @author Pancake */ -public abstract class KeybindManager implements EventClientGameLoop { +public class KeybindManager implements EventClientGameLoop { + + private final IsKeyDownFunc defaultFunction; public static class Keybind { private KeyBinding keyBinding; private String category; private Runnable onKeyDown; + private IsKeyDownFunc isKeyDownFunc; /** * Initialize keybind @@ -33,9 +37,22 @@ public static class Keybind { * @param onKeyDown Will be run when the keybind is pressed */ public Keybind(String name, String category, int defaultKey, Runnable onKeyDown) { + this(name, category, defaultKey, onKeyDown, null); + } + + /** + * Initialize keybind with a different "isKeyDown" method + * + * @param name Name of keybind + * @param category Category of keybind + * @param defaultKey Default key of keybind + * @param onKeyDown Will be run when the keybind is pressed + */ + public Keybind(String name, String category, int defaultKey, Runnable onKeyDown, IsKeyDownFunc func) { this.keyBinding = new KeyBinding(name, defaultKey, category); this.category = category; this.onKeyDown = onKeyDown; + this.isKeyDownFunc = func; } } @@ -43,9 +60,13 @@ public Keybind(String name, String category, int defaultKey, Runnable onKeyDown) private List keybindings; /** - * Initialize keybind manager + * Initialize keybind manage + * + * @param defaultFunction The default function used to determine if a keybind is + * down. Can be overridden when registering a new keybind */ - public KeybindManager() { + public KeybindManager(IsKeyDownFunc defaultFunction) { + this.defaultFunction = defaultFunction; this.keybindings = new ArrayList<>(); } @@ -54,12 +75,14 @@ public KeybindManager() { */ @Override public void onRunClientGameLoop(Minecraft mc) { - for (Keybind keybind : this.keybindings) - if (this.isKeyDown(keybind.keyBinding)) + for (Keybind keybind : this.keybindings){ + IsKeyDownFunc keyDown = keybind.isKeyDownFunc != null ? keybind.isKeyDownFunc : defaultFunction; + if(keyDown.isKeyDown(keybind.keyBinding)){ keybind.onKeyDown.run(); - } + } + } - protected abstract boolean isKeyDown(KeyBinding i); + } /** * Register new keybind @@ -80,4 +103,9 @@ public KeyBinding registerKeybind(Keybind keybind) { return keyBinding; } + @FunctionalInterface + public static interface IsKeyDownFunc { + + public boolean isKeyDown(KeyBinding keybind); + } } \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java b/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java index 0cc90dfd..da3a4bce 100644 --- a/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java +++ b/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java @@ -88,7 +88,9 @@ private static HashMap loadJson(InputStream inputStream) { } Gson gson = new Gson(); HashMap template = new HashMap<>(); - HashMap out = (HashMap) gson.fromJson(new InputStreamReader(inputStream), template.getClass()); + + @SuppressWarnings("unchecked") + HashMap out = (HashMap) gson.fromJson(new InputStreamReader(inputStream), template.getClass()); out.forEach((key, value) -> { value = PATTERN.matcher(value).replaceAll("%$1s"); }); diff --git a/src/main/java/com/minecrafttas/mctcommon/mixin/MixinEntityRenderer.java b/src/main/java/com/minecrafttas/mctcommon/mixin/MixinEntityRenderer.java deleted file mode 100644 index 16f7ff61..00000000 --- a/src/main/java/com/minecrafttas/mctcommon/mixin/MixinEntityRenderer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.minecrafttas.mctcommon.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArg; - -import com.minecrafttas.mctcommon.events.EventClient.EventCamera; -import com.minecrafttas.mctcommon.events.EventClient.EventCamera.CameraData; - -import net.minecraft.client.renderer.EntityRenderer; -import net.minecraft.client.renderer.GlStateManager; - -@Mixin(EntityRenderer.class) -public class MixinEntityRenderer { - - private float currentPitch; - - @ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 8), index = 0) - public float redirect_orientCameraPitch(float pitch) { - currentPitch = pitch; - return 0; - } - - @ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 9), index = 0) - public float redirect_orientCameraYawAnimal(float yawAnimal) { - return redirectCam(yawAnimal); - } - - @ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 10), index = 0) - public float redirect_orientCameraYaw(float yaw) { - return redirectCam(yaw); - } - - private float redirectCam(float yaw) { - CameraData data = EventCamera.fireCameraEvent(new CameraData(currentPitch, yaw)); - GlStateManager.rotate(data.pitch, 1.0f, 0.0f, 0.0f); - GlStateManager.rotate(data.roll, 0.0f, 0.0f, 1.0f); - return data.yaw; - } -} diff --git a/src/main/java/com/minecrafttas/mctcommon/server/exception/InvalidPacketException.java b/src/main/java/com/minecrafttas/mctcommon/server/exception/InvalidPacketException.java index ae1fef9e..fa8e389b 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/exception/InvalidPacketException.java +++ b/src/main/java/com/minecrafttas/mctcommon/server/exception/InvalidPacketException.java @@ -1,6 +1,5 @@ package com.minecrafttas.mctcommon.server.exception; -@SuppressWarnings("serial") public class InvalidPacketException extends Exception { public InvalidPacketException() { diff --git a/src/main/java/com/minecrafttas/mctcommon/server/exception/PacketNotImplementedException.java b/src/main/java/com/minecrafttas/mctcommon/server/exception/PacketNotImplementedException.java index 2731c798..95365017 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/exception/PacketNotImplementedException.java +++ b/src/main/java/com/minecrafttas/mctcommon/server/exception/PacketNotImplementedException.java @@ -4,7 +4,6 @@ import com.minecrafttas.mctcommon.server.interfaces.PacketHandlerBase; import com.minecrafttas.mctcommon.server.interfaces.PacketID; -@SuppressWarnings("serial") public class PacketNotImplementedException extends Exception { public PacketNotImplementedException(String msg) { diff --git a/src/main/java/com/minecrafttas/tasmod/TASmod.java b/src/main/java/com/minecrafttas/tasmod/TASmod.java index db32c6a7..9e94df5f 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmod.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmod.java @@ -121,7 +121,7 @@ public void onServerInit(MinecraftServer server) { // Save Loadstate Count File savestateDirectory = new File(server.getDataDirectory() + File.separator + "saves" + File.separator + "savestates" + File.separator); try { - new SavestateTrackerFile(new File(savestateDirectory, server.getFolderName() + "-info.txt")); + new SavestateTrackerFile(new File(savestateDirectory, server.getFolderName() + "-info.txt")); // TODO Ew, remove } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java index 53c950fe..7c96bddb 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java @@ -1,10 +1,20 @@ package com.minecrafttas.tasmod; -import com.minecrafttas.mctcommon.LanguageManager; +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.Level; +import org.lwjgl.input.Keyboard; + import com.minecrafttas.mctcommon.Configuration; import com.minecrafttas.mctcommon.Configuration.ConfigOptions; import com.minecrafttas.mctcommon.KeybindManager; import com.minecrafttas.mctcommon.KeybindManager.Keybind; +import com.minecrafttas.mctcommon.LanguageManager; import com.minecrafttas.mctcommon.events.EventClient.EventClientInit; import com.minecrafttas.mctcommon.events.EventClient.EventOpenGui; import com.minecrafttas.mctcommon.events.EventClient.EventPlayerJoinedClientSide; @@ -12,12 +22,12 @@ import com.minecrafttas.mctcommon.server.Client; import com.minecrafttas.mctcommon.server.PacketHandlerRegistry; import com.minecrafttas.mctcommon.server.Server; -import com.minecrafttas.tasmod.externalGui.InputContainerView; import com.minecrafttas.tasmod.gui.InfoHud; import com.minecrafttas.tasmod.handlers.InterpolationHandler; import com.minecrafttas.tasmod.handlers.LoadingScreenHandler; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; import com.minecrafttas.tasmod.playback.PlaybackSerialiser; import com.minecrafttas.tasmod.savestates.SavestateHandlerClient; @@ -28,6 +38,7 @@ import com.minecrafttas.tasmod.util.ShieldDownloader; import com.minecrafttas.tasmod.virtual.VirtualInput; import com.minecrafttas.tasmod.virtual.VirtualKeybindings; + import net.fabricmc.api.ClientModInitializer; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; @@ -37,15 +48,9 @@ import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.settings.KeyBinding; import net.minecraft.server.MinecraftServer; -import org.apache.logging.log4j.Level; -import org.lwjgl.input.Keyboard; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static com.minecrafttas.tasmod.TASmod.LOGGER; +import net.minecraft.util.text.ChatType; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; public class TASmodClient implements ClientModInitializer, EventClientInit, EventPlayerJoinedClientSide, EventOpenGui{ @@ -83,7 +88,12 @@ public class TASmodClient implements ClientModInitializer, EventClientInit, Even public static SavestateHandlerClient savestateHandlerClient = new SavestateHandlerClient(); public static Client client; - + /** + * The container where all inputs get stored during recording or stored and + * ready to be played back + */ + public static PlaybackControllerClient controller = new PlaybackControllerClient(); + public static void createTASDir() { File tasDir=new File(tasdirectory); if(!tasDir.exists()) { @@ -119,7 +129,7 @@ public void onInitializeClient() { } else { config.reset(ConfigOptions.FileToOpen); } - virtual=new VirtualInput(fileOnStart); + virtual=new VirtualInput(LOGGER); //TODO Move fileOnStart to PlaybackController // Initialize InfoHud hud = new InfoHud(); @@ -130,23 +140,17 @@ public void onInitializeClient() { // Initialize Ticksync ticksyncClient = new TickSyncClient(); // Initialize keybind manager - keybindManager = new KeybindManager() { - - protected boolean isKeyDown(KeyBinding i) { - return VirtualKeybindings.isKeyDownExceptTextfield(i); - }; - - }; + keybindManager = new KeybindManager(VirtualKeybindings::isKeyDownExceptTextfield); // Register event listeners EventListenerRegistry.register(this); - EventListenerRegistry.register(virtual); +// EventListenerRegistry.register(virtual); TODO Remove if unnecessary EventListenerRegistry.register(hud); EventListenerRegistry.register(shieldDownloader); EventListenerRegistry.register(loadingScreenHandler); EventListenerRegistry.register(ticksyncClient); EventListenerRegistry.register(keybindManager); - EventListenerRegistry.register(interpolation); +// EventListenerRegistry.register(interpolation); EventListenerRegistry.register((EventOpenGui)(gui -> { if(gui instanceof GuiMainMenu) { openMainMenuScheduler.runAllTasks(); @@ -156,7 +160,7 @@ protected boolean isKeyDown(KeyBinding i) { // Register packet handlers LOGGER.info(LoggerMarkers.Networking, "Registering network handlers on client"); - PacketHandlerRegistry.register(virtual.getContainer()); //TODO Move container/playbackcontroller out of virtual package + PacketHandlerRegistry.register(controller); PacketHandlerRegistry.register(ticksyncClient); PacketHandlerRegistry.register(tickratechanger); PacketHandlerRegistry.register(savestateHandlerClient); @@ -174,10 +178,11 @@ protected boolean isKeyDown(KeyBinding i) { public void onClientInit(Minecraft mc) { // initialize keybindings List blockedKeybindings = new ArrayList<>(); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Tickrate 0 Key", "TASmod", Keyboard.KEY_F8, () -> TASmodClient.tickratechanger.togglePause()))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Advance Tick", "TASmod", Keyboard.KEY_F9, () -> TASmodClient.tickratechanger.advanceTick()))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Recording/Playback Stop", "TASmod", Keyboard.KEY_F10, () -> TASmodClient.virtual.getContainer().setTASState(TASstate.NONE)))); + blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Tickrate 0 Key", "TASmod", Keyboard.KEY_F8, () -> TASmodClient.tickratechanger.togglePause(), VirtualKeybindings::isKeyDown))); + blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Advance Tick", "TASmod", Keyboard.KEY_F9, () -> TASmodClient.tickratechanger.advanceTick(), VirtualKeybindings::isKeyDown))); + blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Recording/Playback Stop", "TASmod", Keyboard.KEY_F10, () -> TASmodClient.controller.setTASState(TASstate.NONE), VirtualKeybindings::isKeyDown))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Create Savestate", "TASmod", Keyboard.KEY_J, () -> { + Minecraft.getMinecraft().ingameGUI.addChatMessage(ChatType.CHAT, new TextComponentString("Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); try { TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SAVE).writeInt(-1)); } catch (Exception e) { @@ -185,6 +190,7 @@ public void onClientInit(Minecraft mc) { } }))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Load Latest Savestate", "TASmod", Keyboard.KEY_K, () -> { + Minecraft.getMinecraft().ingameGUI.addChatMessage(ChatType.CHAT, new TextComponentString(TextFormatting.RED+"Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); try { TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_LOAD).writeInt(-1)); } catch (Exception e) { @@ -192,17 +198,16 @@ public void onClientInit(Minecraft mc) { } }))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Open InfoGui Editor", "TASmod", Keyboard.KEY_F6, () -> Minecraft.getMinecraft().displayGuiScreen(TASmodClient.hud)))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Buffer View", "TASmod", Keyboard.KEY_NUMPAD0, () -> InputContainerView.startBufferView()))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Various Testing", "TASmod", Keyboard.KEY_F12, () -> { TASmodClient.client.disconnect(); - }))); + }, VirtualKeybindings::isKeyDown))); blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Various Testing2", "TASmod", Keyboard.KEY_F7, () -> { try { TASmodClient.client = new Client("localhost", TASmod.networkingport-1, TASmodPackets.values(), mc.getSession().getProfile().getName(), true); } catch (Exception e) { e.printStackTrace(); } - }))); + }, VirtualKeybindings::isKeyDown))); blockedKeybindings.forEach(VirtualKeybindings::registerBlockedKeyBinding); createTASDir(); @@ -252,7 +257,7 @@ public void onPlayerJoinedClientSide(EntityPlayerSP player) { gameLoopSchedulerClient.add(()->{ try { // connect to server and authenticate - client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), local); //TODO set timeout by tickrate + client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), local); } catch (Exception e) { LOGGER.error("Unable to connect TASmod client: {}", e.getMessage()); e.printStackTrace(); @@ -294,7 +299,7 @@ public GuiScreen onOpenGui(GuiScreen gui) { ticksyncClient.setEnabled(true); } } else if (gui instanceof GuiControls) { - TASmodClient.virtual.getContainer().setTASState(TASstate.NONE); // Set the TASState to nothing to avoid collisions + TASmodClient.controller.setTASState(TASstate.NONE); // Set the TASState to nothing to avoid collisions if (TASmodClient.tickratechanger.ticksPerSecond == 0) { TASmodClient.tickratechanger.pauseClientGame(false); // Unpause the game waszero = true; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java index d4d3f18b..1dc02367 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java @@ -14,7 +14,7 @@ import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; -public class CommandFullPlay extends CommandBase{ +public class CommandFullPlay extends CommandBase { @Override public String getName() { @@ -28,17 +28,18 @@ public String getUsage(ICommandSender sender) { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "This feature doesn't work at the moment!")); try { TASmod.savestateHandlerServer.loadState(0, false, false); } catch (LoadstateException e) { - sender.sendMessage(new TextComponentString(TextFormatting.RED+"Failed to load a savestate: "+e.getMessage())); + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to load a savestate: " + e.getMessage())); return; } catch (Exception e) { - sender.sendMessage(new TextComponentString(TextFormatting.RED+"Failed to load a savestate: "+e.getCause().toString())); + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to load a savestate: " + e.getCause().toString())); e.printStackTrace(); return; } finally { - TASmod.savestateHandlerServer.state=SavestateState.NONE; + TASmod.savestateHandlerServer.state = SavestateState.NONE; } TASmod.playbackControllerServer.setServerState(TASstate.PLAYBACK); try { diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java index 585a50d1..09a1cfe5 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java @@ -28,6 +28,7 @@ public String getUsage(ICommandSender sender) { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + sender.sendMessage(new TextComponentString(TextFormatting.RED+"This feature doesn't work at the moment!")); try { TASmod.savestateHandlerServer.saveState(0, false); } catch (SavestateException e) { diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java index 2f03c713..64b0f027 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java @@ -38,6 +38,7 @@ public List getAliases() { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "This feature doesn't work at the moment!")); if (!(sender instanceof EntityPlayer)) { return; } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java index 56f58e36..6373f781 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java @@ -38,6 +38,7 @@ public List getAliases() { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "This feature doesn't work at the moment!")); if (!(sender instanceof EntityPlayer)) { return; } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java index 7aee1de1..0d997da9 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java @@ -37,6 +37,7 @@ public int getRequiredPermissionLevel() { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + sender.sendMessage(new TextComponentString("Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); if (args.length == 0) { sendHelp(sender); } else if (args.length >= 1) { diff --git a/src/main/java/com/minecrafttas/tasmod/externalGui/InputContainerView.java b/src/main/java/com/minecrafttas/tasmod/externalGui/InputContainerView.java deleted file mode 100644 index 240c155b..00000000 --- a/src/main/java/com/minecrafttas/tasmod/externalGui/InputContainerView.java +++ /dev/null @@ -1,187 +0,0 @@ -package com.minecrafttas.tasmod.externalGui; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Font; -import java.awt.Rectangle; -import java.awt.Toolkit; -import java.util.Vector; - -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.UIManager; -import javax.swing.border.EmptyBorder; -import javax.swing.table.DefaultTableModel; - -import com.minecrafttas.mctcommon.events.EventClient.EventClientTick; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; -import com.minecrafttas.tasmod.virtual.VirtualInput; - -import net.minecraft.client.Minecraft; - -@Deprecated -public class InputContainerView extends JFrame implements EventClientTick{ - - private static final long serialVersionUID = -1823965270972132025L; - private JPanel contentPane; - private static JTable table; - private static DefaultTableModel model; - private static int prevCount = 0; - - /** - * Launch the application. - */ - public static void main(String[] args) { - EventQueue.invokeLater(new Runnable() { - public void run() { - try { - InputContainerView frame = new InputContainerView(); - frame.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - /** - * Create the frame. - */ - public InputContainerView() { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - } - setIconImage(Toolkit.getDefaultToolkit() - .getImage(InputContainerView.class.getResource("/assets/tasmod/textures/potion2.png"))); - setBackground(Color.WHITE); - setTitle("InputContainer View"); - setFont(new Font("Arial", Font.PLAIN, 12)); - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - setBounds(100, 100, 673, 448); - contentPane = new JPanel(); - contentPane.setBackground(Color.WHITE); - contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); - contentPane.setLayout(new BorderLayout(0, 0)); - setContentPane(contentPane); - - JScrollPane scrollPane = new JScrollPane(); - contentPane.add(scrollPane, BorderLayout.CENTER); - - Vector title = new Vector(); - title.add("Ticks"); - title.add("Keyboard"); - title.add("Mouse"); - title.add("CameraX"); - title.add("CameraY"); - - model = new DefaultTableModel(title, 0); - - table = new JTable(model); - table.getColumnModel().getColumn(0).setPreferredWidth(5); - table.setBackground(Color.DARK_GRAY); - table.setForeground(Color.LIGHT_GRAY); - scrollPane.setViewportView(table); - - contentPane.add(table.getTableHeader(), BorderLayout.PAGE_START); - - } - -// private static void addEmptyRow() { -// addRow(0, "", "", 0F, 0F); -// } - - private static void addRow(int ticks, String keyboard, String mouse, float cameraX, float cameraY) { - Vector row = new Vector(5); - row.add(ticks); - row.add(keyboard); - row.add(mouse); - row.add(cameraX); - row.add(cameraY); - model.addRow(row); - } - - private static void selectRow(int ticks) { - if (ticks >= table.getRowCount()) { - ticks = table.getRowCount() - 1; - } - table.setRowSelectionInterval(ticks, ticks); - table.scrollRectToVisible(new Rectangle(table.getCellRect(ticks, 0, true))); - } - - public static void update(VirtualInput input) { - if (model == null) { - return; - } - PlaybackControllerClient container = input.getContainer(); - if (container == null || container.isEmpty()) { - return; - } - if (prevCount != container.size()) { - prevCount = container.size(); - model.getDataVector().clear(); - - for (int i = 0; i < container.size(); i++) { - TickInputContainer tickContainer = container.get(i); - addRow(i + 1, tickContainer.getKeyboard().toString(), tickContainer.getMouse().toString(), - tickContainer.getSubticks().getPitch(), tickContainer.getSubticks().getYaw()); - } - selectRow(container.index()); - } - if (!container.isNothingPlaying()) { - selectRow(container.index()); - } - -// selectRow(container.index()+1); -// input.getAllInputEvents().forEach(inputsevents->{ -// Vector row=new Vector(4); -// row.add(inputsevents.tick); -// -// String kbs=""; -// int count=0; -// for (VirtualKeyboardEvent keyb:inputsevents.keyboardevent) { -// kbs=kbs.concat("["+count+"]{"+keyb.toString()+"} "); -// count++; -// } -// -// row.add(kbs); -// -// count=0; -// String mbe=""; -// for (VirtualMouseEvent mouseb:inputsevents.mouseEvent) { -// mbe=mbe.concat("["+count+"]{"+mouseb.toString()+"} "); -// count++; -// } -// row.add(mbe); -// -// row.add(inputsevents.subticks.toString().replace("Camera:", "")); -// model.addRow(row); -// }); - - } - - public static void startBufferView() { - EventQueue.invokeLater(new Runnable() { - - @Override - public void run() { - try { - InputContainerView frame = new InputContainerView(); - frame.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - @Override - public void onClientTick(Minecraft mc) { - update(TASmodClient.virtual); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java index 715dffd7..7cc413c3 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java @@ -11,7 +11,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.lwjgl.input.Keyboard; -import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; import com.minecrafttas.mctcommon.events.EventClient.EventClientTick; @@ -290,7 +289,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Facing"; - return String.format("%.2f %.2f", InterpolationHandler.rotationYaw, InterpolationHandler.rotationPitch); + return String.format("%.2f %.2f", Minecraft.getMinecraft().player.rotationPitch, Minecraft.getMinecraft().player.rotationYaw); })); title = "cticks"; @@ -324,7 +323,7 @@ public boolean checkInit() { if (Minecraft.getMinecraft().currentScreen == this) { return "State"; } else { - TASstate state = TASmodClient.virtual.getContainer().getState(); + TASstate state = TASmodClient.controller.getState(); ChatFormatting format = ChatFormatting.WHITE; String out = ""; if (state == TASstate.PLAYBACK) { @@ -343,26 +342,26 @@ public boolean checkInit() { } })); - title = "cursor"; - y += 14; - if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); - lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { - if (Minecraft.getMinecraft().currentScreen == this) return "Mouse Position"; - return String.format("Mouse Cursor: " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorX + " " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorY); - })); +// title = "cursor"; +// y += 14; +// if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); +// lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { +// if (Minecraft.getMinecraft().currentScreen == this) return "Mouse Position"; +// return String.format("Mouse Cursor: " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorX + " " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorY); +// })); TODO Remove? - title = "trajectories"; - y += 14; - if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); - lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { - if (Minecraft.getMinecraft().currentScreen == this) return "Trajectories"; - String message = "Invalid Item"; - Vec3d vec = TrajectoriesCalculator.calculate(); - if (vec != null) { - message = String.format("%.3f %.3f %.3f", vec.x, vec.y, vec.z); - } - return String.format("Trajectories: " + message); - })); +// title = "trajectories"; +// y += 14; +// if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); +// lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { +// if (Minecraft.getMinecraft().currentScreen == this) return "Trajectories"; +// String message = "Invalid Item"; +// Vec3d vec = TrajectoriesCalculator.calculate(); +// if (vec != null) { +// message = String.format("%.3f %.3f %.3f", vec.x, vec.y, vec.z); +// } +// return String.format("Trajectories: " + message); +// })); title = "velocity"; y += 14; @@ -377,7 +376,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Desync"; - DesyncMonitoring dMonitor=TASmodClient.virtual.getContainer().desyncMonitor; + DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; return dMonitor.getStatus(Minecraft.getMinecraft().player); })); @@ -386,7 +385,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Desync Motion"; - DesyncMonitoring dMonitor=TASmodClient.virtual.getContainer().desyncMonitor; + DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; return dMonitor.getMotion(); })); @@ -395,7 +394,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Desync Position"; - DesyncMonitoring dMonitor=TASmodClient.virtual.getContainer().desyncMonitor; + DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; return dMonitor.getPos(); })); @@ -404,7 +403,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Desync KTRNG"; - DesyncMonitoring dMonitor=TASmodClient.virtual.getContainer().desyncMonitor; + DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; return dMonitor.getSeed(); })); @@ -414,7 +413,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y, true); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "PlaybackIndex"; - return Integer.toString(TASmodClient.virtual.getContainer().index()); + return Integer.toString(TASmodClient.controller.index()); })); y = height - 14; @@ -437,10 +436,10 @@ public boolean checkInit() { @Override public void onDrawHotbar() { // render custom info box if control byte is set - if (!ControlByteHandler.hideInfoBox && TASmodClient.virtual.getContainer().isPlayingback()) + if (!ControlByteHandler.hideInfoBox && TASmodClient.controller.isPlayingback()) drawRectWithText(ControlByteHandler.text, 10, 10, true); // skip rendering of control byte is set - if (!ControlByteHandler.shouldRenderHud && TASmodClient.virtual.getContainer().isPlayingback()) + if (!ControlByteHandler.shouldRenderHud && TASmodClient.controller.isPlayingback()) return; int xpos=40; int ypos=190; @@ -490,26 +489,26 @@ private void drawRectWithText(String text, int x, int y, boolean rect) { } private String keystrokes() { - if (Display.isActive()) { - String out1 = ""+ChatFormatting.WHITE; - for (String mouse : TASmodClient.virtual.getCurrentMousePresses()) { - out1 = out1.concat(mouse + " "); - } - out1=out1.concat(""+ChatFormatting.GREEN); - for (String mouse : TASmodClient.virtual.getNextMousePresses()) { - out1 = out1.concat(mouse + " "); - } - - String out2 = ""+ChatFormatting.WHITE; - for (String key : TASmodClient.virtual.getCurrentKeyboardPresses()) { - out2 = out2.concat(key + " "); - } - out2=out2.concat(""+ChatFormatting.GREEN); - for (String key : TASmodClient.virtual.getNextKeyboardPresses()) { - out2 = out2.concat(key + " "); - } - return out1+out2; - } +// if (Display.isActive()) { //TODO Update +// String out1 = ""+ChatFormatting.WHITE; +// for (String mouse : TASmodClient.virtual.getCurrentMousePresses()) { +// out1 = out1.concat(mouse + " "); +// } +// out1=out1.concat(""+ChatFormatting.GREEN); +// for (String mouse : TASmodClient.virtual.getNextMousePresses()) { +// out1 = out1.concat(mouse + " "); +// } +// +// String out2 = ""+ChatFormatting.WHITE; +// for (String key : TASmodClient.virtual.getCurrentKeyboardPresses()) { +// out2 = out2.concat(key + " "); +// } +// out2=out2.concat(""+ChatFormatting.GREEN); +// for (String key : TASmodClient.virtual.getNextKeyboardPresses()) { +// out2 = out2.concat(key + " "); +// } +// return out1+out2; +// } return ""; } diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java index 449be61f..69e4df41 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java @@ -1,7 +1,6 @@ package com.minecrafttas.tasmod.handlers; import com.minecrafttas.mctcommon.events.EventClient.EventCamera; -import com.minecrafttas.mctcommon.events.EventClient.EventCamera.CameraData; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.playback.ControlByteHandler; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; @@ -15,6 +14,7 @@ * @author Pancake * */ +@Deprecated public class InterpolationHandler implements EventCamera { public static float rotationPitch = 0f; @@ -22,8 +22,8 @@ public class InterpolationHandler implements EventCamera { @Override public CameraData onCameraEvent(CameraData dataIn) { - if (TASmodClient.virtual.getContainer().isPlayingback() && ControlByteHandler.shouldInterpolate) { - TickInputContainer input = TASmodClient.virtual.getContainer().get(); + if (TASmodClient.controller.isPlayingback() && ControlByteHandler.shouldInterpolate) { + TickInputContainer input = TASmodClient.controller.get(); if (input == null) return dataIn; float nextPitch = input.getSubticks().getPitch(); diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java index 089331e3..92bcb00b 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java @@ -27,7 +27,7 @@ public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventC @Override public void onLaunchIntegratedServer() { LOGGER.debug(LoggerMarkers.Event, "Starting the integrated server"); - PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + PlaybackControllerClient container = TASmodClient.controller; if (!container.isNothingPlaying() && !container.isPaused()) { container.pause(true); } @@ -62,6 +62,8 @@ public void onDoneLoadingWorld() { if (TASmod.getServerInstance() != null) { // Check if a server is running and if it's an integrated server LOGGER.debug(LoggerMarkers.Event, "Finished loading the world on the client"); loadingScreenDelay = 1; + + TASmodClient.virtual.clear(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java index 33778934..0a16d9e9 100644 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java +++ b/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java @@ -1,10 +1,9 @@ package com.minecrafttas.tasmod.ktrng; -import com.minecrafttas.killtherng.custom.EventAnnotations.CaptureRandomness; public class KTRNGMonitor { - @CaptureRandomness(name = "jukeboxRecordDropPosition") +// @CaptureRandomness(name = "jukeboxRecordDropPosition") public static void monitor(long seed, String value) { // System.out.println(String.format("Seed: %s, Value: %s", seed, value)); } diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java index 66d8533b..309644e5 100644 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java @@ -4,8 +4,6 @@ import java.nio.ByteBuffer; -import com.minecrafttas.killtherng.KillTheRNG; -import com.minecrafttas.killtherng.SeedingModes; import com.minecrafttas.mctcommon.events.EventClient.EventPlayerJoinedClientSide; import com.minecrafttas.mctcommon.events.EventServer.EventServerTick; import com.minecrafttas.mctcommon.server.Client.Side; @@ -45,30 +43,30 @@ public KillTheRNGHandler(boolean isLoaded) { this.isLoaded = isLoaded; if (isLoaded) { - KillTheRNG.LOGGER.info("Connection established with TASmod"); - KillTheRNG.isLibrary = true; - KillTheRNG.mode = SeedingModes.TickChange; - - KillTheRNG.annotations.register(new KTRNGMonitor()); +// KillTheRNG.LOGGER.info("Connection established with TASmod"); +// KillTheRNG.isLibrary = true; +// KillTheRNG.mode = SeedingModes.TickChange; +// +// KillTheRNG.annotations.register(new KTRNGMonitor()); } else { LOGGER.info("KillTheRNG doesn't appear to be loaded"); } } public long advanceGlobalSeedServer() { - if (isLoaded()) { - return KillTheRNG.commonRandom.nextSeed(); - } else { +// if (isLoaded()) { +// return KillTheRNG.commonRandom.nextSeed(); +// } else { return 0; - } +// } } public long getGlobalSeedServer() { - if (isLoaded()) { - return KillTheRNG.commonRandom.GlobalServer.getSeed(); - } else { +// if (isLoaded()) { +// return KillTheRNG.commonRandom.GlobalServer.getSeed(); +// } else { return 0; - } +// } } public boolean isLoaded() { @@ -81,9 +79,9 @@ public boolean isLoaded() { */ @Environment(EnvType.CLIENT) public long getGlobalSeedClient() { - if (isLoaded()) - return KillTheRNG.clientRandom.GlobalClient.getSeed(); - else +// if (isLoaded()) +// return KillTheRNG.clientRandom.GlobalClient.getSeed(); +// else return 0; } @@ -95,13 +93,13 @@ public long getGlobalSeedClient() { @Environment(EnvType.CLIENT) public void setGlobalSeedClient(long seedIn) { if (isLoaded()) { - KillTheRNG.clientRandom.setSeedAll(seedIn); +// KillTheRNG.clientRandom.setSeedAll(seedIn); } } public void setGlobalSeedServer(long seedIn) { if (isLoaded()) { - KillTheRNG.commonRandom.setSeedAll(seedIn); +// KillTheRNG.commonRandom.setSeedAll(seedIn); } } @@ -211,7 +209,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws break; case KILLTHERNG_STARTSEED: - TASmodClient.virtual.getContainer().setStartSeed(seed); + TASmodClient.controller.setStartSeed(seed); break; default: diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinEntityRenderer.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinEntityRenderer.java deleted file mode 100644 index 8e8e32d4..00000000 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinEntityRenderer.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.minecrafttas.tasmod.mixin; - -import org.lwjgl.input.Mouse; -import org.lwjgl.opengl.Display; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.At.Shift; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.handlers.InterpolationHandler; -import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.EntityRenderer; -import net.minecraft.util.math.MathHelper; - -@Mixin(EntityRenderer.class) -public class MixinEntityRenderer implements SubtickDuck { - - public double dX = 0; - public double dY = 0; - - @Shadow - private Minecraft mc; - @Shadow - private float smoothCamYaw; - @Shadow - private float smoothCamPitch; - @Shadow - private float smoothCamPartialTicks; - @Shadow - private float smoothCamFilterX; - @Shadow - private float smoothCamFilterY; - - @Inject(method = "updateCameraAndRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/Profiler;startSection(Ljava/lang/String;)V", ordinal = 0, shift = Shift.AFTER)) - public void injectAtStartSection(float partialTicks, long nanoTime, CallbackInfo ci) { - // Calculate sensitivity - float f = this.mc.gameSettings.mouseSensitivity * 0.6F + 0.2F; - float f1 = f * f * f * 8.0F; - - // No Gui - if (this.mc.currentScreen == null) { - mc.mouseHelper.mouseXYChange(); - mc.getTutorial().handleMouse(mc.mouseHelper); - dX += mc.mouseHelper.deltaX; - dY += mc.mouseHelper.deltaY; - } else { - // In the gui - dX = 0; - dY = 0; - } - if (TASmodClient.virtual.getContainer().isPlayingback()) { - dX = 0; - dY = 0; - } else { - // Comment this out to disable interpolation, also comment out @SubscribeEvent - // in InterpolationEvents - if (this.mc.currentScreen == null) { - InterpolationHandler.rotationYaw = ((float) ((double) InterpolationHandler.rotationYaw + (double) mc.mouseHelper.deltaX * f1 * 0.15D)); - InterpolationHandler.rotationPitch = (float) ((double) InterpolationHandler.rotationPitch - (double) mc.mouseHelper.deltaY * f1 * 0.15D); - InterpolationHandler.rotationPitch = MathHelper.clamp(InterpolationHandler.rotationPitch, -90.0F, 90.0F); - } - } - } - - @Redirect(at = @At(value = "FIELD", target = "Lnet/minecraft/client/Minecraft;inGameHasFocus:Z", opcode = Opcodes.GETFIELD, ordinal = 1), method = "updateCameraAndRender") - public boolean stopVanilla(Minecraft mc) { - if (TASmodClient.tickratechanger.ticksPerSecond != 0) { - return false; - } else { - return mc.inGameHasFocus; - } - } - - @Override - public void runSubtick(float partialTicks) { - boolean flag = Display.isActive(); - if (flag && Minecraft.IS_RUNNING_ON_MAC && mc.inGameHasFocus && !Mouse.isInsideWindow()) { - Mouse.setGrabbed(false); - Mouse.setCursorPosition(Display.getWidth() / 2, Display.getHeight() / 2 - 20); - Mouse.setGrabbed(true); - } - - if (mc.inGameHasFocus && flag) { - mc.getTutorial().handleMouse(mc.mouseHelper); - float f = mc.gameSettings.mouseSensitivity * 0.6F + 0.2F; - float f1 = f * f * f * 8.0F; - float f2 = (float) dX * f1; - float f3 = (float) dY * f1; - int i = 1; - - dX = 0; - dY = 0; - - if (mc.gameSettings.invertMouse) { - i = -1; - } - - if (mc.gameSettings.smoothCamera) { - smoothCamYaw += f2; - smoothCamPitch += f3; - float f4 = partialTicks - smoothCamPartialTicks; - smoothCamPartialTicks = partialTicks; - f2 = smoothCamFilterX * f4; - f3 = smoothCamFilterY * f4; - mc.player.turn(f2, f3 * (float) i); - } else { - smoothCamYaw = 0.0F; - smoothCamPitch = 0.0F; - mc.player.turn(f2, f3 * (float) i); - } - TASmodClient.virtual.updateSubtick(mc.player.rotationPitch, mc.player.rotationYaw); - mc.player.rotationPitch = TASmodClient.virtual.getSubtickPitch(); - mc.player.rotationYaw = TASmodClient.virtual.getSubtickYaw(); - InterpolationHandler.rotationPitch = mc.player.rotationPitch; - InterpolationHandler.rotationYaw = 180f + mc.player.rotationYaw; - } - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java index 4401621c..d6acae24 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java @@ -2,8 +2,6 @@ import java.io.IOException; -import org.lwjgl.input.Keyboard; -import org.lwjgl.input.Mouse; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -16,7 +14,6 @@ import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer; -import com.minecrafttas.tasmod.util.Ducks.GuiScreenDuck; import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; import net.minecraft.client.Minecraft; @@ -35,20 +32,7 @@ public abstract class MixinMinecraft { @Inject(method = "runGameLoop", at = @At(value = "HEAD")) public void injectRunGameLoop(CallbackInfo ci) { - TASmodClient.gameLoopSchedulerClient.runAllTasks(); - - while (Keyboard.next()) { - TASmodClient.virtual.updateNextKeyboard(Keyboard.getEventKey(), Keyboard.getEventKeyState(), Keyboard.getEventCharacter()); - } - while (Mouse.next()) { - if(this.currentScreen == null) { - TASmodClient.virtual.updateNextMouse(Mouse.getEventButton(), Mouse.getEventButtonState(), Mouse.getEventDWheel(), Mouse.getEventX(), Mouse.getEventY(), TASmodClient.tickratechanger.ticksPerSecond==0); - } else { - GuiScreenDuck screen = (GuiScreenDuck) currentScreen; - TASmodClient.virtual.updateNextMouse(Mouse.getEventButton(), Mouse.getEventButtonState(), Mouse.getEventDWheel(), screen.calcX(Mouse.getEventX()), screen.calcY(Mouse.getEventY()), TASmodClient.tickratechanger.ticksPerSecond==0); - } - } } // ===================================================================================================================================== @@ -64,9 +48,8 @@ public void injectRunGameLoop(CallbackInfo ci) { @Redirect(method = "runGameLoop", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;runTick()V")) public void redirectRunTick(Minecraft mc) { - TASmodClient.virtual.updateContainer(); if (TASmodClient.tickratechanger.ticksPerSecond != 0) { - ((SubtickDuck) this.entityRenderer).runSubtick(this.isGamePaused ? this.renderPartialTicksPaused : this.timer.renderPartialTicks); + ((SubtickDuck) this.entityRenderer).runUpdate(this.isGamePaused ? this.renderPartialTicksPaused : this.timer.renderPartialTicks); } this.runTick(); TASmodClient.tickSchedulerClient.runAllTasks(); @@ -92,132 +75,24 @@ public void inject_shutdownMinecraftApplet(CallbackInfo ci) { } } - // ===================================================================================================================================== - @Inject(method = "runTick", at = @At(value = "HEAD")) public void injectRunTick(CallbackInfo ci) throws IOException { if (SavestateHandlerServer.wasLoading) { SavestateHandlerServer.wasLoading = false; if(Minecraft.getMinecraft().player!=null) { //The player can be null when loading a savestate and quitting to the main menu - SavestateHandlerServer.playerLoadSavestateEventClient(); + SavestateHandlerServer.playerLoadSavestateEventClient(); // TODO Replace with event } } } - // ===================================================================================================================================== - - @Inject(method = "runTickKeyboard", at = @At(value = "HEAD")) - public void injectRunTickKeyboard(CallbackInfo ci) { - TASmodClient.virtual.updateCurrentKeyboard(); - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;next()Z", remap = false)) - public boolean redirectKeyboardNext() { - return TASmodClient.virtual.nextKeyboardEvent(); - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKey()I", remap = false)) - public int redirectKeyboardGetEventKey() { - return TASmodClient.virtual.getEventKeyboardKey(); - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventCharacter()C", remap = false)) - public char redirectKeyboardGetEventCharacter() { - return TASmodClient.virtual.getEventKeyboardCharacter(); - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;isKeyDown(I)Z", remap = false)) - public boolean redirectIsKeyDown(int keyCode) { - return TASmodClient.virtual.isKeyDown(keyCode); - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKeyState()Z", remap = false)) - public boolean redirectGetEventState() { - return TASmodClient.virtual.getEventKeyboardState(); - } - - // ===================================================================================================================================== - - @Inject(method = "runTickMouse", at = @At(value = "HEAD")) - public void injectRunTickMouse(CallbackInfo ci) { - TASmodClient.virtual.updateCurrentMouseEvents(); - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;next()Z", remap = false)) - public boolean redirectMouseNext() { - return TASmodClient.virtual.nextMouseEvent(); - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButton()I", remap = false)) - public int redirectMouseGetEventButton() { - -// if(!VirtualKeybindings.isKeyCodeAlwaysBlocked(ClientProxy.virtual.getEventMouseKey()-100)) { -// TASmod.ktrngHandler.nextPlayerInput(); // Advance ktrng seed on player input -// } - return TASmodClient.virtual.getEventMouseKey() + 100; - } - - // ===================================================================================================================================== - - @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButtonState()Z", remap = false)) - public boolean redirectGetEventButtonState() { - return TASmodClient.virtual.getEventMouseState(); - } - - // ===================================================================================================================================== - @ModifyConstant(method = "runTickMouse", constant = @Constant(longValue = 200L)) public long fixMouseWheel(long twohundredLong) { return (long) Math.max(4000F / TASmodClient.tickratechanger.ticksPerSecond, 200L); } - // ===================================================================================================================================== - - @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventDWheel()I", remap = false)) - public int redirectGetEventDWheel() { - return TASmodClient.virtual.getEventMouseScrollWheel(); - } - - // ===================================================================================================================================== - - @Redirect(method = "dispatchKeypresses", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKey()I", remap = false)) - public int redirectGetEventKeyDPK() { - return TASmodClient.virtual.getEventKeyboardKey(); - } - - // ===================================================================================================================================== - - @Redirect(method = "dispatchKeypresses", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventCharacter()C", remap = false)) - public char redirectGetEventCharacterDPK() { - return TASmodClient.virtual.getEventKeyboardCharacter(); - } - - // ===================================================================================================================================== - - @Redirect(method = "dispatchKeypresses", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKeyState()Z", remap = false)) - public boolean redirectGetEventKeyStateDPK() { - return TASmodClient.virtual.getEventKeyboardState(); - } - - // ===================================================================================================================================== - @Inject(method = "runTick", at = @At(value = "RETURN")) public void injectRunTickReturn(CallbackInfo ci) { - TASmodClient.virtual.getContainer().nextTick(); + TASmodClient.controller.nextTick(); // TODO Replace with event } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/fixes/MixinMinecraftFullscreen.java b/src/main/java/com/minecrafttas/tasmod/mixin/fixes/MixinMinecraftFullscreen.java index 0e23b416..5a1104c4 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/fixes/MixinMinecraftFullscreen.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/fixes/MixinMinecraftFullscreen.java @@ -15,11 +15,11 @@ public class MixinMinecraftFullscreen { @Shadow - public GameSettings gameSettings; + private GameSettings gameSettings; @Inject(method = "toggleFullscreen", at = @At("RETURN")) public void inject_toggleFullscreen(CallbackInfo ci) { int keyF11=this.gameSettings.keyBindFullscreen.getKeyCode(); - TASmodClient.virtual.getNextKeyboard().get(keyF11).setPressed(false); + TASmodClient.virtual.KEYBOARD.updateNextKeyboard(keyF11, false, Character.MIN_VALUE); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java new file mode 100644 index 00000000..ee008ef1 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java @@ -0,0 +1,184 @@ +package com.minecrafttas.tasmod.mixin.playbackhooks; + +import org.apache.commons.lang3.tuple.Triple; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalFloatRef; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; +import com.minecrafttas.tasmod.virtual.VirtualInput; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.client.renderer.GlStateManager; + +/** + * Redirects the camera to use {@link VirtualInput.VirtualCameraAngleInput}.
+ * To support handling the camera in TASes and to avoid desyncs via lag,
+ * it was decided to only update the camera every tick.
+ *
+ * To achieve this, some parts of the vanilla code were disabled, but get called every tick in {@link #runUpdate(float)} + * + * @author Scribble, Pancake + */ +@Mixin(EntityRenderer.class) +public class MixinEntityRenderer implements SubtickDuck { + + @Shadow + private Minecraft mc; + @Shadow + private float smoothCamYaw; + @Shadow + private float smoothCamPitch; + @Shadow + private float smoothCamPartialTicks; + @Shadow + private float smoothCamFilterX; + @Shadow + private float smoothCamFilterY; + + /** + * Injects into the vanilla camera updating cycle, runs every frame. + * Updates {@link com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput#nextCameraAngle} + * @param partialTicks The partial ticks of the timer, unused + * @param nanoTime The nanoTime, unused + * @param ci CBI + */ + @Inject(method = "updateCameraAndRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/Profiler;startSection(Ljava/lang/String;)V", ordinal = 0, shift = At.Shift.AFTER)) + public void playback_injectAtStartSection(float partialTicks, long nanoTime, CallbackInfo ci) { + // Calculate sensitivity + float mouseSensititvity = this.mc.gameSettings.mouseSensitivity * 0.6F + 0.2F; + float mouseSensitivityCubed = mouseSensititvity * mouseSensititvity * mouseSensititvity * 8.0F; + + if (this.mc.currentScreen == null && !TASmodClient.controller.isPlayingback() && mc.player != null) { + mc.mouseHelper.mouseXYChange(); + float deltaPitch = mc.mouseHelper.deltaY * mouseSensitivityCubed; + float deltaYaw = mc.mouseHelper.deltaX * mouseSensitivityCubed; + + int invertMouse = 1; + if (this.mc.gameSettings.invertMouse) { + invertMouse = -1; + } + + if (this.mc.gameSettings.smoothCamera) { + this.smoothCamPitch += deltaPitch; + this.smoothCamYaw += deltaYaw; + float partialSensitivity = mouseSensititvity - this.smoothCamPartialTicks; + this.smoothCamPartialTicks = mouseSensititvity; + deltaPitch = this.smoothCamFilterY * partialSensitivity; + deltaYaw = this.smoothCamFilterX * partialSensitivity; + } else { + this.smoothCamYaw = 0.0F; + this.smoothCamPitch = 0.0F; + } + + mc.getTutorial().handleMouse(mc.mouseHelper); + TASmodClient.virtual.CAMERA_ANGLE.updateNextCameraAngle((float) -((double)deltaPitch * 0.15D * invertMouse), (float) ((double)deltaYaw * 0.15D)); + } + } + + @Redirect(method = "updateCameraAndRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/entity/EntityPlayerSP;turn(FF)V")) + public void playback_stopVanilla(EntityPlayerSP player, float deltaYaw, float deltaPitch){ + if(TASmodClient.tickratechanger.ticksPerSecond == 0){ + player.turn(deltaYaw, deltaPitch); + } + } + + /** + * {@inheritDoc} + * Runs every tick + * @see VirtualInput.VirtualCameraAngleInput#nextCameraTick() + * @param partialTicks The partial ticks from the vanilla Minecraft timer + */ + @Override + public void runUpdate(float partialTicks) { + if(mc.player == null){ + return; + } + // Update the currentCameraAngle + TASmodClient.virtual.CAMERA_ANGLE.nextCameraTick(); + + // Store current rotation to be used as prevRotationPitch/Yaw + float prevPitch = mc.player.rotationPitch; + float prevYaw = mc.player.rotationYaw; + + // Get the new pitch from the virtual input + Float newPitch = TASmodClient.virtual.CAMERA_ANGLE.getCurrentPitch(); + Float newYaw = TASmodClient.virtual.CAMERA_ANGLE.getCurrentYaw(); + + // If the pitch or yaw is null (usually on initialize or when the player joins the world), + // set nextCameraAngle to the current absolute camera coordinates. + // This ensures that the camera position is loaded correctly + if(newPitch == null || newYaw == null) { + TASmodClient.virtual.CAMERA_ANGLE.setCamera(prevPitch, prevYaw); + return; + } + + // Update the rotation of the player + mc.player.rotationPitch = newPitch; + mc.player.rotationYaw = newYaw; + + // Update the previous rotation of the player + mc.player.prevRotationPitch = prevPitch; + mc.player.prevRotationYaw = prevYaw; + } + + /** + * Redirects applying the pitch to the camera. + * @param pitch Original pitch of the camera + * @param sharedPitch MixinExtras parameter for sharing values between mixins + * @return 0f for disabeling this method + */ + @ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 8), index = 0) + public float redirect_orientCameraPitch(float pitch, @Share("pitch") LocalFloatRef sharedPitch) { + sharedPitch.set(pitch); + return 0f; + } + + /** + * Redirects applying the yaw to the animal camera + * @param yawAnimal Original yaw of the animal camera + * @param sharedPitch MixinExtras parameter for sharing values between mixins + * @return The redirected yaw + */ + @ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 9), index = 0) + public float redirect_orientCameraYawAnimal(float yawAnimal, @Share("pitch") LocalFloatRef sharedPitch) { + return redirectCam(sharedPitch.get(), yawAnimal); + } + + /** + * Redirects applying the yaw to the camera + * @param yaw Original yaw of the camera + * @param sharedPitch MixinExtras parameter for sharing values between mixins + * @return The redirected yaw + */ + @ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 10), index = 0) + public float redirect_orientCameraYaw(float yaw, @Share("pitch") LocalFloatRef sharedPitch) { + return redirectCam(sharedPitch.get(), yaw); + } + + /** + * Turns the camera via GLStateManager + * @param pitch The pi + * @param yaw The yaw + * @see com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput#getInterpolatedState(float, float, float, boolean) + * @return The redirected yaw + */ + private float redirectCam(float pitch, float yaw) { + Triple interpolated = TASmodClient.virtual.CAMERA_ANGLE.getInterpolatedState(Minecraft.getMinecraft().timer.renderPartialTicks, pitch, yaw, TASmodClient.controller.isPlayingback()); + // Update pitch + GlStateManager.rotate(interpolated.getLeft(), 1.0f, 0.0f, 0.0f); + // Update roll + GlStateManager.rotate(interpolated.getRight(), 0.0f, 0.0f, 1.0f); + // Update yaw + return interpolated.getMiddle(); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGameSettings.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGameSettings.java index 6bd5398f..7c01c628 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGameSettings.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGameSettings.java @@ -11,12 +11,24 @@ @Mixin(GameSettings.class) public class MixinGameSettings { - + + /** + * Redirect Mouse.isButtonDown in keybindings + * @param i The keycode + * @param key The keybinding + * @return Whether the key is down + */ @Redirect(method = "isKeyDown", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;isButtonDown(I)Z", remap = false)) private static boolean redirectIsKeyDown1(int i, KeyBinding key) { return TASmodClient.virtual.isKeyDown(i + 100); } + /** + * Redirect Keyboard.isKeyDown in keybindings + * @param i The keycode + * @param key The keybinding + * @return Whether the key is down + */ @Redirect(method = "isKeyDown", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;isKeyDown(I)Z", remap = false)) private static boolean redirectIsKeyDown2(int i, KeyBinding key) { return TASmodClient.virtual.isKeyDown(i); diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiChat.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiChat.java index 3d26d3e2..c4b0880d 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiChat.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiChat.java @@ -1,5 +1,6 @@ package com.minecrafttas.tasmod.mixin.playbackhooks; +import com.minecrafttas.tasmod.virtual.VirtualInput; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @@ -10,8 +11,11 @@ @Mixin(GuiChat.class) public class MixinGuiChat { + /** + * @return {@link VirtualInput.VirtualMouseInput#getEventMouseScrollWheel()} + */ @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventDWheel()I", remap = false)) public int redirectHandleMouseInput4() { - return TASmodClient.virtual.getEventMouseScrollWheel(); + return TASmodClient.virtual.MOUSE.getEventMouseScrollWheel(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiClickableScrolledSelectionListProxy.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiClickableScrolledSelectionListProxy.java index 3dedb2bc..bfa57dd3 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiClickableScrolledSelectionListProxy.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiClickableScrolledSelectionListProxy.java @@ -1,5 +1,6 @@ package com.minecrafttas.tasmod.mixin.playbackhooks; +import com.minecrafttas.tasmod.virtual.VirtualInput; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @@ -10,8 +11,12 @@ @Mixin(GuiClickableScrolledSelectionListProxy.class) public class MixinGuiClickableScrolledSelectionListProxy { + + /** + * @return {@link VirtualInput.VirtualMouseInput#getEventMouseState()} + */ @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButtonState()Z", ordinal = 0, remap = false)) public boolean redirectHandleMouseInput() { - return TASmodClient.virtual.getEventMouseState(); + return TASmodClient.virtual.MOUSE.getEventMouseState(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainer.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainer.java index a539960e..97dbf995 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainer.java @@ -5,23 +5,45 @@ import org.spongepowered.asm.mixin.injection.Redirect; import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.virtual.VirtualKey; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.gui.inventory.GuiContainer; @Mixin(GuiContainer.class) public class MixinGuiContainer { + + /** + * Redirects the check for {@link VirtualKey#LSHIFT} and {@link VirtualKey#RSHIFT} in mouseClicked + * @param i The keycode to check for + * @return If the keycode is down + */ @Redirect(method = "mouseClicked", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;isKeyDown(I)Z", ordinal = 0, remap = false)) private boolean redirectIsKeyDown(int i) { return TASmodClient.virtual.isKeyDown(i); } + /** + * Redirects the check for {@link VirtualKey#LSHIFT} and {@link VirtualKey#RSHIFT} in mouseReleased + * @param i The keycode to check for + * @return If the keycode is down + */ @Redirect(method = "mouseReleased", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;isKeyDown(I)Z", ordinal = 0, remap = false)) private boolean redirectIsKeyDown2(int i) { return TASmodClient.virtual.isKeyDown(i); } -// @Redirect(method = "keyTyped", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/settings/KeyBinding;isActiveAndMatches(I)Z", remap = false)) //TODO Fix if #67 occurs -// public boolean redirectIsActiveAndMatches(KeyBinding keyBindInventory, int keyCode) { -// return keyBindInventory.isActiveAndMatches(keyCode) && !((GuiContainer)(Object)this).isFocused(); -// } + /** + * Fixes #67 + * @param player The current player + */ + @Redirect(method = "keyTyped", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/entity/EntityPlayerSP;closeScreen()V")) + public void redirectCloseScreen(EntityPlayerSP player) { + Minecraft mc = Minecraft.getMinecraft(); + if(TASmodClient.virtual.isKeyDown(mc.gameSettings.keyBindInventory.getKeyCode()) && ((GuiContainer)(Object)this).isFocused()) { + return; + } + player.closeScreen(); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainerCreative.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainerCreative.java index 4e073014..bad22303 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainerCreative.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiContainerCreative.java @@ -12,11 +12,11 @@ public class MixinGuiContainerCreative { @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventDWheel()I", ordinal = 0, remap = false)) public int redirectHandleMouseInput() { - return TASmodClient.virtual.getEventMouseScrollWheel(); + return TASmodClient.virtual.MOUSE.getEventMouseScrollWheel(); } @Redirect(method = "drawScreen", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;isButtonDown(I)Z", ordinal = 0, remap = false)) public boolean redirectHandleMouseInput2(int i) { - return TASmodClient.virtual.isKeyDown(-100); + return TASmodClient.virtual.MOUSE.isKeyDown(-100); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinGuiScreen.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiScreen.java similarity index 76% rename from src/main/java/com/minecrafttas/tasmod/mixin/MixinGuiScreen.java rename to src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiScreen.java index 73c5d859..0a8a330b 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinGuiScreen.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiScreen.java @@ -1,6 +1,6 @@ -package com.minecrafttas.tasmod.mixin; +package com.minecrafttas.tasmod.mixin.playbackhooks; -import org.lwjgl.input.Mouse; +import com.minecrafttas.tasmod.virtual.VirtualInput; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -11,6 +11,7 @@ import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.util.Ducks.GuiScreenDuck; +import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -18,84 +19,89 @@ @Mixin(GuiScreen.class) public class MixinGuiScreen implements GuiScreenDuck { - // ===================================================================================================================================== - + /** + * Run at the start of run handleInput. Runs every tick. + * @see VirtualInput.VirtualKeyboardInput#nextKeyboardTick() + * @param ci CBI + */ @Inject(method = "handleInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;isCreated()Z", shift = Shift.AFTER, remap = false)) public void injectAfterKeyboardCreated(CallbackInfo ci) { - TASmodClient.virtual.updateCurrentKeyboard(); + TASmodClient.virtual.KEYBOARD.nextKeyboardTick(); } - // ===================================================================================================================================== - + /** + * Redirects a {@link org.lwjgl.input.Keyboard#next()}. Starts running every tick and continues as long as there are {@link VirtualKeyboardEvent}s in {@link VirtualInput} + * @see VirtualInput.VirtualKeyboardInput#nextKeyboardSubtick() + * @return If {@link VirtualKeyboardEvent}s are present in {@link VirtualInput} + */ @Redirect(method = "handleInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;next()Z", remap = false)) public boolean redirectKeyboardNext() { - return TASmodClient.virtual.nextKeyboardEvent(); + return TASmodClient.virtual.KEYBOARD.nextKeyboardSubtick(); } - // ===================================================================================================================================== - + @Redirect(method = "handleKeyboardInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventCharacter()C", remap = false)) public char redirectGetEventCharacter() { - return TASmodClient.virtual.getEventKeyboardCharacter(); + return TASmodClient.virtual.KEYBOARD.getEventKeyboardCharacter(); } // ===================================================================================================================================== @Redirect(method = "handleKeyboardInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKey()I", remap = false)) public int redirectGetEventKey() { - return TASmodClient.virtual.getEventKeyboardKey(); + return TASmodClient.virtual.KEYBOARD.getEventKeyboardKey(); } // ===================================================================================================================================== @Redirect(method = "handleKeyboardInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKeyState()Z", remap = false)) public boolean redirectGetEventState() { - return TASmodClient.virtual.getEventKeyboardState(); + return TASmodClient.virtual.KEYBOARD.getEventKeyboardState(); } // ===================================================================================================================================== @Inject(method = "handleInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;isCreated()Z", shift = Shift.AFTER, remap = false)) public void injectAfterMouseCreated(CallbackInfo ci) { - TASmodClient.virtual.updateCurrentMouseEvents(); + TASmodClient.virtual.MOUSE.nextMouseTick(); } // ===================================================================================================================================== @Redirect(method = "handleInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;next()Z", remap = false)) public boolean redirectMouseNext() { - return TASmodClient.virtual.nextMouseEvent(); + return TASmodClient.virtual.MOUSE.nextMouseSubtick(); } // ===================================================================================================================================== @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButton()I", remap = false)) public int redirectGetEventButton() { - return TASmodClient.virtual.getEventMouseKey() + 100; + return TASmodClient.virtual.MOUSE.getEventMouseKey() + 100; } // ===================================================================================================================================== @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButtonState()Z", remap = false)) public boolean redirectGetEventButtonState() { - if (TASmodClient.virtual.getContainer().isPlayingback()) { - Mouse.setCursorPosition(uncalcX(TASmodClient.virtual.getEventCursorX()), uncalcY(TASmodClient.virtual.getEventCursorY())); + if (TASmodClient.controller.isPlayingback()) { // TODO replace with event + org.lwjgl.input.Mouse.setCursorPosition(rescaleX(TASmodClient.virtual.MOUSE.getEventCursorX()), rescaleY(TASmodClient.virtual.MOUSE.getEventCursorY())); } - return TASmodClient.virtual.getEventMouseState(); + return TASmodClient.virtual.MOUSE.getEventMouseState(); } // ===================================================================================================================================== @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventX()I", remap = false)) public int redirectGetEventX() { - return uncalcX(TASmodClient.virtual.getEventCursorX()); + return rescaleX(TASmodClient.virtual.MOUSE.getEventCursorX()); } // ===================================================================================================================================== @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventY()I", remap = false)) public int redirectGetEventY() { - return uncalcY(TASmodClient.virtual.getEventCursorY()); + return rescaleY(TASmodClient.virtual.MOUSE.getEventCursorY()); } // ===================================================================================================================================== @@ -131,22 +137,22 @@ private static boolean redirectIsAltKeyDown(int i) { private Minecraft mc; @Override - public int calcX(int X) { + public int unscaleX(int X) { return X * this.width / this.mc.displayWidth; } @Override - public int calcY(int Y) { + public int unscaleY(int Y) { return this.height - Y * this.height / this.mc.displayHeight - 1; } @Override - public int uncalcX(int X) { + public int rescaleX(int X) { return X * this.mc.displayWidth / this.width; } @Override - public int uncalcY(int Y) { + public int rescaleY(int Y) { return (this.mc.displayHeight * (this.height - Y - 1) / this.height); } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiSlot.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiSlot.java index 36ad6055..3f566e6e 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiSlot.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinGuiSlot.java @@ -12,11 +12,11 @@ public class MixinGuiSlot { @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE",target = "Lorg/lwjgl/input/Mouse;getEventButtonState()Z",ordinal = 0, remap = false)) public boolean redirectHandleMouseInput() { - return TASmodClient.virtual.getEventMouseState(); + return TASmodClient.virtual.MOUSE.getEventMouseState(); } @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE",target = "Lorg/lwjgl/input/Mouse;getEventButton()I",ordinal = 0, remap = false)) public int redirectHandleMouseInput2() { - return TASmodClient.virtual.getEventMouseKey(); + return TASmodClient.virtual.MOUSE.getEventMouseKey(); } @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE",target = "Lorg/lwjgl/input/Mouse;isButtonDown(I)Z",ordinal = 0, remap = false)) public boolean redirectHandleMouseInput3(int i) { @@ -24,6 +24,6 @@ public boolean redirectHandleMouseInput3(int i) { } @Redirect(method = "handleMouseInput", at = @At(value = "INVOKE",target = "Lorg/lwjgl/input/Mouse;getEventDWheel()I",ordinal = 0, remap = false)) public int redirectHandleMouseInput4() { - return TASmodClient.virtual.getEventMouseScrollWheel(); + return TASmodClient.virtual.MOUSE.getEventMouseScrollWheel(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinMinecraft.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinMinecraft.java new file mode 100644 index 00000000..8f01ad90 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinMinecraft.java @@ -0,0 +1,161 @@ +package com.minecrafttas.tasmod.mixin.playbackhooks; + +import com.minecrafttas.tasmod.virtual.VirtualInput; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualMouseInput; +import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; +import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; + +@Mixin(Minecraft.class) +public class MixinMinecraft { + + @Shadow + private GuiScreen currentScreen; + + /** + * Runs every frame. + * @see VirtualInput#update(GuiScreen) + * @param ci CBI + */ + @Inject(method = "runGameLoop", at = @At(value = "HEAD")) + public void playback_injectRunGameLoop(CallbackInfo ci) { + TASmodClient.virtual.update(currentScreen); + } + + // ============================ Keyboard + + /** + * Run at the start of run tick keyboard. Runs every tick. + * @see VirtualInput.VirtualKeyboardInput#nextKeyboardTick() + * @param ci CBI + */ + @Inject(method = "runTickKeyboard", at = @At(value = "HEAD")) + public void playback_injectRunTickKeyboard(CallbackInfo ci) { + TASmodClient.virtual.KEYBOARD.nextKeyboardTick(); + } + + /** + * Redirects a {@link org.lwjgl.input.Keyboard#next()}. Starts running every tick and continues as long as there are {@link VirtualKeyboardEvent}s in {@link VirtualInput} + * @see VirtualInput.VirtualKeyboardInput#nextKeyboardSubtick() + * @return If {@link VirtualKeyboardEvent}s are present in {@link VirtualInput} + */ + @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;next()Z", remap = false)) + public boolean playback_redirectKeyboardNext() { + return TASmodClient.virtual.KEYBOARD.nextKeyboardSubtick(); + } + + /** + * @return {@link VirtualInput.VirtualKeyboardInput#getEventKeyboardKey()} + */ + @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKey()I", remap = false)) + public int playback_redirectKeyboardGetEventKey() { + return TASmodClient.virtual.KEYBOARD.getEventKeyboardKey(); + } + + /** + * @return {@link VirtualInput.VirtualKeyboardInput#getEventKeyboardState()} + */ + @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKeyState()Z", remap = false)) + public boolean playback_redirectGetEventState() { + return TASmodClient.virtual.KEYBOARD.getEventKeyboardState(); + } + + /** + * @return {@link VirtualInput.VirtualKeyboardInput#getEventKeyboardCharacter()} + */ + @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventCharacter()C", remap = false)) + public char playback_redirectKeyboardGetEventCharacter() { + return TASmodClient.virtual.KEYBOARD.getEventKeyboardCharacter(); + } + + /** + * Runs everytime {@link #playback_redirectKeyboardNext()} has an event ready. Redirects {@link org.lwjgl.input.Keyboard#isKeyDown(int)} + * @see VirtualInput.VirtualKeyboardInput#isKeyDown(int) + * @return Whether the key is down in {@link VirtualInput} + */ + @Redirect(method = "runTickKeyboard", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;isKeyDown(I)Z", remap = false)) + public boolean playback_redirectIsKeyDown(int keyCode) { + return TASmodClient.virtual.KEYBOARD.isKeyDown(keyCode); + } + + /** + * @return {@link VirtualInput.VirtualKeyboardInput#getEventKeyboardKey()} + */ + @Redirect(method = "dispatchKeypresses", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKey()I", remap = false)) + public int playback_redirectGetEventKeyDPK() { + return TASmodClient.virtual.KEYBOARD.getEventKeyboardKey(); + } + + /** + * @return {@link VirtualInput.VirtualKeyboardInput#getEventKeyboardState()} + */ + @Redirect(method = "dispatchKeypresses", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventKeyState()Z", remap = false)) + public boolean playback_redirectGetEventKeyStateDPK() { + return TASmodClient.virtual.KEYBOARD.getEventKeyboardState(); + } + + /** + * @return {@link VirtualInput.VirtualKeyboardInput#getEventKeyboardCharacter()} + */ + @Redirect(method = "dispatchKeypresses", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Keyboard;getEventCharacter()C", remap = false)) + public char playback_redirectGetEventCharacterDPK() { + return TASmodClient.virtual.KEYBOARD.getEventKeyboardCharacter(); + } + + // ============================ Mouse + + /** + * Run at the start of run tick mouse. Runs every tick. + * @see VirtualInput.VirtualMouseInput#nextMouseTick() + * @param ci CBI + */ + @Inject(method = "runTickMouse", at = @At(value = "HEAD")) + public void playback_injectRunTickMouse(CallbackInfo ci) { + TASmodClient.virtual.MOUSE.nextMouseTick(); + } + + /** + * Redirects a {@link org.lwjgl.input.Mouse#next()}. Starts running every tick and continues as long as there are {@link VirtualMouseEvent}s in {@link VirtualInput} + * @see VirtualInput.VirtualMouseInput#nextMouseSubtick() + * @return If {@link VirtualMouseInput}s are present in {@link VirtualInput} + */ + @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;next()Z", remap = false)) + public boolean playback_redirectMouseNext() { + return TASmodClient.virtual.MOUSE.nextMouseSubtick(); + } + + /** + * @return {@link VirtualInput.VirtualMouseInput#getEventMouseKey()} + */ + @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButton()I", remap = false)) + public int playback_redirectMouseGetEventButton() { + return TASmodClient.virtual.MOUSE.getEventMouseKey() + 100; + } + + /** + * @return {@link VirtualInput.VirtualMouseInput#getEventMouseState()} + */ + @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButtonState()Z", remap = false)) + public boolean playback_redirectGetEventButtonState() { + return TASmodClient.virtual.MOUSE.getEventMouseState(); + } + + /** + * @return {@link VirtualInput.VirtualMouseInput#getEventMouseScrollWheel()} + */ + @Redirect(method = "runTickMouse", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventDWheel()I", remap = false)) + public int playback_redirectGetEventDWheel() { + return TASmodClient.virtual.MOUSE.getEventMouseScrollWheel(); + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java index 00641961..40d61059 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java @@ -12,8 +12,7 @@ @Mixin(NetHandlerPlayServer.class) public class MixinNetHandlerPlayServer { - - + @Redirect(method = "processPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/EntityPlayerMP;isInvulnerableDimensionChange()Z")) public boolean redirect_processPlayer(EntityPlayerMP parentIn) { return !parentIn.isInvulnerableDimensionChange() && TASmod.savestateHandlerServer.state!=SavestateState.LOADING; diff --git a/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java b/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java index 4baeebde..4cf26ba6 100644 --- a/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java +++ b/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java @@ -6,7 +6,6 @@ import java.util.List; import com.dselent.bigarraylist.BigArrayList; -import com.minecrafttas.killtherng.custom.CustomRandom; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; @@ -184,7 +183,7 @@ public String getSeed() { lastSeed = ""; } else { if(TASmod.ktrngHandler.isLoaded()) { - long distance = CustomRandom.distance(currentValues.seed, TASmod.ktrngHandler.getGlobalSeedClient()); + long distance = 0; //CustomRandom.distance(currentValues.seed, TASmod.ktrngHandler.getGlobalSeedClient()); if(distance == 0L) { lastSeed = ""; } else { @@ -244,9 +243,9 @@ public DesyncStatus getSeverity(int index, double[] playerValues, long seed) { if(this.seed != seed) { if(TASmod.ktrngHandler.isLoaded()) { - if(CustomRandom.distance(this.seed, seed)!=1) { +// if(CustomRandom.distance(this.seed, seed)!=1) { return DesyncStatus.SEED; - } +// } } else { return DesyncStatus.SEED; } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index 839a04c4..64247e25 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -39,7 +39,7 @@ import com.minecrafttas.tasmod.virtual.VirtualInput; import com.minecrafttas.tasmod.virtual.VirtualKeyboard; import com.minecrafttas.tasmod.virtual.VirtualMouse; -import com.minecrafttas.tasmod.virtual.VirtualSubticks; +import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; import com.mojang.realmsclient.gui.ChatFormatting; import com.mojang.realmsclient.util.Pair; @@ -88,7 +88,7 @@ public class PlaybackControllerClient implements ClientPacketHandler { private VirtualMouse mouse = new VirtualMouse(); - private VirtualSubticks subticks = new VirtualSubticks(); + private VirtualCameraAngle subticks = new VirtualCameraAngle(); public final File directory = new File(Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "tasfiles"); @@ -232,7 +232,7 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { return verbose ? TextFormatting.GREEN + "Pausing a recording" : ""; case NONE: LOGGER.debug(LoggerMarkers.Playback, "Stopping a recording"); - TASmodClient.virtual.unpressEverything(); + TASmodClient.virtual.clear(); state = TASstate.NONE; return verbose ? TextFormatting.GREEN + "Stopping the recording" : ""; } @@ -246,12 +246,12 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { LOGGER.debug(LoggerMarkers.Playback, "Pausing a playback"); state = TASstate.PAUSED; tempPause = TASstate.PLAYBACK; - TASmodClient.virtual.unpressEverything(); + TASmodClient.virtual.clear(); return verbose ? TextFormatting.GREEN + "Pausing a playback" : ""; case NONE: LOGGER.debug(LoggerMarkers.Playback, "Stopping a playback"); Minecraft.getMinecraft().gameSettings.chatLinks = true; - TASmodClient.virtual.unpressEverything(); + TASmodClient.virtual.clear(); state = TASstate.NONE; return verbose ? TextFormatting.GREEN + "Stopping the playback" : ""; } @@ -379,7 +379,7 @@ public VirtualMouse addMouseToContainer(VirtualMouse mouse) { * @param subticks Subticks to add * @return Subticks to retrieve */ - public VirtualSubticks addSubticksToContainer(VirtualSubticks subticks) { + public VirtualCameraAngle addSubticksToContainer(VirtualCameraAngle subticks) { if (state == TASstate.RECORDING) { this.subticks = subticks.clone(); } else if (state == TASstate.PLAYBACK) { @@ -683,9 +683,9 @@ private void tpPlayer(String startLocation) throws NumberFormatException { * Clears {@link #keyboard} and {@link #mouse} */ public void unpressContainer() { - LOGGER.trace(LoggerMarkers.Playback, "Unpressing container"); - keyboard.clear(); - mouse.clear(); +// LOGGER.trace(LoggerMarkers.Playback, "Unpressing container"); +// keyboard.clear(); +// mouse.clear(); } // ============================================================== @@ -734,9 +734,9 @@ public static class TickInputContainer implements Serializable { private VirtualMouse mouse; - private VirtualSubticks subticks; + private VirtualCameraAngle subticks; - public TickInputContainer(int tick, VirtualKeyboard keyboard, VirtualMouse mouse, VirtualSubticks subticks) { + public TickInputContainer(int tick, VirtualKeyboard keyboard, VirtualMouse mouse, VirtualCameraAngle subticks) { this.tick = tick; this.keyboard = keyboard; this.mouse = mouse; @@ -747,7 +747,7 @@ public TickInputContainer(int tick) { this.tick = tick; this.keyboard = new VirtualKeyboard(); this.mouse = new VirtualMouse(); - this.subticks = new VirtualSubticks(0, 0); +// this.subticks = new VirtualCameraAngle(0, 0); } @Override @@ -763,7 +763,7 @@ public VirtualMouse getMouse() { return mouse; } - public VirtualSubticks getSubticks() { + public VirtualCameraAngle getSubticks() { return subticks; } @@ -811,7 +811,7 @@ public static enum TASstate { public void setStateWhenOpened(TASstate state) { TASmodClient.openMainMenuScheduler.add(() -> { - PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + PlaybackControllerClient container = TASmodClient.controller; if (state == TASstate.RECORDING) { long seed = TASmod.ktrngHandler.getGlobalSeedClient(); container.setStartSeed(seed); @@ -847,15 +847,15 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws case PLAYBACK_SAVE: name = TASmodBufferBuilder.readString(buf); - try { - TASmodClient.virtual.saveInputs(name); - } catch (IOException e) { - if (mc.world != null) - mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); - else - e.printStackTrace(); - return; - } +// try { +// TASmodClient.virtual.saveInputs(name); TODO Move to PlaybackController +// } catch (IOException e) { +// if (mc.world != null) +// mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); +// else +// e.printStackTrace(); +// return; +// } if (mc.world != null) mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Saved inputs to " + name + ".mctas")); else @@ -864,15 +864,15 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws case PLAYBACK_LOAD: name = TASmodBufferBuilder.readString(buf); - try { - TASmodClient.virtual.loadInputs(name); - } catch (IOException e) { - if (mc.world != null) - mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); - else - e.printStackTrace(); - return; - } +// try { +// TASmodClient.virtual.loadInputs(name); TODO Move to PlaybackController +// } catch (IOException e) { +// if (mc.world != null) +// mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); +// else +// e.printStackTrace(); +// return; +// } if (mc.world != null) mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Loaded inputs from " + name + ".mctas")); else @@ -895,7 +895,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws case PLAYBACK_FULLRECORD: setStateWhenOpened(TASstate.RECORDING); // Set the state to RECORDING when the main menu is opened - TASmodClient.virtual.getContainer().clear(); // Clear inputs + TASmodClient.controller.clear(); // Clear inputs // Schedule code to be executed on the next tick TASmodClient.tickSchedulerClient.add(() -> { @@ -923,11 +923,11 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws case PLAYBACK_PLAYUNTIL: int until = ByteBufferBuilder.readInt(buf); - TASmodClient.virtual.getContainer().setPlayUntil(until); + TASmodClient.controller.setPlayUntil(until); break; case PLAYBACK_CLEAR_INPUTS: - TASmodClient.virtual.getContainer().clear(); + TASmodClient.controller.clear(); break; case PLAYBACK_TELEPORT: @@ -937,7 +937,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws TASstate networkState = TASmodBufferBuilder.readTASState(buf); boolean verbose = TASmodBufferBuilder.readBoolean(buf); Task task = ()->{ - PlaybackControllerClient container = TASmodClient.virtual.getContainer(); + PlaybackControllerClient container = TASmodClient.controller; if (networkState != container.getState()) { String message = container.setTASStateClient(networkState, verbose); diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java index 4a9ad482..0d3c2b98 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackSerialiser.java @@ -1,29 +1,27 @@ package com.minecrafttas.tasmod.playback; -import static com.minecrafttas.tasmod.TASmod.LOGGER; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.commons.io.FileUtils; - import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.monitoring.DesyncMonitoring; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; import com.minecrafttas.tasmod.util.FileThread; import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; import com.minecrafttas.tasmod.virtual.VirtualKey; import com.minecrafttas.tasmod.virtual.VirtualKeyboard; import com.minecrafttas.tasmod.virtual.VirtualMouse; -import com.minecrafttas.tasmod.virtual.VirtualMouse.PathNode; -import com.minecrafttas.tasmod.virtual.VirtualSubticks; import com.mojang.realmsclient.util.Pair; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; /** * Saves a given {@linkplain PlaybackControllerClient} to a file. Is also able to read an input container from a file.
@@ -334,19 +332,19 @@ private VirtualKeyboard readKeyboard(String section, int linenumber) throws IOEx for (String key : splitKeys) { - VirtualKey vkey = null; - // Check if the key is a keycode - if (isNumeric(key)) { - vkey = keyboard.get(Integer.parseInt(key)); - } else { - vkey = keyboard.get(key); - } - - if (vkey == null) { - throw new IOException(key + " is not a recognised keyboard key in line " + linenumber); - } - - vkey.setPressed(true); +// VirtualKey vkey = null; +// // Check if the key is a keycode +// if (isNumeric(key)) { +// vkey = keyboard.get(Integer.parseInt(key)); +// } else { +// vkey = keyboard.get(key); +// } +// +// if (vkey == null) { +// throw new IOException(key + " is not a recognised keyboard key in line " + linenumber); +// } +// +// vkey.setPressed(true); } } @@ -357,7 +355,7 @@ private VirtualKeyboard readKeyboard(String section, int linenumber) throws IOEx } for (char onechar : chars) { - keyboard.addChar(onechar); +// keyboard.addChar(onechar); } return keyboard; } @@ -379,60 +377,60 @@ private VirtualMouse readMouse(String section, int linenumber) throws IOExceptio String[] splitButtons=buttons.split(","); for (String button : splitButtons) { - VirtualKey vkey = null; - // Check if the key is a keycode - if (isNumeric(button)) { - vkey = mouse.get(Integer.parseInt(button)); - } else { - vkey = mouse.get(button); - } - if (vkey == null) { - throw new IOException(button + " is not a recognised mouse key in line " + linenumber); - } - mouse.get(button).setPressed(true); +// VirtualKey vkey = null; +// // Check if the key is a keycode +// if (isNumeric(button)) { +// vkey = mouse.get(Integer.parseInt(button)); +// } else { +// vkey = mouse.get(button); +// } +// if (vkey == null) { +// throw new IOException(button + " is not a recognised mouse key in line " + linenumber); +// } +// mouse.get(button).setPressed(true); } } - mouse.setPath(readPath(path, linenumber, mouse)); +// mouse.setPath(readPath(path, linenumber, mouse)); return mouse; } - private List readPath(String section, int linenumber, VirtualMouse mouse) throws IOException { - List path = new ArrayList(); - - section = section.replace("[", "").replace("]", ""); - String[] pathNodes = section.split("->"); - - for (String pathNode : pathNodes) { - String[] split = pathNode.split(","); - - int length=split.length; - int scrollWheel = 0; - int cursorX = 0; - int cursorY = 0; - try { - scrollWheel = Integer.parseInt(split[length-3]); - cursorX = Integer.parseInt(split[length-2]); - cursorY = Integer.parseInt(split[length-1]); - } catch (NumberFormatException e) { - throw new IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is not a number"); - } catch (ArrayIndexOutOfBoundsException e) { - throw new IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is missing or is too much"); - } - PathNode node = mouse.new PathNode(); - for (int i=0; i readPath(String section, int linenumber, VirtualMouse mouse) throws IOException { +// List path = new ArrayList(); +// +// section = section.replace("[", "").replace("]", ""); +// String[] pathNodes = section.split("->"); +// +// for (String pathNode : pathNodes) { +// String[] split = pathNode.split(","); +// +// int length=split.length; +// int scrollWheel = 0; +// int cursorX = 0; +// int cursorY = 0; +// try { +// scrollWheel = Integer.parseInt(split[length-3]); +// cursorX = Integer.parseInt(split[length-2]); +// cursorY = Integer.parseInt(split[length-1]); +// } catch (NumberFormatException e) { +// throw new IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is not a number"); +// } catch (ArrayIndexOutOfBoundsException e) { +// throw new IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is missing or is too much"); +// } +// PathNode node = mouse.new PathNode(); +// for (int i=0; iDucks now use up 66.66% less files on my hard drive! I call that a success. + * + * @author Pancake */ public class Ducks { @@ -26,20 +28,64 @@ public static interface ChunkProviderDuck { } /** - * Quacks the gui screen to spit out mouse positions independant of the display size + * Quacks the gui screen to spit out mouse positions independent of the display size */ public static interface GuiScreenDuck { - public int calcX(int X); - public int calcY(int Y); - public int uncalcX(int X); - public int uncalcY(int J); + + /** + * Calculates the true value of the pointer coordinate, by removing the scaling for custom screen sizes applied to it: + *
+		 * X * this.width / this.mc.displayWidth
+		 * 
+ * By storing the true value in the TASfile, we can play back the TAS even with a different GUI Scale applied to it + * @param x The scaled pointer coordinate + * @return The unscaled pointer coordinate + * @see #rescaleX(int) + */ + public int unscaleX(int x); + + /** + * Calculates the true value of the pointer coordinate, by removing the scaling for custom screen sizes applied to it: + *
+		 * this.height - Y * this.height / this.mc.displayHeight - 1
+		 * 
+ * By storing the true value in the TASfile, we can play back the TAS even with a different GUI Scale applied to it + * @param y The scaled pointer coordinate + * @return The unscaled pointer coordinate + * @see #rescaleY(int) + */ + public int unscaleY(int y); + + /** + * Reapplies the math for custom gui scales to the pointer coordinate: + *
+		 * X * this.mc.displayWidth / this.width
+		 * 
+ * @param x The unscaled pointer coordinate + * @return The scaled pointer coordinate + */ + public int rescaleX(int x); + + /** + * Reapplies the math for custom gui scales to the pointer coordinate: + *
+		 * (this.mc.displayHeight * (this.height - Y - 1) / this.height)
+		 * 
+ * @param y The unscaled pointer coordinate + * @return The scaled pointer coordinate + */ + public int rescaleY(int y); } /** * Quacks the subtick */ public static interface SubtickDuck { - public abstract void runSubtick(float partialTicks); + /** + * Custom updating method for EntityRenderer, updating the player rotation + * @param partialTicks The partial ticks from the vanilla Minecraft timer + */ + void runUpdate(float partialTicks); } } diff --git a/src/main/java/com/minecrafttas/tasmod/util/FileThread.java b/src/main/java/com/minecrafttas/tasmod/util/FileThread.java index cdf61425..759c5af1 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/FileThread.java +++ b/src/main/java/com/minecrafttas/tasmod/util/FileThread.java @@ -9,12 +9,17 @@ import java.util.ArrayList; import java.util.List; +/** + * Thread for writing files to disc + * + * @author Pancake + */ public class FileThread extends Thread { - private PrintWriter stream; + private final PrintWriter stream; private boolean end = false; - private List output = new ArrayList(); + private final List output = new ArrayList<>(); public FileThread(File fileLocation, boolean append) throws FileNotFoundException { stream = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileLocation, append), StandardCharsets.UTF_8)); diff --git a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java index c0fb439f..de235e6a 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java +++ b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java @@ -35,4 +35,8 @@ public class LoggerMarkers { public static final Marker Tickrate = MarkerManager.getMarker("Tickrate"); public static final Marker Playback = MarkerManager.getMarker("Playback"); + + public static final Marker Keyboard = MarkerManager.getMarker("Keyboard"); + + public static final Marker Mouse = MarkerManager.getMarker("Mouse"); } diff --git a/src/main/java/com/minecrafttas/tasmod/util/ModIncompatibleException.java b/src/main/java/com/minecrafttas/tasmod/util/ModIncompatibleException.java deleted file mode 100644 index f85575f0..00000000 --- a/src/main/java/com/minecrafttas/tasmod/util/ModIncompatibleException.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.minecrafttas.tasmod.util; - -public class ModIncompatibleException extends Exception{ - /** - * - */ - private static final long serialVersionUID = 9034388504854258444L; - public ModIncompatibleException() { - } - public ModIncompatibleException(String s) { - super(s); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java b/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java index e9136f0d..12a6d2a2 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java +++ b/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java @@ -5,37 +5,41 @@ import net.minecraft.client.gui.GuiWorldSelection; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.gui.inventory.GuiContainerCreative; /** - * Adjusts the pointer/cursor of the playback to different gui scalings. - * - * This was the work of many hours of trial and error. - * - * Out of despair I reached out to Darkmoon to help me with this problem... - * - * @author ScribbleLP, Darkmoon + * Normalizes the cursor to be independent of gui scalings.
+ * That way, a TAS recorded in e.g. Gui Scale "Large" can also be played back on Gui Scale "Small" * + * @author Scribble, Darkmoon */ public class PointerNormalizer { + /** + * Mathematically removes scaling from the x coordinate + * @param pointerX The current pointer coordinate + * @return The normalized x coordinate + */ public static int getNormalizedX(int pointerX) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); - int out = (int) (pointerX - (scaled.getScaledWidth() / 2D)); - return out; + return (int) (pointerX - (scaled.getScaledWidth() / 2D)); } + /** + * Mathematically removes scaling from the y coordinate + * @param pointerY The current pointer coordinate + * @return The normalized y coordinate + */ public static int getNormalizedY(int pointerY) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); int out = pointerY; - if (mc.currentScreen instanceof GuiContainer || mc.currentScreen instanceof GuiContainerCreative) { + if (mc.currentScreen instanceof GuiContainer) { out = (int) (pointerY - (scaled.getScaledHeight() / 2D)); } else if (mc.currentScreen instanceof GuiWorldSelection|| mc.currentScreen instanceof GuiMultiplayer) { - + // TODO Figure out what to do here } else { out = (int) (pointerY - (scaled.getScaledHeight() / 4 + 72 + -16)); } @@ -43,62 +47,62 @@ public static int getNormalizedY(int pointerY) { return out; } - public static int getCoordsX(int normalizedX) { + /** + * Reapplies gui scaling to the normalized pointer x coordinate + * @param normalizedX The normalized pointer coordinate + * @return The scaled coordinate + */ + public static int reapplyScalingX(int normalizedX) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); int out = (int) Math.round(normalizedX + (scaled.getScaledWidth() / 2D)); - return limiterX(out, scaled); + return clamp(out, 0, scaled.getScaledWidth()); } - public static int getCoordsY(int normalizedY) { + /** + * Reapplies gui scaling to the normalized pointer y coordinate + * @param normalizedY The normalized pointer coordinate + * @return The scaled coordinate + */ + public static int reapplyScalingY(int normalizedY) { Minecraft mc = Minecraft.getMinecraft(); ScaledResolution scaled = new ScaledResolution(mc); int out = normalizedY; - if (mc.currentScreen instanceof GuiContainer || mc.currentScreen instanceof GuiContainerCreative) { + if (mc.currentScreen instanceof GuiContainer) { out = (int) Math.round(normalizedY + (scaled.getScaledHeight() / 2D)); } else if (mc.currentScreen instanceof GuiWorldSelection || mc.currentScreen instanceof GuiMultiplayer) { - + // TODO Figure out what to do here } else { out = (int) (normalizedY + (scaled.getScaledHeight() / 4 + 72 + -16)); } - return limiterY(out, scaled); - } - - private static int limiterX(int out, ScaledResolution scaled) { - int width = scaled.getScaledWidth(); - if (out > width) { - out = width; - } else if (out < 0) - out = 0; - return out; - } - - private static int limiterY(int out, ScaledResolution scaled) { - int height = scaled.getScaledHeight(); - if (out > height) { - out = height; - } else if (out < 0) - out = 0; - return out; + return clamp(out, 0, scaled.getScaledHeight()); } - private static int gcd(int a, int b) { - return (b == 0) ? a : gcd(b, a % b); + private static int clamp(int value, int lower, int upper) { + if (value < lower) { + return lower; + } else { + return Math.min(value, upper); + } } public static void printAspectRatio() { int height = Minecraft.getMinecraft().displayHeight; int width = Minecraft.getMinecraft().displayWidth; - int gcd = gcd(width, height); + int gcd = greatestCommonDivisor(width, height); if (gcd == 0) { System.out.println(gcd); } else { System.out.println(width / gcd + ":" + height / gcd); } } - + + private static int greatestCommonDivisor(int a, int b) { + return (b == 0) ? a : greatestCommonDivisor(b, a % b); + } + /* * Here lies 10 hours of work for something I didn't even use. This code * normalizes the pointers coordinates and scales it depending on the screen diff --git a/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java index fd13454b..9e462b3a 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java @@ -7,7 +7,6 @@ * A simple scheduling interface * * @author Scribble - * */ public class Scheduler { diff --git a/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java b/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java index 4f514c1e..2588c4e1 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java +++ b/src/main/java/com/minecrafttas/tasmod/util/ShieldDownloader.java @@ -30,6 +30,11 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.ResourceLocation; +/** + * Downloads shield textures from { + /** + * A list of subtick peripherals.
+ * If a peripheral parent is updated, it first adds it's current state + * to the subtickList before updating.
+ * This makes the subtickList a list of previous peripheral states, with the + * first element being the oldest change.
+ *
+ * To distinguish a peripheral of being a subtick or a "parent", subtickList is + * either null or not null respectively (see {@link #isParent()})
+ */ + protected final List subtickList; + /** + * The way the parent/subtick relationship is set up (see + * {@link #subtickList}),
+ * the subtickList contains all previous changes, while the parent contains the + * current state.
+ * To achieve this and to prevent a ghost state from being added to the + * subtickList,
+ * it is sometimes necessary to ignore the first time an addition is made to the + * subtickList,
+ * to delay the subtickList and make the parent the current state. + */ + private boolean ignoreFirstUpdate = false; + + protected Subtickable(List subtickList, boolean ignoreFirstUpdate) { + this.subtickList = subtickList; + this.ignoreFirstUpdate = ignoreFirstUpdate; + } + + /** + * Adds a peripheral to {@link #subtickList} + * + * @param peripheral The peripheral to add + */ + protected void addSubtick(T peripheral) { + subtickList.add(peripheral); + } + + /** + * @return An immutable list of subticks + */ + public List getSubticks() { + return ImmutableList.copyOf(subtickList); + } + + /** + * @return If the peripheral is a parent and can add subticks + */ + public boolean isParent() { + return subtickList != null; + } + + /** + * Gets all peripheral states in an immutable list.
+ *
+ * This list comprises {@link #subtickList} and the current peripheral + * state added after that
+ * This will result in a list where the first element is the oldest state and + * the last being the current state. + * + * @return An immutable list of keyboard states + */ + @SuppressWarnings("unchecked") + public List getAll() { + return ImmutableList.builder().addAll(subtickList).add((T) this).build(); + } + + protected void clear() { + subtickList.clear(); + } + + /** + * Retrieves and sets {@link #ignoreFirstUpdate} to false + * + * @return If the first update should be ignored + */ + protected boolean ignoreFirstUpdate() { + boolean ignore = ignoreFirstUpdate; + ignoreFirstUpdate = false; + return ignore; + } + + /** + * @return If this peripheral should ignore it's first update + * @see #ignoreFirstUpdate + */ + protected boolean isIgnoreFirstUpdate() { + return ignoreFirstUpdate; + } + + protected void resetFirstUpdate() { + ignoreFirstUpdate = true; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java new file mode 100644 index 00000000..aefb5a50 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java @@ -0,0 +1,180 @@ +package com.minecrafttas.tasmod.virtual; + +import net.minecraft.util.math.MathHelper; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Stores the values of the camera angle of the player in a given timeframe.
+ *
+ * Similar to {@link VirtualKeyboard} and {@link VirtualMouse} with the difference,
+ * that no difference calculation is applied and only the absolute camera coordinates are used.
+ * This makes the playback desync proof to different mouse sensitivity across PCs.
+ * + */ +public class VirtualCameraAngle extends Subtickable implements Serializable { + /** + * Controls the up/down coordinate of the camera. Clamped between -90 and +90 + */ + private Float pitch; + /** + * Controls the left/right coordinate of the camera. In this case the camera is clamped between -180 and +180 + */ + private Float yaw; + + /** + * Creates an empty camera angle with pitch and yaw = 0 + */ + public VirtualCameraAngle() { + this(null, null, new ArrayList<>(), true); + } + + /** + * Creates a subtick camera angle with {@link Subtickable#subtickList} uninitialized + * @param pitch {@link #pitch} + * @param yaw {@link #yaw} + */ + public VirtualCameraAngle(Float pitch, Float yaw) { + this(pitch, yaw, null); + } + + /** + * Creates a parent camera angle + * @param pitch {@link #pitch} + * @param yaw {@link #yaw} + * @param ignoreFirstUpdate {@link Subtickable#ignoreFirstUpdate} + */ + public VirtualCameraAngle(Float pitch, Float yaw, boolean ignoreFirstUpdate) { + this(pitch, yaw, new ArrayList<>(), ignoreFirstUpdate); + } + + /** + * Creates a camera angle with existing values + * @param pitch {@link #pitch} + * @param yaw {@link #yaw} + * @param subtickList {@link Subtickable#subtickList} + */ + public VirtualCameraAngle(Float pitch, Float yaw, List subtickList) { + this(pitch, yaw, subtickList, false); + } + + /** + * Creates a camera angle with initialized values + * @param pitch {@link VirtualCameraAngle#pitch} + * @param yaw {@link VirtualCameraAngle#yaw} + * @param subtickList {@link Subtickable#subtickList} + * @param ignoreFirstUpdate {@link Subtickable#ignoreFirstUpdate} + */ + public VirtualCameraAngle(Float pitch, Float yaw, List subtickList, boolean ignoreFirstUpdate) { + super(subtickList, ignoreFirstUpdate); + this.pitch = pitch; + this.yaw = yaw; + } + + /** + * Updates the camera angle. + * @param pitchDelta The difference between absolute coordinates of the pitch, is added to {@link VirtualCameraAngle#pitch} + * @param yawDelta The difference between absolute coordinates of the yaw, is added to {@link VirtualCameraAngle#yaw} + */ + public void update(float pitchDelta, float yawDelta) { + if(pitch==null || yaw == null) { + return; + } + if(isParent() && !ignoreFirstUpdate()) { + addSubtick(clone()); + } + this.pitch = MathHelper.clamp(this.pitch + pitchDelta, -90.0F, 90.0F); + this.yaw += yawDelta; + } + + /** + * Setting the absolute camera coordinates directly + * @param pitch {@link #pitch} + * @param yaw {@link #yaw} + */ + public void set(float pitch, float yaw) { + this.pitch = pitch; + this.yaw = yaw; + } + + /** + * A list of all camera states in this VirtualCameraAngle. + * It consists of: {@link Subtickable#subtickList} + this + * @param reference A list of VirtualCameraAngles with the newest being the current camera angle + */ + public void getStates(List reference) { + if (isParent()) { + reference.addAll(subtickList); + reference.add(this); + } + } + + /** + * Copies the data from another camera angle into this camera without creating a new object. + * @param camera The camera to move from + */ + public void copyFrom(VirtualCameraAngle camera) { + this.pitch = camera.pitch; + this.yaw = camera.yaw; + this.subtickList.clear(); + this.subtickList.addAll(camera.subtickList); + camera.subtickList.clear(); + } + + /** + * Sets {@link #pitch} and {@link #yaw} to null + */ + @Override + public void clear() { + this.pitch = null; + this.yaw = null; + super.clear(); + } + + /** + * Creates a clone of this object as a subtick + */ + @Override + public VirtualCameraAngle clone() { + return new VirtualCameraAngle(pitch, yaw); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof VirtualCameraAngle) { + VirtualCameraAngle angle = (VirtualCameraAngle) obj; + return (pitch != null && pitch.equals(angle.pitch)) && (yaw != null && yaw.equals(angle.yaw)); + } + return super.equals(obj); + } + + @Override + public String toString() { + if(isParent()) { + return getAll().stream().map(VirtualCameraAngle::toString2).collect(Collectors.joining("\n")); + } else { + return toString2(); + } + } + + private String toString2() { + return String.format("%s;%s", pitch, yaw); + } + + /** + * @return {@link #pitch} + */ + public Float getPitch() { + return pitch; + } + + /** + * @return {@link #yaw} + */ + public Float getYaw() { + return yaw; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualChar.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualChar.java deleted file mode 100644 index 3ed211a7..00000000 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualChar.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.minecrafttas.tasmod.virtual; - -import java.util.Map; - -import com.google.common.collect.Maps; - -@Deprecated -public class VirtualChar { - private char name; - private boolean pressed; - static Map keyChars= Maps.newHashMap(); - - public VirtualChar(char name, boolean pressed) { - this.name=name; - this.pressed=pressed; - keyChars.put(name, this); - } - public void setPressed(boolean pressed) { - this.pressed=pressed; - } - public char getName() { - return name; - } - public boolean isPressed() { - return pressed; - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java index 6045dfab..b59f7a25 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java @@ -1,608 +1,658 @@ package com.minecrafttas.tasmod.virtual; -import static com.minecrafttas.tasmod.TASmod.LOGGER; - -import java.io.File; -import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; - -import com.minecrafttas.mctcommon.events.EventClient.EventPlayerJoinedClientSide; -import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.apache.commons.lang3.tuple.Triple; +import org.apache.logging.log4j.Logger; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer; +import com.minecrafttas.tasmod.mixin.playbackhooks.MixinMinecraft; +import com.minecrafttas.tasmod.util.Ducks; +import com.minecrafttas.tasmod.util.LoggerMarkers; import com.minecrafttas.tasmod.util.PointerNormalizer; +import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; +import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent; -import net.minecraft.client.Minecraft; -import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.util.math.MathHelper; /** - * One of the core classes of this mod
- *
- * This mimics peripherals used to control minecraft which are: The keyboard, - * the mouse and the angle of the player, which is called "Subticks" in this - * case (this came from a time when the camera angle was actually updated on - * a subtick level).
- *
- * For each peripheral there are 2 states. The "current" state (e.g. - * {@linkplain #currentKeyboard}), which is the state of what the game - * actually currently recognizes and the "next" state (e.g. - * {@linkplain #nextKeyboard}) which either the buttons pressed on the - * keyboard, or the buttons pressed in the next playback tick.
- *
- * Outside of this class, there is a third state, which is the Vanilla Minecraft - * keybindings, which, in the best case, should be a copy of the "current" - * state.
- *

Events

To update the vanilla keybindings you need something called - * key events. An event for a keyboard might look like this
- *
- * 17,true,'w'
- *
- * Something like this is sent by the LWJGL methods to the vanilla methods to - * update the keybindings.
- * In this case it is the key with the keycode 17 (The 'W' key), in the state - * true which means it is pressed down. And the 'w' is the character that is - * associated with that key
+ * Main component for redirecting inputs.
*
- * You can find a complete list of LWJGL keycodes in - * {@link VirtualKeyboard}.
- *
- * If W is released, the key event for this would look something like this: - * 17, false, NULL
- * From that, the vanilla keybindings know which key is currently pressed down. - * As a bonus, the character 'w' is used when typing in a textfield.
- *

Emulating events

With the key events from LWJGL, so from the - * "physical keyboard", we can update the nextKeyboard in the - * {@link #updateNextKeyboard(int, boolean, char)} method.
- * This method is called every frame and also works in tickrate 0.
- *
- * And on every tick, we call {@link #updateCurrentKeyboard()}, which updates - * the currentKeyboard with a copy of nextKeyboard.
- * However, we still need to update the Vanilla Minecraft keybinding by using - * key events.
- * To solve this problem we can use - * {@link VirtualKeyboard#getDifference(VirtualKeyboard)}, which compares 2 - * keyboards and extracts the key events from that.
- *
- * For instance if we have a keyboard, where nothing is pressed, then a keyboard - * where only "W" is pressed, we can assume that the key event responsible for - * that change 17,true,? is.
- * But as indicated by the ? we actually don't know the character that is typed - * there. And for that we need to store the characters seperately in the - * keyboard ({@link VirtualKeyboard#getCharList()}).
- *
- * The advantage of this system is: - *
    - *
  • Better support for savestates
  • - *
  • Better support for tickrate 0
  • - *
  • Less cluttering in the resulting files
  • - *
  • Recording support for the full keyboard/eventual modding support
  • - *
- * - * @author Scribble + * This class mimics the LWJGL classes {@link org.lwjgl.input.Keyboard} and + * {@link org.lwjgl.input.Mouse} and redirects the camera angle and player rotation
* + * @author Scribble */ -public class VirtualInput implements EventPlayerJoinedClientSide{ - +public class VirtualInput { + private final Logger LOGGER; /** - * The container where all inputs get stored during recording or stored and - * ready to be played back + * Instance of the {@link VirtualKeyboardInput} subclass, intended to improve readability. */ - private PlaybackControllerClient container = new PlaybackControllerClient(); - - // ===========================Keyboard================================= - + public final VirtualKeyboardInput KEYBOARD; /** - * The state of the keyboard recognized by the game. Updated on a tick basis - *
- * See also: {@link VirtualInput} + * Instance of the {@link VirtualMouseInput} subclass, intended to improve readability. */ - private VirtualKeyboard currentKeyboard = new VirtualKeyboard(); - + public final VirtualMouseInput MOUSE; /** - * The state of the keyboard which will replace - * {@linkplain VirtualInput#currentKeyboard} in the next tick. Updated every - * frame
- * See also: {@link VirtualInput} + * Instance of the {@link VirtualCameraAngleInput} subclass, intended to improve readability. */ - private VirtualKeyboard nextKeyboard = new VirtualKeyboard(); - - private List currentKeyboardEvents = new ArrayList(); - private Iterator currentKeyboardEventIterator = currentKeyboardEvents.iterator(); - - private VirtualKeyboardEvent currentKeyboardEvent = null; + public final VirtualCameraAngleInput CAMERA_ANGLE; - public VirtualKeyboard getCurrentKeyboard() { - return currentKeyboard; + /** + * Creates a new virtual input with an empty {@link VirtualKeyboardInput}, {@link VirtualMouseInput} and {@link VirtualCameraAngleInput} + * @param logger The logger instance + */ + public VirtualInput(Logger logger) { + this(logger, new VirtualKeyboard(), new VirtualMouse(), new VirtualCameraAngle()); } - public VirtualKeyboard getNextKeyboard() { - return nextKeyboard; - } - /** - * Loads the inputs and starts a TAS on initialize - * @param fileToLoad (Nullable) Loads this filename and starts playing back the TAS + * Creates a virtual input with pre-loaded values + * + * @param preloadedKeyboard A keyboard loaded when creating {@link VirtualKeyboardInput} + * @param preloadedMouse A mouse loaded when creating {@link VirtualMouseInput} + * @param preloadedCamera A camera loaded when creating {@link VirtualCameraAngleInput} */ - public VirtualInput(String fileToLoad) { - if (fileToLoad != null) { - try { - loadInputs(fileToLoad); - container.setStateWhenOpened(TASstate.PLAYBACK); - } catch (IOException e) { - LOGGER.error("Cannot load inputs from the start of the TAS: {}", e.getMessage()); - } - } + public VirtualInput(Logger logger, VirtualKeyboard preloadedKeyboard, VirtualMouse preloadedMouse, VirtualCameraAngle preloadedCamera) { + this.LOGGER = logger; + KEYBOARD = new VirtualKeyboardInput(preloadedKeyboard); + MOUSE = new VirtualMouseInput(preloadedMouse); + CAMERA_ANGLE = new VirtualCameraAngleInput(preloadedCamera); } - public void updateNextKeyboard(int keycode, boolean keystate, char character) { - -// System.out.println(keycode+" "+keystate+" "+character); - - if (keystate) { - character = nextKeyboard.encodeUnicode(keycode, character); - } else { - if (keycode == 15) { - character = '\u2907'; - } - } - nextKeyboard.addChar(character); - if (VirtualKeybindings.isKeyCodeAlwaysBlocked(keycode)) { - return; + /** + * Updates the logic for {@link #KEYBOARD}, {@link #MOUSE} and + * {@link #CAMERA_ANGLE}
+ * Runs every frame + * + * @see MixinMinecraft#playback_injectRunGameLoop(CallbackInfo) + * @param currentScreen The current screen from Minecraft.class. Used for + * checking if the mouse logic should be adapted to + * GUIScreens + */ + public void update(GuiScreen currentScreen) { + while (Keyboard.next()) { + KEYBOARD.updateNextKeyboard(Keyboard.getEventKey(), Keyboard.getEventKeyState(), Keyboard.getEventCharacter(), Keyboard.areRepeatEventsEnabled()); } - VirtualKey key = nextKeyboard.get(keycode); - - key.setPressed(keystate); - } - - public List getCurrentKeyboardEvents() { - return currentKeyboard.getDifference(nextKeyboard); - } - - public void updateCurrentKeyboard() { - currentKeyboardEvents = getCurrentKeyboardEvents(); - currentKeyboardEventIterator = currentKeyboardEvents.iterator(); - -// currentKeyboardEvents.forEach(action->{ -// System.out.println(action.toString()); -// }); - - nextKeyboard.clearCharList(); - - currentKeyboard = nextKeyboard.clone(); - } - - public boolean nextKeyboardEvent() { - boolean hasnext = currentKeyboardEventIterator.hasNext(); - if (hasnext) { - currentKeyboardEvent = currentKeyboardEventIterator.next(); + while (Mouse.next()) { + if (currentScreen == null) { + MOUSE.updateNextMouse(Mouse.getEventButton()-100, Mouse.getEventButtonState(), Mouse.getEventDWheel(), 0, 0); + } else { + Ducks.GuiScreenDuck screen = (Ducks.GuiScreenDuck) currentScreen; + int eventX = screen.unscaleX(Mouse.getEventX()); + int eventY = screen.unscaleY(Mouse.getEventY()); + eventX = PointerNormalizer.getNormalizedX(eventX); + eventY = PointerNormalizer.getNormalizedY(eventY); + MOUSE.updateNextMouse(Mouse.getEventButton()-100, Mouse.getEventButtonState(), Mouse.getEventDWheel(), eventX, eventY); + } } - return hasnext; - } - - public int getEventKeyboardKey() { - return currentKeyboardEvent.getKeyCode(); - } - - public boolean getEventKeyboardState() { - return currentKeyboardEvent.isState(); - } - - public char getEventKeyboardCharacter() { - return currentKeyboardEvent.getCharacter(); - } - - public void clearNextKeyboard() { - nextKeyboard.clear(); } + /** + * If the keyboard or mouse key is currently down. + * If keycode >= 0 then {@link VirtualKeyboardInput#isKeyDown(int)} will be called,
+ * otherwise {@link VirtualMouseInput#isKeyDown(int)} + * + * @param keycode The keycode in question + * @return If the key is down either on mouse or keyboard + */ public boolean isKeyDown(int keycode) { - if (keycode >= 0) - return currentKeyboard.get(keycode).isKeyDown(); - else - return currentMouse.get(keycode).isKeyDown(); - } - - public boolean isKeyDown(String keyname) { - if (currentKeyboard.get(keyname).isKeyDown()) { - return true; - } else if (currentMouse.get(keyname).isKeyDown()) { - return true; + if(keycode >= 0) { + return KEYBOARD.isKeyDown(keycode); } else { - return false; + return MOUSE.isKeyDown(keycode); } } - + + /** + * If the keyboard or mouse key is will be down in the next tick. + * If keycode >= 0 then {@link VirtualKeyboardInput#willKeyBeDown(int)} will be called,
+ * otherwise {@link VirtualMouseInput#willKeyBeDown(int)} + * + * @param keycode The keycode in question + * @return If the key will be down either on mouse or keyboard + */ public boolean willKeyBeDown(int keycode) { - if (keycode >= 0) - return nextKeyboard.get(keycode).isKeyDown(); - else - return nextMouse.get(keycode).isKeyDown(); - } - - public boolean willKeyBeDown(String keyname) { - if (nextKeyboard.get(keyname).isKeyDown()) { - return true; - } else if (nextMouse.get(keyname).isKeyDown()) { - return true; + if(keycode >= 0) { + return KEYBOARD.willKeyBeDown(keycode); } else { - return false; + return MOUSE.willKeyBeDown(keycode); } } - - public List getCurrentKeyboardPresses() { - List out = new ArrayList(); - - currentKeyboard.getKeyList().forEach((keycodes, virtualkeys) -> { - if (virtualkeys.isKeyDown()) { - out.add(virtualkeys.getName()); - } - }); - - return out; + + /** + * Unpresses all keys in {@link VirtualKeyboardInput#nextKeyboard} and {@link VirtualMouseInput#nextMouse} + */ + public void clear() { + KEYBOARD.nextKeyboard.clear(); + MOUSE.nextMouse.clear(); + CAMERA_ANGLE.nextCameraAngle.clear(); } - - public List getNextKeyboardPresses() { - - List out = new ArrayList(); - if (container.isPlayingback() && container.get(container.index()) != null) { - container.get(container.index()).getKeyboard().getKeyList().forEach((keycodes, virtualkeys) -> { - if (virtualkeys.isKeyDown()) { - out.add(virtualkeys.getName()); - } - }); - } else { - nextKeyboard.getKeyList().forEach((keycodes, virtualkeys) -> { - if (virtualkeys.isKeyDown()) { - out.add(virtualkeys.getName()); - } - }); + + /** + * Subclass of {@link VirtualInput} handling keyboard logic.
+ *
+ * Vanilla keyboard handling looks something like this: + * + *
+	 *	public void runTickKeyboard()  { // Executed every tick in runTick()
+	 *		while({@linkplain Keyboard#next()}){
+	 *			int keycode = {@linkplain Keyboard#getEventKey()};
+	 *			boolean keystate = {@linkplain Keyboard#getEventKey()};
+	 *			char character = {@linkplain Keyboard#getEventCharacter()}
+	 *
+	 *			Keybindings.updateKeybind(keycode, keystate, character)
+	 *		}
+	 *	}
+	 * 
+ * + * After redirecting the calls in {@link MixinMinecraft}, the resulting logic + * now looks like this: + * + *
+	 *	public void runTickKeyboard()  {
+	 *		{@linkplain #nextKeyboardTick()}
+	 *		while({@linkplain #nextKeyboardSubtick()}){
+	 *			int keycode = {@linkplain #getEventKeyboardKey()}};
+	 *			boolean keystate = {@linkplain #getEventKeyboardState()};
+	 *			char character = {@linkplain #getEventKeyboardCharacter()}
+	 *
+	 *			Keybindings.updateKeybind(keycode, keystate, character)
+	 *		}
+	 *	}
+	 * 
+ * @see VirtualKeyboard + */ + public class VirtualKeyboardInput { + /** + * The keyboard "state" that is currently recognized by the game,
+ * meaning it is a direct copy of the vanilla keybindings. Updated every + * tick.
+ * Updated in {@link #nextKeyboardTick()} + */ + private final VirtualKeyboard currentKeyboard; + /** + * The "state" of the real physical keyboard.
+ * This is updated every frame.
+ * Updates {@link #currentKeyboard} in {@link #nextKeyboardTick()} + */ + private final VirtualKeyboard nextKeyboard = new VirtualKeyboard(); + /** + * Queue for keyboard events.
+ * Is filled in {@link #nextKeyboardTick()} and read in + * {@link #nextKeyboardSubtick()} + */ + private final Queue keyboardEventQueue = new ConcurrentLinkedQueue(); + /** + * The current keyboard event where the vanilla keybindings are reading + * from.
+ * Updated in {@link #nextKeyboardSubtick()} and read out in + *
    + *
  • {@link #getEventKeyboardKey()}
  • + *
  • {@link #getEventKeyboardState()}
  • + *
  • {@link #getEventKeyboardCharacter()}
  • + *
+ */ + private VirtualKeyboardEvent currentKeyboardEvent = new VirtualKeyboardEvent(); + + /** + * Constructor to preload the {@link #currentKeyboard} with an existing keyboard + * @param preloadedKeyboard The new {@link #currentKeyboard} + */ + public VirtualKeyboardInput(VirtualKeyboard preloadedKeyboard) { + currentKeyboard = preloadedKeyboard; } - return out; - } - - // =======================================Mouse============================================ - - private VirtualMouse currentMouse = new VirtualMouse(); - - private VirtualMouse nextMouse = new VirtualMouse(); - - private List currentMouseEvents = new ArrayList(); - private Iterator currentMouseEventIterator = currentMouseEvents.iterator(); - - private VirtualMouseEvent currentMouseEvent = null; - - public VirtualMouse getCurrentMouse() { - return currentMouse; - } - - public VirtualMouse getNextMouse() { - return nextMouse; - } - - public void updateNextMouse(int keycode, boolean keystate, int scrollwheel, int cursorX, int cursorY, boolean filter) { - boolean flag = true; - if (filter) { - flag = nextMouse.isSomethingDown() || scrollwheel != 0 || keycode != -1; + /** + * Updates the next keyboard + * + * @see VirtualInput#update(GuiScreen) + * @param keycode The keycode of this event + * @param keystate The keystate of this event + * @param character The character of this event + */ + public void updateNextKeyboard(int keycode, boolean keystate, char character) { + updateNextKeyboard(keycode, keystate, character, false); } - VirtualKey key = nextMouse.get(keycode - 100); - - if (VirtualKeybindings.isKeyCodeAlwaysBlocked(keycode - 100)) { - key.setPressed(false); - return; + /** + * Updates the next keyboard + * + * @see VirtualInput#update(GuiScreen) + * @param keycode The keycode of this event + * @param keystate The keystate of this event + * @param character The character of this event + * @param repeatEventsEnabled If repeat events are enabled + */ + public void updateNextKeyboard(int keycode, boolean keystate, char character, boolean repeatEventsEnabled) { + LOGGER.debug(LoggerMarkers.Keyboard, "Update: {}, {}, {}, {}", keycode, keystate, character); // Activate with -Dtasmod.marker.keyboard=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) + nextKeyboard.update(keycode, keystate, character, repeatEventsEnabled); } - key.setPressed(keystate); - - nextMouse.setScrollWheel(scrollwheel); - - nextMouse.setCursor(PointerNormalizer.getNormalizedX(cursorX), PointerNormalizer.getNormalizedY(cursorY)); - - if (flag == true) - nextMouse.addPathNode(); - } - - public List getCurrentMouseEvents() { - return currentMouse.getDifference(nextMouse); - } - - public void updateCurrentMouseEvents() { - currentMouseEvents = getCurrentMouseEvents(); - currentMouseEventIterator = currentMouseEvents.iterator(); - - // Prints the mouse events given to the keybindings... very useful -// currentMouseEvents.forEach(action->{ -// System.out.println(action.toString()); -// }); - - resetNextMouseLists(); - - currentMouse = nextMouse.clone(); - } - - public void resetNextMouseLists() { - nextMouse.resetPath(); - } - - public boolean nextMouseEvent() { - boolean hasnext = currentMouseEventIterator.hasNext(); - if (hasnext) { - currentMouseEvent = currentMouseEventIterator.next(); + /** + * Runs when the next keyboard tick is about to occur.
+ * Used to load {@link #nextKeyboard} into {@link #currentKeyboard}, creating + * {@link VirtualKeyboardEvent}s in the process. + * + * @see MixinMinecraft#playback_injectRunTickKeyboard(org.spongepowered.asm.mixin.injection.callback.CallbackInfo) + */ + public void nextKeyboardTick() { + currentKeyboard.getVirtualEvents(nextKeyboard, keyboardEventQueue); + currentKeyboard.copyFrom(nextKeyboard); } - return hasnext; - } - - public int getEventMouseKey() { - return currentMouseEvent.getKeyCode(); - } - - public boolean getEventMouseState() { - return currentMouseEvent.isState(); - } - - public int getEventMouseScrollWheel() { - return currentMouseEvent.getScrollwheel(); - } - public int getEventCursorX() { - return PointerNormalizer.getCoordsX(currentMouseEvent.getMouseX()); - } - - public int getEventCursorY() { - return PointerNormalizer.getCoordsY(currentMouseEvent.getMouseY()); - } - - public void clearNextMouse() { - nextMouse.clear(); - } - - public List getCurrentMousePresses() { - List out = new ArrayList(); - - currentMouse.getKeyList().forEach((keycodes, virtualkeys) -> { - if (virtualkeys.isKeyDown()) { - out.add(virtualkeys.getName()); - } - }); - - return out; - } - - public List getNextMousePresses() { - List out = new ArrayList(); - - if (container.isPlayingback() && container.get(container.index()) != null) { - container.get(container.index()).getMouse().getKeyList().forEach((keycodes, virtualkeys) -> { - if (virtualkeys.isKeyDown()) { - out.add(virtualkeys.getName()); - } - }); - } else { - nextMouse.getKeyList().forEach((keycodes, virtualkeys) -> { - if (virtualkeys.isKeyDown()) { - out.add(virtualkeys.getName()); - } - }); + /** + * Runs in a while loop. Used for updating {@link #currentKeyboardEvent} and + * ending the while loop. + * + * @see MixinMinecraft#playback_redirectKeyboardNext() + * @return If a keyboard event is in {@link #keyboardEventQueue} + */ + public boolean nextKeyboardSubtick() { + return (currentKeyboardEvent = keyboardEventQueue.poll()) != null; } - return out; - } - - public void unpressEverything() { - container.unpressContainer(); - unpressNext(); - } - - public void unpressNext() { - clearNextKeyboard(); - clearNextMouse(); - } - - // ======================================Subticks=========================================== - - VirtualSubticks currentSubtick = new VirtualSubticks(0, 0); - - public void updateSubtick(float pitch, float yaw) { - currentSubtick = container.addSubticksToContainer(new VirtualSubticks(pitch, yaw)); - } - - public float getSubtickPitch() { - return currentSubtick.getPitch(); - } - - public float getSubtickYaw() { - return currentSubtick.getYaw(); - } - - // =====================================Container=========================================== + /** + * @return The keycode of {@link #currentKeyboardEvent} + */ + public int getEventKeyboardKey() { + return currentKeyboardEvent.getKeyCode(); + } - public PlaybackControllerClient getContainer() { - return container; - } + /** + * @return The keystate of {@link #currentKeyboardEvent} + */ + public boolean getEventKeyboardState() { + return currentKeyboardEvent.isState(); + } - /** - * Updates the input container and the {@link #nextKeyboard} as well as - * {@link #nextMouse}
- * Gets executed each game tick - */ - public void updateContainer() { - nextKeyboard = container.addKeyboardToContainer(nextKeyboard); - nextMouse = container.addMouseToContainer(nextMouse); + /** + * @return The character(s) of {@link #currentKeyboardEvent} + */ + public char getEventKeyboardCharacter() { + return currentKeyboardEvent.getCharacter(); + } + + /** + * 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 #currentKeyboard} is down + */ + public boolean isKeyDown(int keycode) { + return currentKeyboard.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 keyboard is pressed + * @param keycode The keycode of the key in question + * @return Whether the key of the {@link #nextKeyboard} is down + */ + public boolean willKeyBeDown(int keycode) { + return nextKeyboard.isKeyDown(keycode); + } } /** - * Replaces the {@link #container}, used in + * Subclass of {@link VirtualInput} handling mouse logic.
+ *
+ * Vanilla mouse handling looks something like this: * - * @param container to replace the current one - */ - public void setContainer(PlaybackControllerClient container) { - this.container = container; - } - - // =====================================Savestates=========================================== - - /** - * Loads and preloads the inputs from the new InputContainer to - * {@link #container} + *
+	 *	public void runTickMouse()  { // Executed every tick in runTick()
+	 *		while({@linkplain Mouse#next()}){
+	 *			int keycode = {@linkplain Mouse#getEventButton()};
+	 *			boolean keystate = {@linkplain Mouse#getEventButtonState()};
+	 *			int scrollWheel = {@linkplain Mouse#getEventDWheel()}
+	 *			int cursorX = {@linkplain Mouse#getEventX()} // Important in GUIs
+	 *			int cursorY = {@linkplain Mouse#getEventY()}
+	 *
+	 *			Keybindings.updateKeybind(keycode, keystate, etc...)
+	 *		}
+	 *	}
+	 * 
* - * Saving a savestate is done via {@linkplain com.minecrafttas.tasmod.playback.PlaybackSerialiser#saveToFileV1(File, PlaybackControllerClient)} in {@linkplain com.minecrafttas.tasmod.savestates.SavestateHandlerClient#savestate(String)} + * After redirecting the calls in {@link MixinMinecraft}, the resulting logic + * now looks like this: * - * @param savestatecontainer The container that should be loaded. + *
+	 *	public void runTickMouse()  { // Executed every tick in runTick()
+	 *		{@linkplain #nextMouseTick()}
+	 *		while({@linkplain #nextMouseSubtick}){
+	 *			int keycode = {@linkplain #getEventMouseKey};
+	 *			boolean keystate = {@linkplain #getEventMouseState()} ()};
+	 *			int scrollWheel = {@linkplain #getEventMouseScrollWheel()}
+	 *			int cursorX = {@linkplain #getEventCursorX()} // Important in GUIs
+	 *			int cursorY = {@linkplain #getEventCursorY()}
+	 *
+	 *			Keybindings.updateKeybind(keycode, keystate, etc...)
+	 *		}
+	 *	}
+	 * 
+ * @see VirtualMouse */ - public void loadClientSavestate(PlaybackControllerClient savestatecontainer) { - - if (container.isPlayingback()) { - preloadInput(container, savestatecontainer.size() - 1); // Preloading from the current container and - // from the second to last index of - // the savestatecontainer. Since this is - // executed during playback, - // we will only load the position of the - // savestate container and not replace the - // container itself. This is due to the fact - // that the playback would immediately end - // when you replace the container. - - if (container.size() >= savestatecontainer.size()) { // Check if the current container is bigger than the - // savestated one. - - try { - container.setIndex(savestatecontainer.size()); // Set the "playback" index of the current - // container to the latest index of the - // savestatecontainer. Meaning this index will - // be played next - } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); - } - } else { - String start = savestatecontainer.getStartLocation(); - savestatecontainer.setStartLocation(""); - - try { - savestatecontainer.setIndex(savestatecontainer.size() - 1); - } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); - } - savestatecontainer.setTASStateClient(TASstate.PLAYBACK); - savestatecontainer.setStartLocation(start); - container = savestatecontainer; - } - - } else if (container.isRecording()) { - String start = savestatecontainer.getStartLocation(); - preloadInput(savestatecontainer, savestatecontainer.size() - 1); // Preload the input of the savestate + public class VirtualMouseInput { + /** + * The mouse "state" that is currently recognized by the game,
+ * meaning it is a direct copy of the vanilla mouse. Updated every + * tick.
+ * Updated in {@link #nextMouseTick()} + */ + private final VirtualMouse currentMouse; + /** + * The "state" of the real physical mouse.
+ * This is updated every frame.
+ * Updates {@link #currentMouse} in {@link #nextMouseTick()} + */ + private final VirtualMouse nextMouse = new VirtualMouse(); + /** + * Queue for keyboard events.
+ * Is filled in {@link #nextMouseTick()} and read in + * {@link #nextMouseSubtick()} + */ + private final Queue mouseEventQueue = new ConcurrentLinkedQueue<>(); + /** + * The current mouse event where the vanilla mouse is reading + * from.
+ * Updated in {@link #nextMouseSubtick()} and read out in + *
    + *
  • {@link #getEventMouseKey()}
  • + *
  • {@link #getEventMouseState()}
  • + *
  • {@link #getEventMouseScrollWheel()}
  • + *
  • {@link #getEventCursorX()}
  • + *
  • {@link #getEventCursorY()}
  • + *
+ */ + private VirtualMouseEvent currentMouseEvent = new VirtualMouseEvent(); + + /** + * Constructor to preload the {@link #currentMouse} with an existing mouse + * @param preloadedMouse The new {@link #currentMouse} + */ + public VirtualMouseInput(VirtualMouse preloadedMouse) { + currentMouse = preloadedMouse; + } - nextKeyboard = new VirtualKeyboard(); // Unpress the nextKeyboard and mouse to get rid of the preloaded inputs in the - // next keyboard. Note that these inputs are still loaded in the current - // keyboard - nextMouse = new VirtualMouse(); + /** + * Updates the next keyboard + * + * @see VirtualInput#update(GuiScreen) + * @param keycode The keycode of this event + * @param keystate The keystate of this event + * @param scrollwheel The scrollwheel direction of this event + * @param cursorX The x coordinate of the cursor of this event + * @param cursorY The y coordinate of the cursot of this event + */ + public void updateNextMouse(int keycode, boolean keystate, int scrollwheel, int cursorX, int cursorY) { + LOGGER.debug(LoggerMarkers.Mouse,"Update: {} ({}), {}, {}, {}, {}", keycode, VirtualKey.getName(keycode), keystate, scrollwheel, cursorX, cursorY); // Activate with -Dtasmod.marker.mouse=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) + nextMouse.update(keycode, keystate, scrollwheel, cursorX, cursorY); + } - try { - savestatecontainer.setIndex(savestatecontainer.size()); - } catch(IndexOutOfBoundsException e) { - e.printStackTrace(); - } - - savestatecontainer.setTASStateClient(TASstate.RECORDING); - savestatecontainer.setStartLocation(start); - container = savestatecontainer; // Replace the current container with the savestated container + /** + * Runs when the next mouse tick is about to occur.
+ * Used to load {@link #nextMouse} into {@link #currentMouse}, creating + * {@link VirtualMouseEvent}s in the process. + * + * @see MixinMinecraft#playback_injectRunTickMouse(org.spongepowered.asm.mixin.injection.callback.CallbackInfo) + */ + public void nextMouseTick() { + currentMouse.getVirtualEvents(nextMouse, mouseEventQueue); + currentMouse.copyFrom(nextMouse); } - } - /** - * Preloads the specified index from, the container to {@link #nextMouse} and - * {@link #nextKeyboard} - * - * @param container The container from which the inputs should be pre loaded - * @param index The index of the container from which the inputs should be - * loaded - */ - private void preloadInput(PlaybackControllerClient container, int index) { - TickInputContainer tickcontainer = container.get(index); + /** + * Runs in a while loop. Used for updating {@link #currentMouseEvent} and + * ending the while loop. + * + * @see MixinMinecraft#playback_redirectMouseNext() + * @return If a mouse event is in {@link #mouseEventQueue} + */ + public boolean nextMouseSubtick() { + return (currentMouseEvent = mouseEventQueue.poll()) != null; + } - if (tickcontainer != null) { - tickcontainer = tickcontainer.clone(); - nextKeyboard = tickcontainer.getKeyboard().clone(); - nextMouse = tickcontainer.getMouse().clone(); + /** + * @return The keycode of {@link #currentMouseEvent} + */ + public int getEventMouseKey() { + return currentMouseEvent.getKeyCode(); + } - Minecraft.getMinecraft().runTickKeyboard(); // Letting mouse and keyboard tick once to load inputs into the - // "currentKeyboard" - Minecraft.getMinecraft().runTickMouse(); - } else { - LOGGER.warn("Can't preload inputs, specified inputs are null!"); + /** + * @return The keystate of {@link #currentMouseEvent} + */ + public boolean getEventMouseState() { + return currentMouseEvent.isState(); } - } - // ================================Load/Save Inputs===================================== - - public void loadInputs(String filename) throws IOException { - setContainer(TASmodClient.serialiser.fromEntireFileV1(new File(TASmodClient.tasdirectory + "/" + filename + ".mctas"))); - getContainer().fixTicks(); - } - - public void saveInputs(String filename) throws IOException { - TASmodClient.createTASDir(); - TASmodClient.serialiser.saveToFileV1(new File(TASmodClient.tasdirectory + "/" + filename + ".mctas"), TASmodClient.virtual.getContainer()); - } - // =====================================Debug=========================================== + /** + * @return The scroll wheel of {@link #currentMouseEvent} + */ + public int getEventMouseScrollWheel() { + return currentMouseEvent.getScrollwheel(); + } - public class InputEvent { - public int tick; - public List keyboardevent; - public List mouseEvent; - public VirtualSubticks subticks; + /** + * @return The scaled x coordinate of the cursor of {@link #currentMouseEvent} + */ + public int getEventCursorX() { + return PointerNormalizer.reapplyScalingX(getNormalizedCursorX()); + } - public InputEvent(int tick, List keyboardevent, List 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); + } +}