Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/com/minecrafttas/tasmod/TASmodClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ private void registerEventListeners() {
EventListenerRegistry.register(savestateHandlerClient);

EventListenerRegistry.register(virtual.interpolationHandler);
EventListenerRegistry.register(tickratechanger);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.minecrafttas.tasmod.mixin.tickrate;

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.callback.CallbackInfo;

import com.minecrafttas.tasmod.TASmodClient;

import paulscode.sound.Source;

@Mixin(Source.class)
public abstract class MixinAudioPitch {

@Shadow(remap = false)
public float pitch;

@Inject(method = "setPitch", at = @At(value = "RETURN"), remap = false)
public void redosetPitch(float value, CallbackInfo ci) {
pitch = value * (TASmodClient.tickratechanger.ticksPerSecond / 20F);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.minecrafttas.tasmod.mixin.tickrate;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

import com.minecrafttas.tasmod.TASmodClient;

import net.minecraft.client.renderer.RenderItem;
//#else
//$$ import net.minecraft.client.renderer.entity.RenderItem;
//#endif

@Mixin(RenderItem.class)
public abstract class MixinEnchantmentGlimm {

@ModifyVariable(method = "renderEffect", at = @At("STORE"), index = 2, ordinal = 0)
public float modifyrenderEffect1(float f) {
return (TASmodClient.tickratechanger.getMilliseconds() % 3000L) / 3000.0F / 8F;
}

@ModifyVariable(method = "renderEffect", at = @At("STORE"), index = 3, ordinal = 1)
public float modifyrenderEffect2(float f) {
return (TASmodClient.tickratechanger.getMilliseconds() % 4873L) / 4873.0F / 8F;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.minecrafttas.tasmod.mixin.tickrate;

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.ModifyVariable;

import com.minecrafttas.tasmod.TASmodClient;

import net.minecraft.client.Minecraft;

/**
* This mixin tries to make the animation of the advancement toasts dependent on the tickrate while keeping the code as vanilla as possible<br>
* <br>
* While I spent a long amount of time watching this code, I still don't quite fully understand the math behind this...<br>
* <br>
* Here's what I could find out: Toasts have 2 different states represented in visibility in the ToastInstance.<br>
* And if it's set to SHOW the fly in animation and sound will play, the same goes with HIDE where it flies out after a certain amount of time<br>
* After a lot of trial and error I found out that animationTimer, which was originally "i", is the way to go...<br>
* <br>
* So just as RenderItem and GuiSubtitleOverlay, things are done with an offset for tickrate 0 and simple multiplication<br>
* Also I used a copy of the vanilla ToastInstance-class to make it work for every subtitle on screen... If you seek to make it work for only 1, at the end is a commented code that shows you how to use @ModyfyVarable<br>
* <br>
* There is one compromise I had to make... When you change the tickrate while a toast is showing, it will stay at the old tickrate until it's done...<br>
* Maybe I still fix this and get into this mess once more, but for now this will do and don't make the subtitles stuck in a loop until you change to the old tickrate<br>
* Am I doing this right with commenting code? I hope so...<br>
* <br>
* Update 02.03.21:<br>
* Well, it's been roughly a year since I have touched this code and I am finally back with updating this. And while I am at it, I removed that compromise mentioned earlier.<br>
*
* Update 26.03.26 Unease <br>
* Reusing 5 year old code for legacy... how fitting
*
* @author Scribble
* @author Unease
*/
@Mixin(targets = "net/minecraft/client/gui/toasts/GuiToast$ToastInstance")
public abstract class MixinGuiToast {

/**
* Vanilla, current time in ms when the animationBegan. The delta between animationTimer and animation time shows the progress
*/
@Shadow
private long animationTime;
/**
* Vanilla, the time the animation is visible. Used in the toast instances (e.g. AdvancementToast) to time their animation. Also used to set the visibility to HIDE and make the toast go away
*/
@Shadow
private long visibleTime;

/**
* When entering tickrate 0, store is the (ms) time when tickrate 0 was activated. This time replaces the "animationTimer" during tickrate 0
*/
private long store = 0L;
/**
* Makes sure the code runs only once when switching between tickrate 0 and not tickrate 0... I have yet to find a more elegant solution...
*/
private boolean once = false;
/**
* The offset of the ms time when using tickrate 0. This is used to "resume" the animation without any jumps, after exiting tickrate 0
*/
private long offset = 0L;
/**
* When changing the tickrate while a toast is on screen, the {@link #animationTime} is not correct. The correct animationTime can be optained by subtracting the current time from this delta<br>
*/
private long animationDelta = 0L;
/**
* When changing the tickrate while a toast is on screen, the {@link #visibleTime} is not correct. The correct visibleTime can be optained by subtracting the current time from this delta<br>
*/
private long visibleDelta = 0L;
/**
* Used to detect a change in the tickrate and to run the code on.
*/
private float ticksave = TASmodClient.tickratechanger.ticksPerSecond;

@ModifyVariable(method = "render(II)Z", at = @At(value = "STORE", ordinal = 0))
public long modifyAnimationTime(long animationTimer) {
//===========TICKRATE OTHER THAN 0===========
if (TASmodClient.tickratechanger.ticksPerSecond != 0) {
if (once) {
once = false;
offset = Minecraft.getSystemTime() - store;
}

animationTimer = (long) ((Minecraft.getSystemTime() - offset) * (TASmodClient.tickratechanger.ticksPerSecond / 20));

if (ticksave != TASmodClient.tickratechanger.ticksPerSecond) {
ticksave = TASmodClient.tickratechanger.ticksPerSecond;
animationTime = animationTimer - animationDelta;
visibleTime = animationTimer - visibleDelta;
}
animationDelta = animationTimer - animationTime;
visibleDelta = animationTimer - visibleTime;
//===========TICKRATE 0===========
} else {
if (!once) {
once = true;
store = (long) ((Minecraft.getSystemTime() - offset));
}
animationTimer = (long) (store * (TASmodClient.tickratechanger.tickrateSaved / 20));
}
return animationTimer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.minecrafttas.tasmod.mixin.tickrate;

import java.util.Map;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;

import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.SoundManager;

@Mixin(SoundManager.class)
public class MixinSoundManager implements com.minecrafttas.tasmod.util.Ducks.SoundManagerDuck {

@Shadow
private boolean loaded;
@Shadow
private Map<String, ISound> playingSounds;
@Shadow
private SoundManager.SoundSystemStarterThread sndSystem;

@Override
public void updatePitch() {
if (this.loaded) {
playingSounds.forEach((sourceName, sound) -> {
sndSystem.setPitch(sourceName, sound.getPitch());
});
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.minecrafttas.tasmod.mixin.tickrate;

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.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import com.minecrafttas.tasmod.TASmodClient;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiSubtitleOverlay;

@Mixin(GuiSubtitleOverlay.Subtitle.class)
public class MixinSubtitle {

@Shadow
private long startTime;

private long offset = 0;
private long store = 0;

private boolean once = false;

@Inject(method = "getStartTime", at = @At(value = "HEAD"), cancellable = true)
public void redoGetStartTime(CallbackInfoReturnable<Long> ci) {
if (TASmodClient.tickratechanger.ticksPerSecond == 0) {
if (!once) {
once = true;
store = Minecraft.getSystemTime() - offset;
}
offset = Minecraft.getSystemTime() - store;
} else {
if (once) {
once = false;
}
}
ci.setReturnValue(this.startTime + offset);
ci.cancel();
}

@Inject(method = "refresh", at = @At(value = "HEAD"))
public void resetOnRefresh(CallbackInfo ci) {
offset = 0;
store = 0;
once = false;
}
}
//#else
//$$ @Mixin(Minecraft.class)
//$$ public class MixinSubtitle {
//$$
//$$ }
//#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.minecrafttas.tasmod.mixin.tickrate;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

import com.minecrafttas.tasmod.TASmodClient;

import net.minecraft.client.gui.GuiSubtitleOverlay;

@Mixin(GuiSubtitleOverlay.class)
public abstract class MixinSubtitleOverlay {

@ModifyConstant(method = "renderSubtitles", constant = @Constant(longValue = 3000L))
public long applyTickrate(long threethousand) {
float multiplier = TASmodClient.tickratechanger.ticksPerSecond == 0 ? 20F / TASmodClient.tickratechanger.tickrateSaved : 20F / TASmodClient.tickratechanger.ticksPerSecond;
return (long) (threethousand * multiplier);
}

@ModifyConstant(method = "renderSubtitles", constant = @Constant(floatValue = 3000F))
public float applyTickrate2(float threethousand) {
float multiplier = TASmodClient.tickratechanger.ticksPerSecond == 0 ? 20F / TASmodClient.tickratechanger.tickrateSaved : 20F / TASmodClient.tickratechanger.ticksPerSecond;
return threethousand * multiplier;
}

}
//#else
//$$ import net.minecraft.client.Minecraft;
//$$ @Mixin(Minecraft.class)
//$$ public abstract class MixinSubtitleOverlay {
//$$
//$$ }
//#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.minecrafttas.tasmod.mixin.tickrate;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;

import com.minecrafttas.tasmod.TASmodClient;

import net.minecraft.client.renderer.RenderGlobal;

@Mixin(RenderGlobal.class)
public class MixinWorldborder {

@ModifyVariable(method = "renderWorldBorder", at = @At(value = "STORE"), index = 20, ordinal = 4)
public float injectf3(float f) {
return (TASmodClient.tickratechanger.getMilliseconds() % 3000L) / 3000.0F;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

import java.nio.ByteBuffer;

import com.minecrafttas.mctcommon.events.EventClient.EventClientGameLoop;
import com.minecrafttas.mctcommon.events.EventListenerRegistry;
import com.minecrafttas.mctcommon.networking.Client.Side;
import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException;
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.TASmodClient;
import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost;
import com.minecrafttas.tasmod.events.EventTickratechanger;
import com.minecrafttas.tasmod.networking.TASmodBufferBuilder;
import com.minecrafttas.tasmod.registries.TASmodPackets;
Expand All @@ -25,7 +27,7 @@
* @author Scribble
*
*/
public class TickrateChangerClient implements ClientPacketHandler {
public class TickrateChangerClient implements ClientPacketHandler, EventClientGameLoop {
/**
* The current tickrate of the client
*/
Expand All @@ -47,6 +49,26 @@ public class TickrateChangerClient implements ClientPacketHandler {
*/
public long millisecondsPerTick = 50L;

/**
* Timestamp when the last tickrate change was initiated
*/
public long timestampSinceLastTickRateChange = System.currentTimeMillis();

/**
* Time since the last tickrate change was initiated without being affected by tickrate
*/
public long fakeTimeSinceTickRateChange = System.currentTimeMillis();

/**
* Timestamp since last game loop and only updated when the tickrate is 0
*/
private long timestampSinceLastGameLoop;

/**
* Counts the milliseconds when the tickrate is 0
*/
private float timeOffset = 0L;

/**
* The tickrate steps that can be set via {@link #increaseTickrate()} and {@link #decreaseTickrate()}
*/
Expand Down Expand Up @@ -142,6 +164,15 @@ public void changeServerTickrate(float tickrate) {
}
}

/**
* @return Milliseconds affected by tickrate
*/
public long getMilliseconds() {
long time = (long) (System.currentTimeMillis() - timestampSinceLastTickRateChange - timeOffset);
time *= (ticksPerSecond / 20F);
return (long) (fakeTimeSinceTickRateChange + time);
}

/**
* <p>Toggles between tickrate 0 and tickrate > 0
*/
Expand Down Expand Up @@ -342,4 +373,12 @@ private static int clamp(long value, int min, int max) {
}
return (int) Math.min(max, Math.max(value, min));
}

@Override
public void onRunClientGameLoop(Minecraft mc) {
if (ticksPerSecond == 0) {
timeOffset += System.currentTimeMillis() - timestampSinceLastGameLoop;
timestampSinceLastGameLoop = System.currentTimeMillis();
}
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/minecrafttas/tasmod/util/Ducks.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,9 @@ public static interface PlayerChunkMapDuck {
*/
public void forceTick();
}

public static interface SoundManagerDuck {

public void updatePitch();
}
}
Loading
Loading