diff --git a/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java b/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java index 4dee7837..dd6c9cbe 100644 --- a/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java +++ b/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java @@ -7,6 +7,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.network.NetHandlerPlayClient; /** * Contains all events fired on the client side @@ -59,6 +60,21 @@ public static interface EventDoneLoadingWorld extends EventBase { public void onDoneLoadingWorld(); } + /** + * Fired when the player is done loading, after the position has been loaded from the server side + * @author Scribble + * @see NetHandlerPlayClient#handlePlayerPosLook(net.minecraft.network.play.server.SPacketPlayerPosLook) + */ + @FunctionalInterface + public static interface EventDoneLoadingPlayer extends EventBase { + + /** + * Fired when the player is done loading, after the position has been loaded from the server side + * @see NetHandlerPlayClient#handlePlayerPosLook(net.minecraft.network.play.server.SPacketPlayerPosLook) + */ + public void onDoneLoadingPlayer(); + } + /** * Fired when the client ticks * @author Scribble diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java index 7d164842..3cc890c9 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java @@ -3,12 +3,15 @@ import static com.minecrafttas.tasmod.TASmod.LOGGER; import com.minecrafttas.mctcommon.events.EventClient.EventClientGameLoop; +import com.minecrafttas.mctcommon.events.EventClient.EventDoneLoadingPlayer; import com.minecrafttas.mctcommon.events.EventClient.EventDoneLoadingWorld; import com.minecrafttas.mctcommon.events.EventClient.EventLaunchIntegratedServer; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.minecrafttas.tasmod.virtual.VirtualInput; import net.minecraft.client.Minecraft; @@ -17,7 +20,7 @@ * * @author Scribble */ -public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventClientGameLoop, EventDoneLoadingWorld { +public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventClientGameLoop, EventDoneLoadingWorld, EventDoneLoadingPlayer { private boolean waszero; private boolean isLoading; @@ -62,7 +65,6 @@ public void onDoneLoadingWorld() { LOGGER.debug(LoggerMarkers.Event, "Finished loading the world on the client"); loadingScreenDelay = 1; - TASmodClient.virtual.clear(); } } @@ -70,4 +72,25 @@ public boolean isLoading() { return isLoading; } + /** + * {@inheritDoc} + * + *

Fixes an issue, where the look position of the player is reset to 0 -180,
+ * As well as removing any keyboard inputs present in the main menu + * + *

{@link MixinEntityRenderer#runUpdate(float)} rewrites the camera input,
+ * So that it can be used with interpolation.
+ * However, when you start the game, this camera input needs to be initialised with the current look position from the server.
+ * So a special condition is set, that if the {@link VirtualInput#CAMERA_ANGLE} is null,
+ * it intialises the {@link VirtualInput#CAMERA_ANGLE CAMERA_ANGLE} with the current player camera angle. + * + *

So {@link VirtualInput#clear()} has to be called at the right moment in the player initialisation
+ * to set the correct values. Before that, the playerRotation defaults to 0 -180 + */ + @Override + public void onDoneLoadingPlayer() { + LOGGER.debug(LoggerMarkers.Event, "Finished loading the player position on the client"); + TASmodClient.virtual.clear(); + } + } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinNetHandlerPlayClient.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinNetHandlerPlayClient.java new file mode 100644 index 00000000..c11ed085 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinNetHandlerPlayClient.java @@ -0,0 +1,20 @@ +package com.minecrafttas.tasmod.mixin.events; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.minecrafttas.mctcommon.events.EventClient.EventDoneLoadingPlayer; +import com.minecrafttas.mctcommon.events.EventListenerRegistry; + +import net.minecraft.client.network.NetHandlerPlayClient; + +@Mixin(NetHandlerPlayClient.class) +public class MixinNetHandlerPlayClient { + + @Inject(method = "handlePlayerPosLook", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;displayGuiScreen(Lnet/minecraft/client/gui/GuiScreen;)V")) + public void event_handlePlayerPosLook(CallbackInfo ci) { + EventListenerRegistry.fireEvent(EventDoneLoadingPlayer.class); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldServer.java index 5572281b..c3f8b06b 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldServer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldServer.java @@ -17,7 +17,7 @@ * @author Scribble */ @Mixin(WorldServer.class) -public abstract class MixinWorldServer implements WorldServerDuck { +public class MixinWorldServer implements WorldServerDuck { @Shadow private PlayerChunkMap playerChunkMap; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index 4d372071..90e3681d 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -539,12 +539,7 @@ public void setInputs(BigArrayList inputs) { } public void setInputs(BigArrayList inputs, long index) { - try { - this.inputs.clearMemory(); - } catch (IOException e) { - e.printStackTrace(); - } - this.inputs = new BigArrayList(tasFileDirectory + File.separator + "temp"); + clearInputList(); SerialiserFlavorBase.addAll(this.inputs, inputs); setIndex(index); } @@ -582,14 +577,19 @@ public InputContainer get() { public void clear() { LOGGER.info(LoggerMarkers.Playback, "Clearing playback controller"); + clearInputList(); EventListenerRegistry.fireEvent(EventPlaybackClient.EventRecordClear.class); + + index = 0; + } + + private void clearInputList() { try { inputs.clearMemory(); } catch (IOException e) { e.printStackTrace(); } inputs = new BigArrayList(tasFileDirectory + File.separator + "temp"); - index = 0; } /** diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java index 7a4764d3..63703a1a 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java @@ -2,9 +2,10 @@ import static com.minecrafttas.tasmod.TASmod.LOGGER; -import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.mctcommon.events.EventListenerRegistry; @@ -50,7 +51,7 @@ */ public class SavestateHandlerClient implements ClientPacketHandler, EventSavestate.EventClientCompleteLoadstate, EventSavestate.EventClientLoadPlayer { - public final static File savestateDirectory = TASmodClient.savestatedirectory.toFile(); //TODO Change to path... don't want to deal with this rn ._. + public final static Path clientSavestateDirectory = TASmodClient.tasfiledirectory.resolve("savestates"); /** * A bug occurs when unloading the client world. The client world has a @@ -122,15 +123,15 @@ public static void savestate(String nameOfSavestate) throws SavestateException, return; } - SavestateHandlerClient.savestateDirectory.mkdir(); + createClientSavestateDirectory(); - File targetfile = new File(SavestateHandlerClient.savestateDirectory, nameOfSavestate + ".mctas"); + Path targetfile = clientSavestateDirectory.resolve(nameOfSavestate + ".mctas"); PlaybackControllerClient container = TASmodClient.controller; if (container.isRecording()) { - PlaybackSerialiser.saveToFile(targetfile.toPath(), container, ""); // If the container is recording, store it entirely + PlaybackSerialiser.saveToFile(targetfile, container, ""); // If the container is recording, store it entirely } else if (container.isPlayingback()) { - PlaybackSerialiser.saveToFile(targetfile.toPath(), container, "", container.index()); // If the container is playing, store it until the current index + PlaybackSerialiser.saveToFile(targetfile, container, "", container.index()); // If the container is playing, store it until the current index } } @@ -150,13 +151,14 @@ public static void loadstate(String nameOfSavestate) throws Exception { return; } - savestateDirectory.mkdir(); - PlaybackControllerClient controller = TASmodClient.controller; TASstate state = controller.getState(); if (state == TASstate.NONE) { + TASmodClient.tickSchedulerClient.add(() -> { + EventListenerRegistry.fireEvent(EventSavestate.EventClientCompleteLoadstate.class); + }); return; } @@ -164,12 +166,12 @@ public static void loadstate(String nameOfSavestate) throws Exception { state = controller.getStateAfterPause(); } - File targetfile = new File(savestateDirectory, nameOfSavestate + ".mctas"); + Path targetfile = clientSavestateDirectory.resolve(nameOfSavestate + ".mctas"); BigArrayList savestateContainerList; - if (targetfile.exists()) { - savestateContainerList = PlaybackSerialiser.loadFromFile(targetfile.toPath(), state != TASstate.PLAYBACK); + if (Files.exists(targetfile)) { + savestateContainerList = PlaybackSerialiser.loadFromFile(targetfile, state != TASstate.PLAYBACK); } else { controller.setTASStateClient(TASstate.NONE, false); Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "Inputs could not be loaded for this savestate,")); @@ -199,12 +201,14 @@ public static void loadstate(String nameOfSavestate) throws Exception { if (state == TASstate.RECORDING) { long index = savestateContainerList.size() - 1; + preload(savestateContainerList, index); controller.setInputs(savestateContainerList, index); - - /* - * When loading a savestate during a playback 2 different scenarios can happen. - * */ - } else if (state == TASstate.PLAYBACK) { + TASmodClient.virtual.clear(); + } + /* + * When loading a savestate during a playback 2 different scenarios can happen. + */ + else if (state == TASstate.PLAYBACK) { /* * Scenario 1: @@ -233,6 +237,7 @@ public static void loadstate(String nameOfSavestate) throws Exception { preload(controller.getInputs(), index); controller.setIndex(index); + TASmodClient.virtual.clear(); } /* * Scenario 2: @@ -254,7 +259,13 @@ public static void loadstate(String nameOfSavestate) throws Exception { }); } + private static void createClientSavestateDirectory() throws IOException { + LOGGER.trace(LoggerMarkers.Savestate, "Creating savestate directory at {}", clientSavestateDirectory); + Files.createDirectories(clientSavestateDirectory); + } + 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()); @@ -289,8 +300,8 @@ public static void loadPlayer(NBTTagCompound compound) { double z = player.motionZ; float rx = player.moveForward; - float ry = player.moveStrafing; - float rz = player.moveVertical; + float ry = player.moveVertical; + float rz = player.moveStrafing; boolean sprinting = player.isSprinting(); float jumpVector = player.jumpMovementFactor; diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java index fdb69c59..e7d5b18b 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java @@ -410,12 +410,17 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex TASmod.tickratechanger.pauseGame(false); } + // Unlock savestating + state = SavestateState.NONE; + + /* + * TODO Savestates can be reloaded without a tick passing... + * And since this scheduler is not cleared, it would execute the same task multiple times in the next tick + * Rn it's not a problem, but this should be looked at... + */ TASmod.tickSchedulerServer.add(() -> { EventListenerRegistry.fireEvent(EventSavestate.EventServerCompleteLoadstate.class); onLoadstateComplete(); - - // Unlock savestating - state = SavestateState.NONE; }); } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateWorldHandler.java b/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateWorldHandler.java index ad117d1f..33694a9c 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateWorldHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateWorldHandler.java @@ -239,6 +239,7 @@ public void loadAllWorlds(String string, String string2) { } server.worlds[i].addEventListener(new ServerWorldEventHandler(server, server.worlds[i])); + server.worlds[i].tick(); // TODO I give up... } server.getPlayerList().setPlayerManager(server.worlds); diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java index 60558537..5dc7264c 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java @@ -133,7 +133,7 @@ public void set(float pitch, float yaw) { public void getStates(List reference) { if (isParent()) { reference.addAll(subtickList); - reference.add(this); + reference.add(this.shallowClone()); // Fixes a crash where "this" can become null in the reference. } } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java index 1dbf2a04..440074af 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java @@ -141,11 +141,19 @@ public void clear() { } public void preloadInput(VirtualKeyboard keyboardToPreload, VirtualMouse mouseToPreload, VirtualCameraAngle angleToPreload) { + // Preload the nextKeyboard KEYBOARD.nextKeyboard.deepCopyFrom(keyboardToPreload); MOUSE.nextMouse.deepCopyFrom(mouseToPreload); CAMERA_ANGLE.nextCameraAngle.deepCopyFrom(angleToPreload); + + // Preload the currentKeyboard + KEYBOARD.nextKeyboardTick(); + MOUSE.nextMouseTick(); + + // Preload vanilla inputs Minecraft.getMinecraft().runTickKeyboard(); // Letting mouse and keyboard tick once to load inputs into the "currentKeyboard" Minecraft.getMinecraft().runTickMouse(); + SubtickDuck entityRenderer = (SubtickDuck) Minecraft.getMinecraft().entityRenderer; entityRenderer.runUpdate(0); } @@ -262,7 +270,7 @@ public void updateNextKeyboard(int keycode, boolean keystate, char character) { * @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) + LOGGER.debug(LoggerMarkers.Keyboard, "Update: {}, {}, {}", keycode, keystate, character); // Activate with -Dtasmod.marker.keyboard=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) nextKeyboard.updateFromEvent(keycode, keystate, character, repeatEventsEnabled); } @@ -277,6 +285,7 @@ public void nextKeyboardTick() { nextKeyboard.deepCopyFrom((VirtualKeyboard) EventListenerRegistry.fireEvent(EventVirtualInput.EventVirtualKeyboardTick.class, nextKeyboard)); currentKeyboard.getVirtualEvents(nextKeyboard, keyboardEventQueue); currentKeyboard.moveFrom(nextKeyboard); + LOGGER.debug(LoggerMarkers.Keyboard, "KeyboardTick: {}", currentKeyboard); // Activate with -Dtasmod.marker.keyboard=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) } /** @@ -693,8 +702,10 @@ public Triple getInterpolatedState(float partialTick, float if (enable && !cameraAngleInterpolationStates.isEmpty()) { int index = (int) MathHelper.clampedLerp(0, cameraAngleInterpolationStates.size() - 1, partialTick); // Get interpolate index - interpolatedPitch = cameraAngleInterpolationStates.get(index).getPitch(); - interpolatedYaw = cameraAngleInterpolationStates.get(index).getYaw() + 180; + VirtualCameraAngle interpolatedCamera = cameraAngleInterpolationStates.get(index); + + interpolatedPitch = interpolatedCamera.getPitch(); + interpolatedYaw = interpolatedCamera.getYaw() + 180; } return Triple.of(interpolatedPitch, interpolatedYaw, 0f); diff --git a/src/main/resources/tasmod.mixin.json b/src/main/resources/tasmod.mixin.json index ed795f87..7c67b9f2 100644 --- a/src/main/resources/tasmod.mixin.json +++ b/src/main/resources/tasmod.mixin.json @@ -43,6 +43,7 @@ // Events "events.MixinGuiIngame", "events.MixinEntityRenderer", + "events.MixinNetHandlerPlayClient", "clientcommands.MixinEntityPlayerSP", "clientcommands.MixinTabCompleter",