diff --git a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java
index 8dc8ec4c..177b830f 100644
--- a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java
+++ b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java
@@ -99,7 +99,7 @@ public class TASmodClient implements ClientModInitializer, EventClientInit, Even
* 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 PlaybackControllerClient controller;
public static void createTASfileDir() {
try {
@@ -131,6 +131,7 @@ public void onInitializeClient() {
loadConfig(mc);
virtual = new VirtualInput(LOGGER);
+ controller = new PlaybackControllerClient(virtual, tasfiledirectory, LOGGER);
// Initialize InfoHud
hud = new InfoHud();
diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java
index 7c4a4a6e..449d6ba2 100644
--- a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java
+++ b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java
@@ -45,6 +45,18 @@ public static interface EventDrawHotbarAlways extends EventBase {
public void onDrawHotbarAlways();
}
+ /**
+ * Fired at the beginning of a client tick
+ */
+ @FunctionalInterface
+ public static interface EventClientTickPre extends EventBase {
+
+ /**
+ * Fired at the beginning of a client tick
+ */
+ public void onClientTickPre(Minecraft mc);
+ }
+
/**
* Fired at the end of a client tick
*/
diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java
index 450efdc7..9b8115e9 100644
--- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java
+++ b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java
@@ -11,6 +11,7 @@
import com.minecrafttas.mctcommon.events.EventListenerRegistry;
import com.minecrafttas.tasmod.TASmodClient;
+import com.minecrafttas.tasmod.events.EventClient;
import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost;
import com.minecrafttas.tasmod.util.Ducks.SubtickDuck;
@@ -45,6 +46,7 @@ public void injectRunGameLoop(CallbackInfo ci) {
@Redirect(method = "runGameLoop", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;runTick()V"))
public void redirectRunTick(Minecraft mc) {
+ EventListenerRegistry.fireEvent(EventClient.EventClientTickPre.class, (Minecraft) (Object) this);
if (TASmodClient.tickratechanger.ticksPerSecond != 0) {
((SubtickDuck) this.entityRenderer).runUpdate(this.isGamePaused ? this.renderPartialTicksPaused : this.timer.renderPartialTicks);
}
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java
index 6c48a0ba..564dde01 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java
@@ -1,6 +1,5 @@
package com.minecrafttas.tasmod.playback;
-import static com.minecrafttas.tasmod.TASmod.LOGGER;
import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_CLEAR_INPUTS;
import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_FULLPLAY;
import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_FULLRECORD;
@@ -34,9 +33,9 @@
import com.minecrafttas.mctcommon.networking.exception.WrongSideException;
import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler;
import com.minecrafttas.mctcommon.networking.interfaces.PacketID;
-import com.minecrafttas.tasmod.TASmod;
import com.minecrafttas.tasmod.TASmodClient;
import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost;
+import com.minecrafttas.tasmod.events.EventClient.EventClientTickPre;
import com.minecrafttas.tasmod.events.EventClient.EventDrawScreen;
import com.minecrafttas.tasmod.events.EventPlaybackClient;
import com.minecrafttas.tasmod.events.EventPlaybackClient.EventControllerStateChange;
@@ -52,12 +51,12 @@
import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackSaveException;
import com.minecrafttas.tasmod.registries.TASmodConfig;
import com.minecrafttas.tasmod.registries.TASmodPackets;
+import com.minecrafttas.tasmod.util.DebugWriter;
import com.minecrafttas.tasmod.util.Ducks.GuiScreenDuck;
import com.minecrafttas.tasmod.util.LoggerMarkers;
import com.minecrafttas.tasmod.util.Scheduler.Task;
import com.minecrafttas.tasmod.virtual.VirtualCameraAngle;
import com.minecrafttas.tasmod.virtual.VirtualInput;
-import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput;
import com.minecrafttas.tasmod.virtual.VirtualKeyboard;
import com.minecrafttas.tasmod.virtual.VirtualMouse;
@@ -93,6 +92,7 @@ public class PlaybackControllerClient implements
ClientPacketHandler,
EventClientInit,
+ EventClientTickPre,
EventClientTickPost,
EventDrawScreen,
@@ -102,7 +102,7 @@ public class PlaybackControllerClient implements
//@formatter:on
{
- private Logger logger = TASmod.LOGGER;
+ private final Logger logger;
/**
* The current state of the controller.
@@ -119,6 +119,11 @@ public class PlaybackControllerClient implements
*/
private long index;
+ /**
+ * The virtual input instance
+ */
+ private final VirtualInput virtual;
+
/**
*
The current keyboard used in the {@link PlaybackControllerClient PlaybackController}
*
Used during recording to store incoming inputs from the {@link VirtualInput#KEYBOARD}
@@ -171,9 +176,10 @@ public class PlaybackControllerClient implements
// =====================================================================================================
- public PlaybackControllerClient() {
- tasFileDirectory = TASmodClient.tasfiledirectory;
-
+ public PlaybackControllerClient(VirtualInput virtual, Path tasFileDirectory, Logger logger) {
+ this.virtual = virtual;
+ this.logger = logger;
+ this.tasFileDirectory = tasFileDirectory;
inputs = new BigArrayList(tasFileDirectory.resolve("temp").toAbsolutePath().toString());
}
@@ -230,11 +236,9 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) {
switch (stateIn) {
case PLAYBACK:
startPlayback();
- state = TASstate.PLAYBACK;
return verbose ? TextFormatting.GREEN + "Starting playback" : "";
case RECORDING:
startRecording();
- state = TASstate.RECORDING;
return verbose ? TextFormatting.GREEN + "Starting a recording" : "";
case PAUSED:
return verbose ? TextFormatting.RED + "Can't pause anything because nothing is running" : "";
@@ -248,13 +252,12 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) {
case RECORDING:
return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Recording)";
case PAUSED:
- LOGGER.debug(LoggerMarkers.Playback, "Pausing a recording");
+ logger.debug(LoggerMarkers.Playback, "Pausing a recording");
state = TASstate.PAUSED;
stateAfterPause = TASstate.RECORDING;
return verbose ? TextFormatting.GREEN + "Pausing a recording" : "";
case NONE:
stopRecording();
- state = TASstate.NONE;
return verbose ? TextFormatting.GREEN + "Stopping the recording" : "";
}
} else if (state == TASstate.PLAYBACK) { // If the container is currently playing back
@@ -264,13 +267,12 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) {
case RECORDING:
stopPlayback(false);
startRecording();
- state = TASstate.RECORDING;
return verbose ? TextFormatting.GREEN + "Switching from playback to recording" : "";
case PAUSED:
- LOGGER.debug(LoggerMarkers.Playback, "Pausing a playback");
+ logger.debug(LoggerMarkers.Playback, "Pausing a playback");
state = TASstate.PAUSED;
stateAfterPause = TASstate.PLAYBACK;
- TASmodClient.virtual.clearNext();
+ virtual.clearNext();
return verbose ? TextFormatting.GREEN + "Pausing a playback" : "";
case NONE:
stopPlayback(true);
@@ -280,58 +282,66 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) {
} else if (state == TASstate.PAUSED) {
switch (stateIn) {
case PLAYBACK:
- LOGGER.debug(LoggerMarkers.Playback, "Resuming a playback");
+ logger.debug(LoggerMarkers.Playback, "Resuming a playback");
state = TASstate.PLAYBACK;
stateAfterPause = TASstate.NONE;
return verbose ? TextFormatting.GREEN + "Resuming a playback" : "";
case RECORDING:
- LOGGER.debug(LoggerMarkers.Playback, "Resuming a recording");
+ logger.debug(LoggerMarkers.Playback, "Resuming a recording");
state = TASstate.RECORDING;
stateAfterPause = TASstate.NONE;
return verbose ? TextFormatting.GREEN + "Resuming a recording" : "";
case PAUSED:
return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Paused)";
case NONE:
- LOGGER.debug(LoggerMarkers.Playback, "Aborting pausing");
+ logger.debug(LoggerMarkers.Playback, "Aborting pausing");
state = TASstate.NONE;
- TASstate statey = stateAfterPause;
+ TASstate stateAfterPauseTemp = stateAfterPause;
stateAfterPause = TASstate.NONE;
- return TextFormatting.GREEN + "Aborting a " + statey.toString().toLowerCase() + " that was paused";
+ return TextFormatting.GREEN + "Aborting a " + stateAfterPauseTemp.toString().toLowerCase() + " that was paused";
}
}
return "Something went wrong ._.";
}
private void startRecording() {
- LOGGER.debug(LoggerMarkers.Playback, "Starting recording");
+ logger.debug(LoggerMarkers.Playback, "Starting recording");
+ state = TASstate.RECORDING;
if (this.inputs.isEmpty()) {
- VirtualCameraAngleInput CAMERA_ANGLE = TASmodClient.virtual.CAMERA_ANGLE;
- Float pitch = CAMERA_ANGLE.getCurrentPitch();
- Float yaw = CAMERA_ANGLE.getCurrentYaw();
- this.currentPlaybackCameraAngle.set(pitch, yaw);
-
- inputs.add(new InputContainer());
+ virtual.preloadInputs();
+ inputs.add(new InputContainer(this.currentPlaybackKeyboard.clone(), this.currentPlaybackMouse.clone(), this.currentPlaybackCameraAngle.clone()));
}
}
private void stopRecording() {
- LOGGER.debug(LoggerMarkers.Playback, "Stopping a recording");
- TASmodClient.virtual.clearNext();
+ logger.debug(LoggerMarkers.Playback, "Stopping a recording");
+ virtual.clearNext();
+ state = TASstate.NONE;
}
private void startPlayback() {
- LOGGER.debug(LoggerMarkers.Playback, "Starting playback");
- Minecraft.getMinecraft().gameSettings.chatLinks = false; // #119
+ logger.debug(LoggerMarkers.Playback, "Starting playback");
+ state = TASstate.PLAYBACK;
+
+ InputContainer initialContainer = inputs.get(0);
+ this.currentPlaybackKeyboard = initialContainer.getKeyboard().clone();
+ this.currentPlaybackMouse = initialContainer.getMouse().clone();
+ this.currentPlaybackCameraAngle = initialContainer.getCameraAngle().clone();
+ virtual.preloadInputs(initialContainer);
+
+ if (Minecraft.getMinecraft() != null)
+ Minecraft.getMinecraft().gameSettings.chatLinks = false; // #119
index = 0;
-// TASmod.ktrngHandler.setInitialSeed(startSeed);
}
private void stopPlayback(boolean clearInputs) {
- LOGGER.debug(LoggerMarkers.Playback, "Stopping a playback");
- Minecraft.getMinecraft().gameSettings.chatLinks = true;
+ logger.debug(LoggerMarkers.Playback, "Stopping a playback");
+ if (Minecraft.getMinecraft() != null)
+ Minecraft.getMinecraft().gameSettings.chatLinks = true;
if (clearInputs) {
- TASmodClient.virtual.clearNext();
+ virtual.clearNext();
}
+ state = TASstate.NONE;
}
/**
@@ -354,7 +364,7 @@ public TASstate togglePause() {
* @param pause True, if it should be paused
*/
public void pause(boolean pause) {
- LOGGER.trace(LoggerMarkers.Playback, "Pausing {}", pause);
+ logger.trace(LoggerMarkers.Playback, "Pausing {}", pause);
if (pause) {
if (state != TASstate.NONE) {
setTASStateClient(TASstate.PAUSED, false);
@@ -445,51 +455,46 @@ public void onDrawScreen(GuiScreen screen, int x, int y) {
Mouse.setCursorPosition(duckedScreen.rescaleX(x), duckedScreen.rescaleY(y));
}
- /**
- * Updates the input container.
- *
- * During a recording this adds the {@linkplain #currentPlaybackKeyboard}, {@linkplain #currentPlaybackMouse}
- * and {@linkplain #currentPlaybackCameraAngle} to {@linkplain #inputs} and increases the
- * {@linkplain #index}.
- *
- * During playback the opposite is happening, getting the inputs from
- * {@linkplain #inputs} and temporarily storing them in {@linkplain #currentPlaybackKeyboard},
- * {@linkplain #currentPlaybackMouse} and {@linkplain #currentPlaybackCameraAngle}.
- *
- * Then in {@linkplain VirtualInput}, {@linkplain #currentPlaybackKeyboard},
- * {@linkplain #currentPlaybackMouse} and {@linkplain #currentPlaybackCameraAngle} are retrieved and emulated as
- * the next inputs
- */
+ @Override
+ public void onClientTickPre(Minecraft mc) {
+ if (state == TASstate.PLAYBACK) {
+ /* Tick the next playback*/
+ playbackNextTick();
+ }
+ }
+
@Override
public void onClientTickPost(Minecraft mc) {
/* Stop the playback while player is still loading */
- EntityPlayerSP player = mc.player;
- if (player != null && player.addedToChunk) {
- if (isPaused() && stateAfterPause != TASstate.NONE) { // TODO Find a better solution...
- setTASState(stateAfterPause); // The recording is paused in LoadWorldEvents#startLaunchServer
- pause(false);
- EventListenerRegistry.fireEvent(EventPlaybackJoinedWorld.class, state);
+ if (mc != null) { // Mc can be null during Unit Tests
+ EntityPlayerSP player = mc.player;
+ if (player != null && player.addedToChunk) {
+ if (isPaused() && stateAfterPause != TASstate.NONE) { // TODO Find a better solution...
+ setTASState(stateAfterPause); // The recording is paused in LoadWorldEvents#startLaunchServer
+ pause(false);
+ EventListenerRegistry.fireEvent(EventPlaybackJoinedWorld.class, state);
+ }
}
}
- /* Tick the next playback or recording */
+ /* Tick the next recording */
if (state == TASstate.RECORDING) {
recordNextTick();
- } else if (state == TASstate.PLAYBACK) {
- playbackNextTick();
}
-// if (TASmod.isDevEnvironment) {
-// DebugWriter.writeDebugFile(this);
-// }
+ if (mc != null)
+ DebugWriter.writeDebugFile(this);
}
+ /**
+ * Records the inputs from {@link #currentPlaybackKeyboard} etc. into {@link #inputs}
+ */
private void recordNextTick() {
index++;
InputContainer container = new InputContainer(currentPlaybackKeyboard.clone(), currentPlaybackMouse.clone(), currentPlaybackCameraAngle.clone());
if (inputs.size() <= index) {
if (inputs.size() < index) {
- LOGGER.warn("Index is {} inputs bigger than the container!", index - inputs.size());
+ logger.warn("Index is {} inputs bigger than the container!", index - inputs.size());
}
inputs.add(container);
} else {
@@ -499,46 +504,50 @@ private void recordNextTick() {
EventListenerRegistry.fireEvent(EventRecordTick.class, index, container);
}
+ /**
+ * Retrieves the inputs from {@link #inputs} and adds them to {@link #currentPlaybackKeyboard} etc.
+ * Also updates {@link #nextPlaybackCameraAngle} which is only used for visualizing what the input in the next tick is
+ */
private void playbackNextTick() {
Minecraft mc = Minecraft.getMinecraft();
- if (!Display.isActive() && mc.gameSettings.pauseOnLostFocus) { // Stops the playback when you tab out of minecraft, for once as a failsafe,
- // secondly as potential exploit protection
- LOGGER.info(LoggerMarkers.Playback, "Stopping a {} since the user tabbed out of the game", state);
+ if (mc != null && !Display.isActive() && mc.gameSettings.pauseOnLostFocus) { // Stops the playback when you tab out of minecraft, for once as a failsafe,
+ // secondly as potential exploit protection
+ logger.info(LoggerMarkers.Playback, "Stopping a {} since the user tabbed out of the game", state);
setTASState(TASstate.NONE);
+ return;
}
- index++; // Increase the index and load the next inputs
-
- EventListenerRegistry.fireEvent(EventPlaybackTickPre.class, index);
-
/* Stop condition */
- if (index == inputs.size() || inputs.isEmpty()) {
- index--;
+ if (index + 1 == inputs.size() || inputs.isEmpty()) {
unpressContainer();
setTASState(TASstate.NONE);
+ return;
}
+
+ index++; // Increase the index and load the next inputs
+
+ EventListenerRegistry.fireEvent(EventPlaybackTickPre.class, index);
+
/* Continue condition */
- else {
- InputContainer container = null;
- if (index + 1 < inputs.size()) {
- container = inputs.get(index + 1); // Loads the new inputs from the container
-
- this.currentPlaybackKeyboard = this.nextPlaybackKeyboard.clone();
- this.currentPlaybackMouse = this.nextPlaybackMouse.clone();
- this.currentPlaybackCameraAngle = this.nextPlaybackCameraAngle.clone();
-
- this.nextPlaybackKeyboard = container.getKeyboard().clone();
- this.nextPlaybackMouse = container.getMouse().clone();
- this.nextPlaybackCameraAngle = container.getCameraAngle().clone();
- } else {
- container = inputs.get(index); // Loads the new inputs from the container
- this.currentPlaybackKeyboard = container.getKeyboard().clone();
- this.currentPlaybackMouse = container.getMouse().clone();
- this.currentPlaybackCameraAngle = container.getCameraAngle().clone();
- }
- EventListenerRegistry.fireEvent(EventPlaybackTick.class, index, container);
+ if (index + 1 < inputs.size()) {
+ InputContainer nextContainer = inputs.get(index + 1);
+
+ this.nextPlaybackKeyboard = nextContainer.getKeyboard().clone();
+ this.nextPlaybackMouse = nextContainer.getMouse().clone();
+ this.nextPlaybackCameraAngle = nextContainer.getCameraAngle().clone();
+ } else {
+ nextPlaybackKeyboard.clear();
+ nextPlaybackMouse.clear();
+ nextPlaybackCameraAngle.clear();
}
+
+ InputContainer container = inputs.get(index); // Loads the new inputs from the container
+ this.currentPlaybackKeyboard = container.getKeyboard().clone();
+ this.currentPlaybackMouse = container.getMouse().clone();
+ this.currentPlaybackCameraAngle = container.getCameraAngle().clone();
+
+ EventListenerRegistry.fireEvent(EventPlaybackTick.class, index, container);
}
// =====================================================================================================
// Methods to manipulate inputs
@@ -606,7 +615,7 @@ public InputContainer get() {
}
public void clear() {
- LOGGER.info(LoggerMarkers.Playback, "Clearing playback controller");
+ logger.info(LoggerMarkers.Playback, "Clearing playback controller");
clearInputList();
EventListenerRegistry.fireEvent(EventPlaybackClient.EventRecordClear.class);
@@ -663,7 +672,7 @@ public String toString() {
* Clears {@link #currentPlaybackKeyboard} and {@link #currentPlaybackMouse}
*/
public void unpressContainer() {
- LOGGER.trace(LoggerMarkers.Playback, "Unpressing container");
+ logger.trace(LoggerMarkers.Playback, "Unpressing container");
currentPlaybackKeyboard.clear();
currentPlaybackMouse.clear();
}
@@ -942,12 +951,12 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws
} catch (PlaybackSaveException e) {
if (mc.world != null)
mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage()));
- LOGGER.catching(e);
+ logger.catching(e);
return;
} catch (Exception e) {
if (mc.world != null)
mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + "Saving failed, something went very wrong"));
- LOGGER.catching(e);
+ logger.catching(e);
return;
}
@@ -956,7 +965,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws
confirm.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/folder tasfiles"));
mc.ingameGUI.getChatGUI().printChatMessage(confirm);
} else
- LOGGER.debug(LoggerMarkers.Playback, "Saved inputs to " + name + ".mctas");
+ logger.debug(LoggerMarkers.Playback, "Saved inputs to " + name + ".mctas");
break;
case PLAYBACK_LOAD:
@@ -970,19 +979,19 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws
TextComponentString textComponent = new TextComponentString(e.getMessage());
mc.ingameGUI.getChatGUI().printChatMessage(textComponent);
}
- LOGGER.catching(e);
+ logger.catching(e);
return;
} catch (Exception e) {
if (mc.world != null)
mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + "Loading failed, something went very wrong"));
- LOGGER.catching(e);
+ logger.catching(e);
return;
}
if (mc.world != null)
mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Loaded inputs from " + name + ".mctas"));
else
- LOGGER.debug(LoggerMarkers.Playback, "Loaded inputs from " + name + ".mctas");
+ logger.debug(LoggerMarkers.Playback, "Loaded inputs from " + name + ".mctas");
break;
case PLAYBACK_FULLPLAY:
@@ -1048,7 +1057,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws
if (Minecraft.getMinecraft().world != null)
Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage(new TextComponentString(message));
else
- LOGGER.debug(LoggerMarkers.Playback, message);
+ logger.debug(LoggerMarkers.Playback, message);
}
}
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java
index 808d86ff..ef697ee7 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java
@@ -12,6 +12,7 @@
import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_RESTARTANDPLAY;
import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_SAVE;
import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_STATE;
+import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_STATE_TEMP_SAVESTATE;
import static com.minecrafttas.tasmod.registries.TASmodPackets.SAVESTATE_CLEAR_SCREEN;
import static com.minecrafttas.tasmod.util.LoggerMarkers.Playback;
@@ -50,6 +51,7 @@ public PacketID[] getAcceptedPacketIDs() {
return new TASmodPackets[]
{
PLAYBACK_STATE,
+ PLAYBACK_STATE_TEMP_SAVESTATE,
PLAYBACK_CLEAR_INPUTS,
PLAYBACK_FULLPLAY,
PLAYBACK_FULLRECORD,
@@ -73,21 +75,31 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws
TASstate networkState = TASmodBufferBuilder.readEnum(TASstate.class, buf);
/* TODO Permissions */
- setTASState(networkState);
+ TASmod.gameLoopSchedulerServer.add(() -> {
+ setTASState(networkState);
+ });
break;
case PLAYBACK_CLEAR_INPUTS:
- clearInputs();
+ TASmod.gameLoopSchedulerServer.add(() -> {
+ clearInputs();
+ });
break;
case PLAYBACK_FULLRECORD:
- fullRecord();
+ TASmod.gameLoopSchedulerServer.add(() -> {
+ fullRecord();
+ });
break;
case PLAYBACK_FULLPLAY:
- fullPlay();
+ TASmod.gameLoopSchedulerServer.add(() -> {
+ fullPlay();
+ });
break;
case PLAYBACK_RESTARTANDPLAY:
String tasFileName = TASmodBufferBuilder.readString(buf);
- restartAndPlay(tasFileName);
+ TASmod.gameLoopSchedulerServer.add(() -> {
+ restartAndPlay(tasFileName);
+ });
break;
case PLAYBACK_SAVE:
case PLAYBACK_LOAD:
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java
index 1a383e98..c4bd6559 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java
@@ -69,7 +69,7 @@ public String[] getFileCommandNames() {
@Override
public void onControllerStateChange(TASstate newstate, TASstate oldstate) {
if (newstate == TASstate.RECORDING && monitorContainer.isEmpty()) {
- recordNull(0);
+ onRecord(0, null);
}
currentValues = null;
}
@@ -129,7 +129,7 @@ public void recordNull(long tick) {
@Override
public void onPlayback(long tick, InputContainer inputContainer) {
- currentValues = get(tick - 1);
+ currentValues = get(tick);
}
private MonitorContainer loadFromFile(long tick, String[] args) throws PlaybackLoadException {
diff --git a/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java
index bbd88526..4473c8e0 100644
--- a/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java
+++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java
@@ -1,5 +1,7 @@
package com.minecrafttas.tasmod.registries;
+import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_STATE_TEMP_SAVESTATE;
+
import org.lwjgl.input.Keyboard;
import com.minecrafttas.mctcommon.KeybindManager.IsKeyDownFunc;
@@ -46,6 +48,11 @@ public enum TASmodKeybinds implements KeybindID {
TASmodClient.virtual.CAMERA_ANGLE.updateNextCameraAngle(0, 45);
}),
TEST1("Various Testing", "TASmod", Keyboard.KEY_F12, () -> {
+ try {
+ TASmodClient.client.send(new TASmodBufferBuilder(PLAYBACK_STATE_TEMP_SAVESTATE).writeEnum(TASstate.RECORDING));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}, VirtualKeybindings::isKeyDown),
TEST2("Various Testing2", "TASmod", Keyboard.KEY_F7, () -> {
}, VirtualKeybindings::isKeyDown);
diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java
index 24e23965..64019477 100644
--- a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java
+++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java
@@ -269,7 +269,7 @@ else if (state == TASstate.PLAYBACK) {
private static void preload(BigArrayList containerList, long index) {
LOGGER.trace(LoggerMarkers.Savestate, "Preloading container at index {}", index);
InputContainer containerToPreload = containerList.get(index);
- TASmodClient.virtual.preloadInput(containerToPreload.getKeyboard(), containerToPreload.getMouse(), containerToPreload.getCameraAngle());
+ TASmodClient.virtual.preloadInputs(containerToPreload);
TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.onPlaybackTick(index, containerToPreload);
}
diff --git a/src/main/java/com/minecrafttas/tasmod/util/DebugWriter.java b/src/main/java/com/minecrafttas/tasmod/util/DebugWriter.java
index 58988346..83a3c674 100644
--- a/src/main/java/com/minecrafttas/tasmod/util/DebugWriter.java
+++ b/src/main/java/com/minecrafttas/tasmod/util/DebugWriter.java
@@ -16,6 +16,8 @@ public class DebugWriter {
private static Path debugTASFile = TASmodClient.tasfiledirectory.resolve("debug.mctas");
public static void writeDebugFile(PlaybackControllerClient controller) {
- PlaybackSerialiser.saveToFile(debugTASFile, controller, null);
+ if (System.getProperty("tasmod.playback.trace", "false").equals("true")) {
+ PlaybackSerialiser.saveToFile(debugTASFile, controller, null);
+ }
}
}
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java
index 5dc7264c..50a00047 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java
@@ -142,7 +142,7 @@ public void getStates(List reference) {
* @param camera The camera to copy from
*/
public void moveFrom(VirtualCameraAngle camera) {
- if (camera == null)
+ if (camera == null || this == camera)
return;
this.pitch = camera.pitch;
this.yaw = camera.yaw;
@@ -156,7 +156,7 @@ public void moveFrom(VirtualCameraAngle camera) {
* @param camera The camera to copy from
*/
public void deepCopyFrom(VirtualCameraAngle camera) {
- if (camera == null || !camera.isParent())
+ if (camera == null || this == camera || !camera.isParent())
return;
this.pitch = camera.pitch;
this.yaw = camera.yaw;
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java
index 00753400..7f868932 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java
@@ -13,6 +13,7 @@
import com.minecrafttas.tasmod.events.EventVirtualInput;
import com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer;
import com.minecrafttas.tasmod.mixin.playbackhooks.MixinMinecraft;
+import com.minecrafttas.tasmod.playback.PlaybackControllerClient.InputContainer;
import com.minecrafttas.tasmod.util.Ducks;
import com.minecrafttas.tasmod.util.Ducks.SubtickDuck;
import com.minecrafttas.tasmod.util.LoggerMarkers;
@@ -198,7 +199,24 @@ public void clearCurrent() {
MOUSE.clearCurrent();
}
- public void preloadInput(VirtualKeyboard keyboardToPreload, VirtualMouse mouseToPreload, VirtualCameraAngle angleToPreload) {
+ /**
+ * Preloads the nextInputs into the currentInputs
+ */
+ public void preloadInputs() {
+ preloadInputs(KEYBOARD.nextKeyboard, MOUSE.nextMouse, CAMERA_ANGLE.nextCameraAngle);
+ }
+
+ public void preloadInputs(InputContainer inputContainer) {
+ preloadInputs(inputContainer.getKeyboard(), inputContainer.getMouse(), inputContainer.getCameraAngle());
+ }
+
+ /**
+ * Preloads the next and current inputs with the Virtual Keyboard mouse and camera angle
+ * @param keyboardToPreload
+ * @param mouseToPreload
+ * @param angleToPreload
+ */
+ public void preloadInputs(VirtualKeyboard keyboardToPreload, VirtualMouse mouseToPreload, VirtualCameraAngle angleToPreload) {
// Preload the nextKeyboard
KEYBOARD.nextKeyboard.deepCopyFrom(keyboardToPreload);
MOUSE.nextMouse.deepCopyFrom(mouseToPreload);
@@ -209,6 +227,9 @@ public void preloadInput(VirtualKeyboard keyboardToPreload, VirtualMouse mouseTo
MOUSE.nextMouseTick();
// Preload vanilla inputs
+ if (Minecraft.getMinecraft() == null) { // If running in unit test env
+ return;
+ }
Minecraft.getMinecraft().runTickKeyboard(); // Letting mouse and keyboard tick once to load inputs into the "currentKeyboard"
Minecraft.getMinecraft().runTickMouse();
@@ -363,6 +384,10 @@ public boolean nextKeyboardSubtick() {
return isPolled;
}
+ public VirtualKeyboardEvent getCurrentEvent() {
+ return currentKeyboardEvent;
+ }
+
/**
* @return The keycode of {@link #currentKeyboardEvent}
*/
@@ -543,6 +568,10 @@ public boolean nextMouseSubtick() {
return isPolled;
}
+ public VirtualMouseEvent getCurrentEvent() {
+ return currentMouseEvent;
+ }
+
/**
* @return The keycode of {@link #currentMouseEvent}
*/
@@ -757,6 +786,10 @@ public void setCamera(Float pitch, Float yaw) {
nextCameraAngle.set(pitch, yaw);
}
+ public VirtualCameraAngle getCurrentCameraAngle() {
+ return currentCameraAngle;
+ }
+
/**
* @return The current pitch coordinate of the player. May be null when it's initialized
*/
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java
index b35260cc..00d71a96 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java
@@ -336,7 +336,7 @@ public VirtualKeyboard clone() {
@Override
public void moveFrom(VirtualKeyboard keyboard) {
- if (keyboard == null)
+ if (keyboard == null || this == keyboard)
return;
super.moveFrom(keyboard);
charList.clear();
@@ -346,7 +346,7 @@ public void moveFrom(VirtualKeyboard keyboard) {
@Override
public void copyFrom(VirtualKeyboard keyboard) {
- if (keyboard == null)
+ if (keyboard == null || this == keyboard)
return;
super.copyFrom(keyboard);
charList.clear();
@@ -355,7 +355,7 @@ public void copyFrom(VirtualKeyboard keyboard) {
@Override
public void deepCopyFrom(VirtualKeyboard keyboard) {
- if (keyboard == null)
+ if (keyboard == null || this == keyboard)
return;
super.deepCopyFrom(keyboard);
charList.clear();
diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java
index 1a475b77..71d25919 100644
--- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java
+++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java
@@ -295,7 +295,7 @@ public VirtualMouse clone() {
@Override
public void moveFrom(VirtualMouse mouse) {
- if (mouse == null)
+ if (mouse == null || this == mouse)
return;
super.moveFrom(mouse);
this.scrollWheel = mouse.scrollWheel;
@@ -306,7 +306,7 @@ public void moveFrom(VirtualMouse mouse) {
@Override
public void copyFrom(VirtualMouse mouse) {
- if (mouse == null)
+ if (mouse == null || this == mouse)
return;
super.copyFrom(mouse);
this.scrollWheel = mouse.scrollWheel;
@@ -316,7 +316,7 @@ public void copyFrom(VirtualMouse mouse) {
@Override
public void deepCopyFrom(VirtualMouse mouse) {
- if (mouse == null)
+ if (mouse == null || this == mouse)
return;
super.deepCopyFrom(mouse);
this.scrollWheel = mouse.scrollWheel;
diff --git a/src/main/resources/tasmod.mixin.json b/src/main/resources/tasmod.mixin.json
index 5d2c5e53..1c5e6b97 100644
--- a/src/main/resources/tasmod.mixin.json
+++ b/src/main/resources/tasmod.mixin.json
@@ -30,7 +30,6 @@
"killtherng.mathrand.MixinEntityXPOrb",
"killtherng.mathrand.MixinEntityTNTPrimed",
"killtherng.mathrand.MixinWorldEntitySpawner"
-
],
"client": [
// General
@@ -79,7 +78,7 @@
"fixes.MixinMouseHelper",
// KTRNG
- "killtherng.MixinRenderLivingBase"
+ "killtherng.MixinRenderLivingBase",
// Tickrate Rendering
"tickrate.MixinAudioPitch",
diff --git a/src/test/java/tasmod/TestUtil.java b/src/test/java/tasmod/TestUtil.java
new file mode 100644
index 00000000..7bd718c3
--- /dev/null
+++ b/src/test/java/tasmod/TestUtil.java
@@ -0,0 +1,8 @@
+package tasmod;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class TestUtil {
+ public static Logger LOGGER = LogManager.getLogger("TASmod TEST");
+}
diff --git a/src/test/java/tasmod/playback/PlaybackControllerClientTest.java b/src/test/java/tasmod/playback/PlaybackControllerClientTest.java
new file mode 100644
index 00000000..133d718c
--- /dev/null
+++ b/src/test/java/tasmod/playback/PlaybackControllerClientTest.java
@@ -0,0 +1,311 @@
+package tasmod.playback;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+
+import java.nio.file.Paths;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.dselent.bigarraylist.BigArrayList;
+import com.minecrafttas.mctcommon.events.EventListenerRegistry;
+import com.minecrafttas.tasmod.playback.PlaybackControllerClient;
+import com.minecrafttas.tasmod.playback.PlaybackControllerClient.InputContainer;
+import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate;
+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;
+import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent;
+import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent;
+
+import tasmod.TestUtil;
+
+class PlaybackControllerClientTest {
+
+ VirtualInput input;
+ PlaybackControllerClient controller;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ input = new VirtualInput(TestUtil.LOGGER);
+ controller = new PlaybackControllerClient(input, Paths.get("src/test/resources/temp"), TestUtil.LOGGER);
+ EventListenerRegistry.register(controller);
+ }
+
+ @AfterEach
+ void tearDown() {
+ EventListenerRegistry.unregister(controller);
+ }
+
+ /**
+ * Testing if the initial input when starting a recording is correctly set
+ */
+ @Test
+ void testStartRecord() {
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.LSHIFT.getKeycode(), true, '\0');
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.W.getKeycode(), true, 'w');
+ controller.setTASStateClient(TASstate.RECORDING, false);
+
+ InputContainer actual = controller.getInputs().get(0);
+
+ VirtualKeyboard keyboard = new VirtualKeyboard();
+ keyboard.updateFromEvent(VirtualKey.LSHIFT.getKeycode(), true, '\0');
+ keyboard.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w');
+ InputContainer expected = new InputContainer(keyboard, new VirtualMouse(), new VirtualCameraAngle());
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testRecord() {
+ controller.setTASStateClient(TASstate.RECORDING, false);
+
+ // Tick 1
+
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.W.getKeycode(), true, 'w');
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.LSHIFT.getKeycode(), true, '\0');
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.LSHIFT.getKeycode(), false, '\0');
+ input.MOUSE.updateNextMouse(VirtualKey.LC.getKeycode(), true, 0, 0, 0);
+ input.MOUSE.updateNextMouse(VirtualKey.LC.getKeycode(), false, 0, 0, 0);
+ input.CAMERA_ANGLE.setCamera(0F, 15F);
+
+ input.KEYBOARD.nextKeyboardTick();
+ input.MOUSE.nextMouseTick();
+ input.CAMERA_ANGLE.nextCameraTick();
+ controller.onClientTickPost(null);
+
+ // Tick 2
+
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.D.getKeycode(), true, 'd');
+ input.MOUSE.updateNextMouse(VirtualKey.MOUSEMOVED.getKeycode(), false, 20, 3, 13);
+ input.CAMERA_ANGLE.setCamera(5f, 18f);
+ input.CAMERA_ANGLE.updateNextCameraAngle(6, 19);
+ input.CAMERA_ANGLE.updateNextCameraAngle(7, 20);
+
+ input.KEYBOARD.nextKeyboardTick();
+ input.MOUSE.nextMouseTick();
+ input.CAMERA_ANGLE.nextCameraTick();
+ controller.onClientTickPost(null);
+
+ // Tick 3
+
+ input.KEYBOARD.updateNextKeyboard(VirtualKey.W.getKeycode(), false, '\0');
+ input.MOUSE.updateNextMouse(VirtualKey.MOUSEMOVED.getKeycode(), false, -20, 0, 0);
+ input.CAMERA_ANGLE.setCamera(0f, 0f);
+
+ input.KEYBOARD.nextKeyboardTick();
+ input.MOUSE.nextMouseTick();
+ input.CAMERA_ANGLE.nextCameraTick();
+ controller.onClientTickPost(null);
+
+ controller.setTASStateClient(TASstate.NONE);
+
+ BigArrayList actual = controller.getInputs();
+
+ // Expected
+
+ BigArrayList expected = new BigArrayList<>();
+
+ // Tick 0
+ expected.add(new InputContainer());
+
+ // Tick 1
+ VirtualKeyboard keyboard1 = new VirtualKeyboard();
+ VirtualMouse mouse1 = new VirtualMouse();
+ VirtualCameraAngle cameraAngle1 = new VirtualCameraAngle();
+
+ keyboard1.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w');
+ keyboard1.updateFromEvent(VirtualKey.LSHIFT.getKeycode(), true, '\0');
+ keyboard1.updateFromEvent(VirtualKey.LSHIFT.getKeycode(), false, '\0');
+ mouse1.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 0, 0);
+ mouse1.updateFromEvent(VirtualKey.LC.getKeycode(), false, 0, 0, 0);
+ cameraAngle1.set(0, 15);
+
+ expected.add(new InputContainer(keyboard1, mouse1, cameraAngle1));
+
+ // Tick 2
+
+ VirtualKeyboard keyboard2 = new VirtualKeyboard();
+ VirtualMouse mouse2 = new VirtualMouse();
+ VirtualCameraAngle cameraAngle2 = new VirtualCameraAngle();
+
+ keyboard2.updateFromEvent(VirtualKey.D.getKeycode(), true, 'd');
+ mouse2.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 20, 3, 13);
+ cameraAngle2.set(5, 18);
+ cameraAngle2.updateFromEvent(6, 19);
+ cameraAngle2.updateFromEvent(7, 20);
+
+ expected.add(new InputContainer(keyboard2, mouse2, cameraAngle2));
+
+ // Tick 3
+
+ VirtualKeyboard keyboard3 = new VirtualKeyboard();
+ VirtualMouse mouse3 = new VirtualMouse();
+ VirtualCameraAngle cameraAngle3 = new VirtualCameraAngle();
+
+ keyboard3.updateFromEvent(VirtualKey.W.getKeycode(), false, '\0');
+ mouse3.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, -20, 0, 0);
+ cameraAngle3.set(0, 0);
+
+ expected.add(new InputContainer(keyboard3, mouse3, cameraAngle3));
+
+ assertIterableEquals(expected, actual);
+ }
+
+ @Test
+ void testPlayback() {
+ // Expected
+
+ BigArrayList data = new BigArrayList<>();
+
+ // Tick 0
+ data.add(new InputContainer());
+
+ // Tick 1
+ VirtualKeyboard keyboard1 = new VirtualKeyboard();
+ VirtualMouse mouse1 = new VirtualMouse();
+ VirtualCameraAngle cameraAngle1 = new VirtualCameraAngle();
+
+ keyboard1.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w');
+ keyboard1.updateFromEvent(VirtualKey.LSHIFT.getKeycode(), true, '\0');
+ keyboard1.updateFromEvent(VirtualKey.LSHIFT.getKeycode(), false, '\0');
+ mouse1.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 0, 0);
+ mouse1.updateFromEvent(VirtualKey.LC.getKeycode(), false, 0, 0, 0);
+ cameraAngle1.set(0, 15);
+
+ data.add(new InputContainer(keyboard1, mouse1, cameraAngle1));
+
+ // Tick 2
+
+ VirtualKeyboard keyboard2 = new VirtualKeyboard();
+ VirtualMouse mouse2 = new VirtualMouse();
+ VirtualCameraAngle cameraAngle2 = new VirtualCameraAngle();
+
+ keyboard2.updateFromEvent(VirtualKey.D.getKeycode(), true, 'd');
+ mouse2.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 20, 3, 13);
+ cameraAngle2.updateFromEvent(5, 18);
+ cameraAngle2.updateFromEvent(6, 19);
+ cameraAngle2.updateFromEvent(7, 20);
+
+ data.add(new InputContainer(keyboard2, mouse2, cameraAngle2));
+
+ // Tick 3
+
+ VirtualKeyboard keyboard3 = new VirtualKeyboard();
+ VirtualMouse mouse3 = new VirtualMouse();
+ VirtualCameraAngle cameraAngle3 = new VirtualCameraAngle();
+
+ keyboard3.updateFromEvent(VirtualKey.W.getKeycode(), false, '\0');
+ mouse3.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, -20, 0, 0);
+ cameraAngle3.set(0, 0);
+
+ data.add(new InputContainer(keyboard3, mouse3, cameraAngle3));
+
+ // Test
+
+ controller.setInputs(data);
+
+ controller.setTASStateClient(TASstate.PLAYBACK);
+
+ // Tick 1
+ controller.onClientTickPre(null);
+ input.KEYBOARD.nextKeyboardTick();
+ input.MOUSE.nextMouseTick();
+
+ VirtualKeyboardEvent expectedK;
+ VirtualMouseEvent expectedM;
+
+ // Subtick 1
+ input.KEYBOARD.nextKeyboardSubtick();
+ expectedK = new VirtualKeyboardEvent(VirtualKey.W.getKeycode(), true, 'w');
+ assertEquals(expectedK, input.KEYBOARD.getCurrentEvent());
+
+ input.MOUSE.nextMouseSubtick();
+ expectedM = new VirtualMouseEvent(VirtualKey.LC.getKeycode(), true, 0, 0, 0);
+ assertEquals(expectedM, input.MOUSE.getCurrentEvent());
+
+ input.CAMERA_ANGLE.nextCameraTick();
+ VirtualCameraAngle expectedC1 = new VirtualCameraAngle(0f, 15f);
+ assertEquals(expectedC1, input.CAMERA_ANGLE.getCurrentCameraAngle());
+
+ // Subtick 2
+ input.KEYBOARD.nextKeyboardSubtick();
+ expectedK = new VirtualKeyboardEvent(VirtualKey.LSHIFT.getKeycode(), true, '\0');
+ assertEquals(expectedK, input.KEYBOARD.getCurrentEvent());
+
+ input.MOUSE.nextMouseSubtick();
+ expectedM = new VirtualMouseEvent(VirtualKey.LC.getKeycode(), false, 0, 0, 0);
+ assertEquals(expectedM, input.MOUSE.getCurrentEvent());
+
+ // Subtick 3
+ input.KEYBOARD.nextKeyboardSubtick();
+ expectedK = new VirtualKeyboardEvent(VirtualKey.LSHIFT.getKeycode(), false, '\0');
+ assertEquals(expectedK, input.KEYBOARD.getCurrentEvent());
+
+ // Tick 2
+ controller.onClientTickPre(null);
+ input.KEYBOARD.nextKeyboardTick();
+ input.MOUSE.nextMouseTick();
+
+ // Subtick 1
+ input.KEYBOARD.nextKeyboardSubtick();
+ expectedK = new VirtualKeyboardEvent(VirtualKey.W.getKeycode(), false, '\0');
+ assertEquals(expectedK, input.KEYBOARD.getCurrentEvent());
+
+ input.MOUSE.nextMouseSubtick();
+ expectedM = new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 20, 3, 13);
+ assertEquals(expectedM, input.MOUSE.getCurrentEvent());
+
+ input.CAMERA_ANGLE.nextCameraTick();
+ VirtualCameraAngle expectedC2 = new VirtualCameraAngle();
+ expectedC2.deepCopyFrom(expectedC1);
+ expectedC2.updateFromEvent(5f, 18f);
+ assertEquals(expectedC2, input.CAMERA_ANGLE.getCurrentCameraAngle());
+
+ // Subtick 2
+ input.KEYBOARD.nextKeyboardSubtick();
+ expectedK = new VirtualKeyboardEvent(VirtualKey.D.getKeycode(), true, 'd');
+ assertEquals(expectedK, input.KEYBOARD.getCurrentEvent());
+
+ input.CAMERA_ANGLE.nextCameraTick();
+ VirtualCameraAngle expectedC3 = new VirtualCameraAngle();
+ expectedC3.deepCopyFrom(expectedC2);
+ expectedC3.updateFromEvent(6f, 19f);
+ assertEquals(expectedC3, input.CAMERA_ANGLE.getCurrentCameraAngle());
+
+ // Subtick 3
+ input.CAMERA_ANGLE.nextCameraTick();
+ VirtualCameraAngle expectedC4 = new VirtualCameraAngle();
+ expectedC4.deepCopyFrom(expectedC3);
+ expectedC4.updateFromEvent(6f, 19f);
+ assertEquals(expectedC4, input.CAMERA_ANGLE.getCurrentCameraAngle());
+
+ // Subtick 4
+ input.CAMERA_ANGLE.nextCameraTick();
+ VirtualCameraAngle expectedC5 = new VirtualCameraAngle();
+ expectedC5.deepCopyFrom(expectedC4);
+ expectedC5.updateFromEvent(7f, 20f);
+ assertEquals(expectedC5, input.CAMERA_ANGLE.getCurrentCameraAngle());
+
+ // Tick 3
+ controller.onClientTickPre(null);
+ input.KEYBOARD.nextKeyboardTick();
+ input.MOUSE.nextMouseTick();
+
+ input.KEYBOARD.nextKeyboardSubtick();
+ expectedK = new VirtualKeyboardEvent(VirtualKey.D.getKeycode(), false, '\0');
+ assertEquals(expectedK, input.KEYBOARD.getCurrentEvent());
+
+ input.MOUSE.nextMouseSubtick();
+ expectedM = new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, -20, 0, 0);
+ assertEquals(expectedM, input.MOUSE.getCurrentEvent());
+
+ input.CAMERA_ANGLE.nextCameraTick();
+ VirtualCameraAngle expectedC6 = new VirtualCameraAngle(0f, 0f);
+ assertEquals(expectedC6, input.CAMERA_ANGLE.getCurrentCameraAngle());
+ }
+}