Files
fabric-carpet/src/main/java/carpet/patches/TickSyncedBorderExtent.java
T
2025-09-04 02:08:10 +02:00

171 lines
4.8 KiB
Java

package carpet.patches;
import carpet.CarpetServer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTickRateManager;
import net.minecraft.util.Mth;
import net.minecraft.util.TimeUtil;
import net.minecraft.world.level.border.BorderChangeListener;
import net.minecraft.world.level.border.BorderStatus;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.NotNull;
/**
* This class is essentially a copy of {@link net.minecraft.world.level.border.WorldBorder.MovingBorderExtent}
* but instead of using real time to lerp the border
* this class uses the in game ticks.
*/
@SuppressWarnings("JavadocReference")
public class TickSyncedBorderExtent implements WorldBorder.BorderExtent
{
private final WorldBorder border;
private final long realDuration;
private final double tickDuration;
private final double from;
private final double to;
private int ticks;
public TickSyncedBorderExtent(WorldBorder border, long realDuration, double from, double to)
{
this.border = border;
this.realDuration = realDuration;
this.tickDuration = realDuration / 50.0;
this.from = from;
this.to = to;
this.ticks = 0;
}
@Override
public double getMinX()
{
int maxSize = this.border.getAbsoluteMaxSize();
return Mth.clamp(this.border.getCenterX() - this.getSize() / 2.0, -maxSize, maxSize);
}
@Override
public double getMaxX()
{
int maxSize = this.border.getAbsoluteMaxSize();
return Mth.clamp(this.border.getCenterX() + this.getSize() / 2.0, -maxSize, maxSize);
}
@Override
public double getMinZ()
{
int maxSize = this.border.getAbsoluteMaxSize();
return Mth.clamp(this.border.getCenterZ() - this.getSize() / 2.0, -maxSize, maxSize);
}
@Override
public double getMaxZ()
{
int maxSize = this.border.getAbsoluteMaxSize();
return Mth.clamp(this.border.getCenterZ() + this.getSize() / 2.0, -maxSize, maxSize);
}
@Override
public double getSize()
{
double progress = this.ticks / this.tickDuration;
return progress < 1.0 ? Mth.lerp(progress, this.from, this.to) : this.to;
}
@Override
public double getLerpSpeed()
{
return Math.abs(this.from - this.to) / this.realDuration;
}
@Override
public long getLerpTime()
{
// Rough estimation
MinecraftServer server = CarpetServer.minecraft_server;
double ms;
if (server == null)
{
// can this even happen?
ms = 50.0;
}
else
{
ms = ((double)server.getAverageTickTimeNanos())/ TimeUtil.NANOSECONDS_PER_MILLISECOND;
ServerTickRateManager trm = server.tickRateManager();
if (!trm.isSprinting())
{
ms = Math.max(ms, trm.millisecondsPerTick());
}
}
double tps = 1_000.0D / ms;
return (long) ((this.tickDuration - this.ticks) / tps * 1_000);
}
@Override
public double getLerpTarget()
{
return this.to;
}
@NotNull
@Override
public BorderStatus getStatus()
{
return this.to < this.from ? BorderStatus.SHRINKING : BorderStatus.GROWING;
}
@Override
public void onAbsoluteMaxSizeChange()
{
}
@Override
public void onCenterChange()
{
}
@NotNull
@Override
public WorldBorder.BorderExtent update()
{
if (this.ticks++ % 20 == 0)
{
// We need to update any listeners
// Most importantly those that send updates to the client
// This is because the client logic uses real time
// So if the tick speed has changed we need to tell the client
for (BorderChangeListener listener : this.border.getListeners())
{
// We do not want to update DelegateBorderChangeListener
// This updates borders in other dimensions
listener.onLerpSize(this.border, this.from, this.to, this.realDuration);
}
}
return this.ticks >= this.tickDuration ? this.border.new StaticBorderExtent(this.to) : this;
}
@NotNull
@Override
public VoxelShape getCollisionShape()
{
return Shapes.join(
Shapes.INFINITY,
Shapes.box(
Math.floor(this.getMinX()),
Double.NEGATIVE_INFINITY,
Math.floor(this.getMinZ()),
Math.ceil(this.getMaxX()),
Double.POSITIVE_INFINITY,
Math.ceil(this.getMaxZ())
),
BooleanOp.ONLY_FIRST
);
}
}