Add rule to make the World Border update with ticks instead of real time (#1550)
This commit is contained in:
1 parent
0e2f6df4d3
commit
009d971584
5 files changed
+242
No files matched your search
@@ -27,6 +27,8 @@ import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.StructureBlockEntity;
|
||||
import net.minecraft.world.level.block.piston.PistonStructureResolver;
|
||||
import net.minecraft.world.level.border.BorderStatus;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -1068,6 +1070,48 @@ public class CarpetSettings
|
||||
)
|
||||
public static int sculkSensorRange = 8;
|
||||
|
||||
/**
|
||||
* Listener, we need to update world borders to change whether
|
||||
* they are currently moving in real time or in game time.
|
||||
*/
|
||||
private static class WorldBorderValidator extends Validator<Boolean>
|
||||
{
|
||||
@Override
|
||||
public Boolean validate(CommandSourceStack source, CarpetRule<Boolean> changingRule, Boolean newValue, String userInput)
|
||||
{
|
||||
if (changingRule.value() ^ newValue)
|
||||
{
|
||||
// Needed for the update
|
||||
tickSyncedWorldBorders = newValue;
|
||||
MinecraftServer server = CarpetServer.minecraft_server;
|
||||
if (server == null)
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
for (ServerLevel level : server.getAllLevels())
|
||||
{
|
||||
WorldBorder worldBorder = level.getWorldBorder();
|
||||
if (worldBorder.getStatus() != BorderStatus.STATIONARY)
|
||||
{
|
||||
double from = worldBorder.getSize();
|
||||
double to = worldBorder.getLerpTarget();
|
||||
long time = worldBorder.getLerpRemainingTime();
|
||||
worldBorder.lerpSizeBetween(from, to, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Rule(
|
||||
desc = "Makes world borders move based on in game time instead of real time",
|
||||
extra = "This has the effect that when the tick rate changes the world border speed also changes proportional to it",
|
||||
category = FEATURE,
|
||||
validate = WorldBorderValidator.class
|
||||
)
|
||||
public static boolean tickSyncedWorldBorders = false;
|
||||
|
||||
public enum FungusFixMode {
|
||||
FALSE, RANDOM, ALL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.patches.TickSyncedBorderExtent;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
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;
|
||||
|
||||
@Mixin(WorldBorder.class)
|
||||
public class WorldBorder_tickMixin
|
||||
{
|
||||
@Shadow private WorldBorder.BorderExtent extent;
|
||||
|
||||
@Inject(method = "lerpSizeBetween", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/border/WorldBorder;getListeners()Ljava/util/List;"))
|
||||
private void getExtent(double d, double e, long l, CallbackInfo ci)
|
||||
{
|
||||
if (d != e && CarpetSettings.tickSyncedWorldBorders)
|
||||
{
|
||||
this.extent = new TickSyncedBorderExtent((WorldBorder) (Object) this, l, d, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package carpet.patches;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.Mth;
|
||||
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;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 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 getLerpRemainingTime()
|
||||
{
|
||||
// Rough estimation
|
||||
MinecraftServer server = CarpetServer.minecraft_server;
|
||||
double ms;
|
||||
if (server == null)
|
||||
{
|
||||
ms = TickSpeed.mspt;
|
||||
}
|
||||
else
|
||||
{
|
||||
ms = Arrays.stream(server.tickTimes).average().orElseThrow(IllegalStateException::new) * 1.0E-6D;
|
||||
}
|
||||
double tps = 1_000.0D / Math.max((TickSpeed.time_warp_start_time != 0) ? 0.0 : TickSpeed.mspt, 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
|
||||
if (!(listener instanceof BorderChangeListener.DelegateBorderChangeListener))
|
||||
{
|
||||
listener.onBorderSizeLerping(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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,11 @@ accessible class net/minecraft/server/level/ChunkMap$DistanceManager
|
||||
accessible class net/minecraft/server/level/ThreadedLevelLightEngine$TaskType
|
||||
accessible class net/minecraft/world/item/crafting/Ingredient$Value
|
||||
accessible class net/minecraft/world/level/lighting/BlockLightSectionStorage$BlockDataLayerStorageMap
|
||||
accessible class net/minecraft/world/level/border/WorldBorder$BorderExtent
|
||||
accessible class net/minecraft/world/level/border/WorldBorder$StaticBorderExtent
|
||||
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
|
||||
accessible class net/minecraft/world/level/biome/Biome$ClimateSettings
|
||||
|
||||
accessible method net/minecraft/world/level/border/WorldBorder getListeners ()Ljava/util/List;
|
||||
|
||||
accessible field net/minecraft/world/level/block/state/BlockBehaviour UPDATE_SHAPE_ORDER [Lnet/minecraft/core/Direction;
|
||||
@@ -38,6 +38,7 @@
|
||||
"ChunkMap_tickMixin",
|
||||
"DistanceManager_tickMixin",
|
||||
"ServerFunctionManager_tickMixin",
|
||||
"WorldBorder_tickMixin",
|
||||
"ChunkMap_scarpetChunkCreationMixin",
|
||||
"LevelEntityGetterAdapter_scarpetMixin",
|
||||
"ChunkHolder_scarpetChunkCreationMixin",
|
||||
|
||||
Reference in new issue
Block a user