|
15 | 15 |
|
16 | 16 | @Mixin(Timer.class) |
17 | 17 | /** |
18 | | - * Rewrites updateTimer to make it possible to interpolate ticks. |
| 18 | + * <p>Rewrites updateTimer, to add a tickratechanger and apply ticksync. |
| 19 | + * <p>Dynamically speeds up or slows down the tickrate depending on the time between {@link TickSyncClient TickSync} packages. |
19 | 20 | * @author Pancake |
20 | 21 | * |
21 | 22 | */ |
22 | 23 | public class MixinTimer { |
23 | 24 |
|
| 25 | + /** |
| 26 | + * <p>How many ticks elapsed since the {@link Timer#updateTimer()} method was called. |
| 27 | + * <p>Used in Minecraft#runGameLoop to call {@link Minecraft#runTick()} in the for loop |
| 28 | + */ |
24 | 29 | @Shadow |
25 | 30 | private int elapsedTicks; |
26 | | - @Shadow |
27 | | - private float renderPartialTicks; |
| 31 | + /** |
| 32 | + * How many "frames" elapsed since the {@link Timer#updateTimer()} method was called |
| 33 | + */ |
28 | 34 | @Shadow |
29 | 35 | private float elapsedPartialTicks; |
| 36 | + /** |
| 37 | + * Value between 0-1 of how many frames have elapsed inbetween ticks |
| 38 | + */ |
30 | 39 | @Shadow |
31 | | - private long lastSyncSysClock; |
| 40 | + private float renderPartialTicks; |
| 41 | + /** |
| 42 | + * The last time the {@link Timer#updateTimer()} method was called |
| 43 | + */ |
32 | 44 | @Shadow |
33 | | - private float tickLength; |
| 45 | + private long lastSyncSysClock; |
34 | 46 |
|
| 47 | + // ========================================================== |
| 48 | + |
| 49 | + /** |
| 50 | + * System time the last time a tick has run and ticksync has triggered |
| 51 | + */ |
35 | 52 | @Unique |
36 | | - private long millisLastTick; |
37 | | - @Unique |
38 | | - private long lastGameLoop; |
| 53 | + private long timeSinceLastTick; |
| 54 | + /** |
| 55 | + * The tick length in the last tick |
| 56 | + */ |
39 | 57 | @Unique |
40 | | - private float lastTickDuration; |
| 58 | + private float lastTickLength; |
41 | 59 |
|
| 60 | + /** |
| 61 | + * <p>Overwrites {@link Timer#updateTimer()} in a way,<br> |
| 62 | + * so that the tickrate matches the tickrate of the server. |
| 63 | + * |
| 64 | + * <p>It does this by removing {@link Timer#tickLength} from the equasion.<br> |
| 65 | + * Takes the time between incoming packets from {@link TickSyncClient#onClientPacket(com.minecrafttas.mctcommon.networking.interfaces.PacketID, java.nio.ByteBuffer, String) TickSyncClient.onClientPacket()} |
| 66 | + * and calculates the tickrate dynamically. |
| 67 | + * |
| 68 | + * <p>If no packet is present, it stops the client.<br> |
| 69 | + * This can happen when: |
| 70 | + * <ol> |
| 71 | + * <li>The packet did not reach it's destination</li> |
| 72 | + * <li>The tickrate on the server is 0</li> |
| 73 | + * <li>The server is lagging and unable to send packets</li> |
| 74 | + * <li>Another player is taking too long to send a packet</li> |
| 75 | + * </ol> |
| 76 | + * |
| 77 | + * @param ci |
| 78 | + */ |
42 | 79 | @Inject(method = "updateTimer", at = @At("HEAD"), cancellable = true) |
43 | 80 | public void inject_tick(CallbackInfo ci) { |
44 | | - if (TASmodClient.client != null && !TASmodClient.client.isClosed()) { |
45 | | - lastSyncSysClock = Minecraft.getSystemTime(); // update the tick tracker so that after returning to scheduling the client won't catch up all ticks (max 10) |
46 | | - this.elapsedTicks = 0; // do not do any ticks |
47 | | - long newGameLoop = Minecraft.getSystemTime(); |
| 81 | + /* |
| 82 | + * Run overriden updateTimer method |
| 83 | + * |
| 84 | + * Only runs when there is a connection to the custom networking server and the player is in a world |
| 85 | + */ |
| 86 | + if (TASmodClient.client != null && !TASmodClient.client.isClosed() && Minecraft.getMinecraft().world != null) { |
| 87 | + |
| 88 | + long currentTime = Minecraft.getSystemTime(); // The current system time as of calling this method |
| 89 | + /* |
| 90 | + * The length of the tick in milliseconds. |
| 91 | + * Set to 50 in vanilla, but in this instance, |
| 92 | + * it is set to the duration of when Ticksync.shouldTick was true |
| 93 | + */ |
| 94 | + float tickLength = lastTickLength; |
| 95 | + |
| 96 | + this.elapsedTicks = 0; // Prevent the client from ticking |
| 97 | + |
| 98 | + /* |
| 99 | + * Ticksync block. |
| 100 | + * Allows the client to run for 1 tick if TickSyncClient.shouldTick is true |
| 101 | + */ |
48 | 102 | if (TickSyncClient.shouldTick.compareAndSet(true, false)) { |
49 | | - this.elapsedTicks++; |
50 | | - this.lastTickDuration = newGameLoop - this.millisLastTick; |
| 103 | + |
| 104 | + this.elapsedTicks++; // Allow the client to tick once |
| 105 | + tickLength = currentTime - timeSinceLastTick; // Check the time between ticks |
51 | 106 | if (TASmodClient.tickratechanger.advanceTick) { |
52 | | - lastTickDuration = TASmodClient.tickratechanger.millisecondsPerTick; // Keep the lastTick duration steady during tickadvance, since it grows larger the longer you wait in tickrate 0 |
| 107 | + tickLength = TASmodClient.tickratechanger.millisecondsPerTick; // Keep the lastTick duration steady during tickadvance, since it grows larger the longer you wait in tickrate 0 |
53 | 108 | } |
54 | | - this.millisLastTick = newGameLoop; // Update millisLastTick |
55 | | - this.renderPartialTicks = 0; // Reset after the tick |
| 109 | + timeSinceLastTick = currentTime; |
| 110 | + this.renderPartialTicks = 0; // Reset render partial ticks after a new tick |
56 | 111 | } |
57 | | - // Interpolating |
58 | | - this.elapsedPartialTicks = (newGameLoop - this.lastGameLoop) / this.lastTickDuration; |
59 | | - float newPartialTicks = this.renderPartialTicks; |
60 | | - newPartialTicks += this.elapsedPartialTicks; |
61 | | - newPartialTicks -= (int) this.renderPartialTicks; |
62 | | - if (newPartialTicks > this.renderPartialTicks) { |
63 | | - this.renderPartialTicks = newPartialTicks; |
| 112 | + |
| 113 | + /* |
| 114 | + * Vanilla calculation from updateTimer, |
| 115 | + * with added interpolation calculation |
| 116 | + */ |
| 117 | + this.elapsedPartialTicks = (currentTime - this.lastSyncSysClock) / tickLength; // Use the calculated tickLength instead of the vanill tickLength |
| 118 | + float newRenderPartialTicks = this.renderPartialTicks; |
| 119 | + newRenderPartialTicks += this.elapsedPartialTicks; |
| 120 | + newRenderPartialTicks -= (int) this.renderPartialTicks; |
| 121 | + |
| 122 | + if (newRenderPartialTicks > this.renderPartialTicks) { // Fixes stuttering when the renderPartialTicks stay the same during tickrate 0 |
| 123 | + this.renderPartialTicks = newRenderPartialTicks; |
64 | 124 | } |
65 | | - this.lastGameLoop = newGameLoop; |
| 125 | + |
| 126 | + this.lastSyncSysClock = currentTime; // Update vanilla variable |
| 127 | + lastTickLength = tickLength; // Update last tick length |
66 | 128 | ci.cancel(); |
67 | | - } else { |
68 | | - this.millisLastTick = Minecraft.getSystemTime(); |
69 | | - this.lastGameLoop = Minecraft.getSystemTime(); |
70 | | - TickSyncClient.shouldTick.set(true); // The client should always tick if it once thrown out of the vanilla scheduling part, to make the server tick, etc. |
| 129 | + } |
| 130 | + // Run vanilla updateTimer |
| 131 | + else { |
| 132 | + this.timeSinceLastTick = Minecraft.getSystemTime(); |
| 133 | + TickSyncClient.shouldTick.set(true); // Client should always tick, when in the main menu |
71 | 134 | } |
72 | 135 | } |
73 | 136 | } |
0 commit comments