Skip to content

Commit 509c761

Browse files
authored
[VirtualInput] Fix crash when playing back while changing dimensions (#248)
In #240 I added a "[VirtualInput] Fix camera being reset to 0 -180 when entering a world", which introduced this crash. Reason is that the "onDoneLoadingPlayer" event, that I introduced in that PR, is also fired when switching dimensions. And switching dimensions in this case sets the virtual camera to null, which crashes the playback with a NullPointerException. And being once again confronted with this "clearing the virtual camera angle at the right moment" problem I realized that this system is pretty idiotic... To fix this, the virtual camera is now set to null when leaving the game and then initialised when the player is done loading. This way, the player should retain it's camera angle on world load and the camera angle shouldn't become null unexpectedly. Changes - Rewrote camera resetting and moved it to LoadingScreenHandler - Seperated clearing the next camera angle from clear() and renamed the method to clearKeys() - Added clearNext() to keyboard, mouse and camera - Updated documentation - Changed method names in MixinEntityRenderer to start with `playback` for better consistency
2 parents 50e1564 + aff8f61 commit 509c761

File tree

8 files changed

+104
-69
lines changed

8 files changed

+104
-69
lines changed

src/main/java/com/minecrafttas/mctcommon/events/EventClient.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,19 @@ public static interface EventDoneLoadingWorld extends EventBase {
6262
}
6363

6464
/**
65-
* Fired when the player is done loading, after the position has been loaded from the server side
65+
* Fired when the player is done loading, after the position has been loaded from the server side.<br>
66+
* This also fires every time the player switches dimensions.
67+
*
6668
* @author Scribble
6769
* @see NetHandlerPlayClient#handlePlayerPosLook(net.minecraft.network.play.server.SPacketPlayerPosLook)
6870
*/
6971
@FunctionalInterface
7072
public static interface EventDoneLoadingPlayer extends EventBase {
7173

7274
/**
73-
* Fired when the player is done loading, after the position has been loaded from the server side
75+
* Fired when the player is done loading, after the position has been loaded from the server side.<br>
76+
* This also fires every time the player switches dimensions.
77+
*
7478
* @see NetHandlerPlayClient#handlePlayerPosLook(net.minecraft.network.play.server.SPacketPlayerPosLook)
7579
*/
7680
public void onDoneLoadingPlayer();

src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,23 @@
66
import com.minecrafttas.mctcommon.events.EventClient.EventDoneLoadingPlayer;
77
import com.minecrafttas.mctcommon.events.EventClient.EventDoneLoadingWorld;
88
import com.minecrafttas.mctcommon.events.EventClient.EventLaunchIntegratedServer;
9+
import com.minecrafttas.mctcommon.events.EventClient.EventPlayerJoinedClientSide;
10+
import com.minecrafttas.mctcommon.events.EventClient.EventPlayerLeaveClientSide;
911
import com.minecrafttas.tasmod.TASmod;
1012
import com.minecrafttas.tasmod.TASmodClient;
11-
import com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer;
1213
import com.minecrafttas.tasmod.playback.PlaybackControllerClient;
1314
import com.minecrafttas.tasmod.util.LoggerMarkers;
14-
import com.minecrafttas.tasmod.virtual.VirtualInput;
15+
import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput;
1516

1617
import net.minecraft.client.Minecraft;
18+
import net.minecraft.client.entity.EntityPlayerSP;
1719

1820
/**
1921
* Handles logic during a loading screen to transition between states.
2022
*
2123
* @author Scribble
2224
*/
23-
public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventClientGameLoop, EventDoneLoadingWorld, EventDoneLoadingPlayer {
25+
public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventClientGameLoop, EventDoneLoadingWorld, EventDoneLoadingPlayer, EventPlayerJoinedClientSide, EventPlayerLeaveClientSide {
2426

2527
private boolean waszero;
2628
private boolean isLoading;
@@ -75,22 +77,40 @@ public boolean isLoading() {
7577
/**
7678
* {@inheritDoc}
7779
*
78-
* <p>Fixes an issue, where the look position of the player is reset to 0 -180,<br>
79-
* As well as removing any keyboard inputs present in the main menu
80-
*
81-
* <p>{@link MixinEntityRenderer#runUpdate(float)} rewrites the camera input,<br>
82-
* So that it can be used with interpolation. <br>
83-
* However, when you start the game, this camera input needs to be initialised with the current look position from the server.<br>
84-
* So a special condition is set, that if the {@link VirtualInput#CAMERA_ANGLE} is null,<br>
85-
* it intialises the {@link VirtualInput#CAMERA_ANGLE CAMERA_ANGLE} with the current player camera angle.
86-
*
87-
* <p>So {@link VirtualInput#clear()} has to be called at the right moment in the player initialisation<br>
88-
* to set the correct values. Before that, the playerRotation defaults to 0 -180
80+
* <p>Initializes the virtual camera to be in line with the vanilla camera.
8981
*/
9082
@Override
9183
public void onDoneLoadingPlayer() {
9284
LOGGER.debug(LoggerMarkers.Event, "Finished loading the player position on the client");
93-
TASmodClient.virtual.clear();
85+
VirtualCameraAngleInput cameraAngle = TASmodClient.virtual.CAMERA_ANGLE;
86+
if (cameraAngle.getCurrentPitch() == null || cameraAngle.getCurrentYaw() == null) {
87+
LOGGER.debug("Setting the initial pitch and yaw");
88+
Minecraft mc = Minecraft.getMinecraft();
89+
EntityPlayerSP player = mc.player;
90+
cameraAngle.setCamera(player.rotationPitch, player.rotationYaw);
91+
}
92+
}
93+
94+
/**
95+
* {@inheritDoc}
96+
*
97+
* <p>Fixes stuck keys when loading the world
98+
*/
99+
@Override
100+
public void onPlayerJoinedClientSide(EntityPlayerSP player) {
101+
TASmodClient.virtual.clearKeys();
94102
}
95103

104+
/**
105+
* {@inheritDoc}
106+
*
107+
* <p>Resets the camera angle when leaving the world.
108+
* <p>If you later rejoin the world {@link #onDoneLoadingPlayer()} will re-initialise the camera angle
109+
*/
110+
@Override
111+
public void onPlayerLeaveClientSide(EntityPlayerSP player) {
112+
LOGGER.debug(LoggerMarkers.Event, "Finished leaving on the on the client side");
113+
LOGGER.debug("Resetting the camera angle on leaving the world");
114+
TASmodClient.virtual.CAMERA_ANGLE.clearNext();
115+
}
96116
}

src/main/java/com/minecrafttas/tasmod/mixin/events/MixinEntityRenderer.java

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111

1212
import com.llamalad7.mixinextras.sugar.Share;
1313
import com.llamalad7.mixinextras.sugar.ref.LocalFloatRef;
14+
import com.minecrafttas.mctcommon.events.EventListenerRegistry;
1415
import com.minecrafttas.tasmod.TASmodClient;
16+
import com.minecrafttas.tasmod.events.EventClient.EventDrawHotbarAlways;
1517
import com.minecrafttas.tasmod.util.Ducks.SubtickDuck;
1618
import com.minecrafttas.tasmod.virtual.VirtualInput;
1719

1820
import net.minecraft.client.Minecraft;
1921
import net.minecraft.client.entity.EntityPlayerSP;
22+
import net.minecraft.client.gui.ScaledResolution;
2023
import net.minecraft.client.renderer.EntityRenderer;
2124
import net.minecraft.client.renderer.GlStateManager;
2225

@@ -114,11 +117,19 @@ public void runUpdate(float partialTicks) {
114117
Float newPitch = TASmodClient.virtual.CAMERA_ANGLE.getCurrentPitch();
115118
Float newYaw = TASmodClient.virtual.CAMERA_ANGLE.getCurrentYaw();
116119

117-
// If the pitch or yaw is null (usually on initialize or when the player joins the world),
118-
// set nextCameraAngle to the current absolute camera coordinates.
119-
// This ensures that the camera position is loaded correctly
120+
/*
121+
* If the pitch or yaw is null,
122+
* usually on initialize or when the player joins the world),
123+
* do not update the camera angle.
124+
*
125+
* This is called during the loading screen for 2 game loops,
126+
* at which point the player is not initialized,
127+
* hence we do not have the correct camera angle yet.
128+
*
129+
* The angle is instead initialized in LoadingScreenHandler#onDoneLoadingPlayer.
130+
*/
120131
if (newPitch == null || newYaw == null) {
121-
TASmodClient.virtual.CAMERA_ANGLE.setCamera(prevPitch, prevYaw);
132+
// TASmodClient.virtual.CAMERA_ANGLE.setCamera(prevPitch, prevYaw);
122133
return;
123134
}
124135

@@ -138,7 +149,7 @@ public void runUpdate(float partialTicks) {
138149
* @return 0f for disabeling this method
139150
*/
140151
@ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 8), index = 0)
141-
public float redirect_orientCameraPitch(float pitch, @Share("pitch") LocalFloatRef sharedPitch) {
152+
public float playback_orientCameraPitch(float pitch, @Share("pitch") LocalFloatRef sharedPitch) {
142153
sharedPitch.set(pitch);
143154
return 0f;
144155
}
@@ -150,7 +161,7 @@ public float redirect_orientCameraPitch(float pitch, @Share("pitch") LocalFloatR
150161
* @return The redirected yaw
151162
*/
152163
@ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 9), index = 0)
153-
public float redirect_orientCameraYawAnimal(float yawAnimal, @Share("pitch") LocalFloatRef sharedPitch) {
164+
public float playback_orientCameraYawAnimal(float yawAnimal, @Share("pitch") LocalFloatRef sharedPitch) {
154165
return redirectCam(sharedPitch.get(), yawAnimal);
155166
}
156167

@@ -161,10 +172,27 @@ public float redirect_orientCameraYawAnimal(float yawAnimal, @Share("pitch") Loc
161172
* @return The redirected yaw
162173
*/
163174
@ModifyArg(method = "orientCamera", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;rotate(FFFF)V", ordinal = 10), index = 0)
164-
public float redirect_orientCameraYaw(float yaw, @Share("pitch") LocalFloatRef sharedPitch) {
175+
public float playback_orientCameraYaw(float yaw, @Share("pitch") LocalFloatRef sharedPitch) {
165176
return redirectCam(sharedPitch.get(), yaw);
166177
}
167178

179+
/**
180+
* Updates the game overlay and adds an event
181+
* @param ci CallBackInfo
182+
*/
183+
@Inject(method = "updateCameraAndRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/Profiler;endStartSection(Ljava/lang/String;)V"))
184+
public void playback_updateOverlay(CallbackInfo ci) {
185+
ScaledResolution scaledResolution = new ScaledResolution(this.mc);
186+
GlStateManager.clear(256);
187+
GlStateManager.matrixMode(5889);
188+
GlStateManager.loadIdentity();
189+
GlStateManager.ortho(0.0, scaledResolution.getScaledWidth_double(), scaledResolution.getScaledHeight_double(), 0.0, 1000.0, 3000.0);
190+
GlStateManager.matrixMode(5888);
191+
GlStateManager.loadIdentity();
192+
GlStateManager.translate(0.0F, 0.0F, -2000.0F);
193+
EventListenerRegistry.fireEvent(EventDrawHotbarAlways.class);
194+
}
195+
168196
/**
169197
* Turns the camera via GLStateManager
170198
* @param pitch The pi

src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) {
245245
LOGGER.debug(LoggerMarkers.Playback, "Pausing a playback");
246246
state = TASstate.PAUSED;
247247
stateAfterPause = TASstate.PLAYBACK;
248-
TASmodClient.virtual.clear();
248+
TASmodClient.virtual.clearKeys();
249249
return verbose ? TextFormatting.GREEN + "Pausing a playback" : "";
250250
case NONE:
251251
stopPlayback(true);
@@ -291,7 +291,7 @@ private void startRecording() {
291291

292292
private void stopRecording() {
293293
LOGGER.debug(LoggerMarkers.Playback, "Stopping a recording");
294-
TASmodClient.virtual.clear();
294+
TASmodClient.virtual.clearKeys();
295295
}
296296

297297
private void startPlayback() {
@@ -305,7 +305,7 @@ private void stopPlayback(boolean clearInputs) {
305305
LOGGER.debug(LoggerMarkers.Playback, "Stopping a playback");
306306
Minecraft.getMinecraft().gameSettings.chatLinks = true;
307307
if (clearInputs) {
308-
TASmodClient.virtual.clear();
308+
TASmodClient.virtual.clearKeys();
309309
}
310310
}
311311

src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public static void loadstate(String nameOfSavestate) throws Exception {
203203

204204
preload(savestateContainerList, index);
205205
controller.setInputs(savestateContainerList, index);
206-
TASmodClient.virtual.clear();
206+
TASmodClient.virtual.clearKeys();
207207
}
208208
/*
209209
* When loading a savestate during a playback 2 different scenarios can happen.
@@ -237,7 +237,7 @@ else if (state == TASstate.PLAYBACK) {
237237

238238
preload(controller.getInputs(), index);
239239
controller.setIndex(index);
240-
TASmodClient.virtual.clear();
240+
TASmodClient.virtual.clearKeys();
241241
}
242242
/*
243243
* Scenario 2:

src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,9 @@ public boolean willKeyBeDown(int keycode) {
134134
/**
135135
* Unpresses all keys in {@link VirtualKeyboardInput#nextKeyboard} and {@link VirtualMouseInput#nextMouse}
136136
*/
137-
public void clear() {
138-
KEYBOARD.nextKeyboard.clear();
139-
MOUSE.nextMouse.clear();
140-
CAMERA_ANGLE.nextCameraAngle.clear();
137+
public void clearKeys() {
138+
KEYBOARD.clearNext();
139+
MOUSE.clearNext();
141140
}
142141

143142
public void preloadInput(VirtualKeyboard keyboardToPreload, VirtualMouse mouseToPreload, VirtualCameraAngle angleToPreload) {
@@ -340,6 +339,13 @@ public boolean isKeyDown(int keycode) {
340339
public boolean willKeyBeDown(int keycode) {
341340
return nextKeyboard.isKeyDown(keycode);
342341
}
342+
343+
/**
344+
* Clears the {@link #nextKeyboard}
345+
*/
346+
public void clearNext() {
347+
nextKeyboard.clear();
348+
}
343349
}
344350

345351
/**
@@ -531,6 +537,12 @@ public boolean willKeyBeDown(int keycode) {
531537
return nextMouse.isKeyDown(keycode);
532538
}
533539

540+
/**
541+
* Clears the {@link #nextMouse}
542+
*/
543+
public void clearNext() {
544+
nextMouse.clear();
545+
}
534546
}
535547

536548
/**
@@ -710,5 +722,12 @@ public Triple<Float, Float, Float> getInterpolatedState(float partialTick, float
710722
}
711723
return Triple.of(interpolatedPitch, interpolatedYaw, 0f);
712724
}
725+
726+
/**
727+
* Clears the {@link #nextCameraAngle}
728+
*/
729+
public void clearNext() {
730+
nextCameraAngle.clear();
731+
}
713732
}
714733
}

src/main/resources/tasmod.mixin.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141

4242
// Events
4343
"events.MixinGuiIngame",
44-
"events.MixinEntityRenderer",
4544
"events.MixinNetHandlerPlayClient",
4645

4746
"clientcommands.MixinEntityPlayerSP",

0 commit comments

Comments
 (0)