diff --git a/src/main/java/com/github/steveplays28/realisticsleep/config/RealisticSleepConfig.java b/src/main/java/com/github/steveplays28/realisticsleep/config/RealisticSleepConfig.java index e59df24..a53c6d6 100644 --- a/src/main/java/com/github/steveplays28/realisticsleep/config/RealisticSleepConfig.java +++ b/src/main/java/com/github/steveplays28/realisticsleep/config/RealisticSleepConfig.java @@ -43,6 +43,6 @@ public class RealisticSleepConfig implements ConfigData { public long tickDelay = -1; public enum SleepSpeedCurve { - @ConfigEntry.Gui.Tooltip LINEAR + LINEAR, EXPONENTIAL } } diff --git a/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerPlayerEntityMixin.java b/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerPlayerEntityMixin.java index 0f1c928..7f270b0 100644 --- a/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerPlayerEntityMixin.java @@ -1,6 +1,5 @@ package com.github.steveplays28.realisticsleep.mixin; -import com.github.steveplays28.realisticsleep.util.SleepMathUtil; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.server.network.ServerPlayerEntity; @@ -14,6 +13,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import static com.github.steveplays28.realisticsleep.RealisticSleep.config; +import static com.github.steveplays28.realisticsleep.util.SleepMathUtil.*; @Mixin(ServerPlayerEntity.class) public abstract class ServerPlayerEntityMixin extends Entity { @@ -24,22 +24,35 @@ public ServerPlayerEntityMixin(EntityType type, World world) { @Shadow public abstract void sendMessage(Text message, boolean overlay); - @Shadow public abstract ServerWorld getWorld(); + @Shadow + public abstract ServerWorld getWorld(); @Inject(method = "wakeUp(ZZ)V", at = @At(value = "HEAD")) public void wakeUpInject(boolean skipSleepTimer, boolean updateSleepingPlayers, CallbackInfo ci) { - if (!SleepMathUtil.isNightTime(getWorld().getTimeOfDay())) { - // Return if we shouldn't send the dawn message - if (!config.sendDawnMessage || config.dawnMessage.isEmpty()) return; + var timeOfDay = getWorld().getTimeOfDay() % DAY_LENGTH; + var ticksUntilDawn = Math.abs(timeOfDay - DAWN_WAKE_UP_TIME); + var ticksUntilDusk = Math.abs(timeOfDay - DUSK_WAKE_UP_TIME); - // Send dawn HUD message to player - sendMessage(Text.of(config.dawnMessage), true); - } else if (config.allowDaySleeping && SleepMathUtil.isNightTime(getWorld().getTimeOfDay())) { //Only shows this message if day sleeping is allowed - // Return if we shouldn't send the dawn or dusk message - if (!config.sendDuskMessage || config.duskMessage.isEmpty()) return; + if (ticksUntilDawn > WAKE_UP_GRACE_PERIOD_TICKS && ticksUntilDusk > WAKE_UP_GRACE_PERIOD_TICKS) { + return; + } - // Send dawn or dusk HUD message to player + if (ticksUntilDusk < ticksUntilDawn) { + // Return if the dusk message shouldn't be sent + if (!config.sendDuskMessage || config.duskMessage.isEmpty()) { + return; + } + + // Send dusk message to player in the actionbar sendMessage(Text.of(config.duskMessage), true); + } else { + // Return if the dawn message shouldn't be sent + if (!config.sendDawnMessage || config.dawnMessage.isEmpty()) { + return; + } + + // Send dawn message to player in the actionbar + sendMessage(Text.of(config.dawnMessage), true); } } } diff --git a/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerWorldMixin.java b/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerWorldMixin.java index ad29aea..ad8acc3 100644 --- a/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerWorldMixin.java +++ b/src/main/java/com/github/steveplays28/realisticsleep/mixin/ServerWorldMixin.java @@ -33,11 +33,12 @@ import static com.github.steveplays28.realisticsleep.RealisticSleep.MOD_ID; import static com.github.steveplays28.realisticsleep.RealisticSleep.config; import static com.github.steveplays28.realisticsleep.util.SleepMathUtil.DAY_LENGTH; +import static com.github.steveplays28.realisticsleep.util.SleepMathUtil.WAKE_UP_GRACE_PERIOD_TICKS; @Mixin(ServerWorld.class) public abstract class ServerWorldMixin extends World { @Unique - public double nightTimeStepPerTick = 1; + public double nightTimeStepPerTick = 2; @Unique public int nightTimeStepPerTickRounded = 1; @Unique @@ -72,7 +73,8 @@ public abstract class ServerWorldMixin extends World { @Shadow public abstract List getPlayers(); - @Shadow protected abstract void wakeSleepingPlayers(); + @Shadow + protected abstract void wakeSleepingPlayers(); protected ServerWorldMixin(MutableWorldProperties properties, RegistryKey registryRef, RegistryEntry registryEntry, Supplier profiler, boolean isClient, boolean debugWorld, long seed, int maxChainedNeighborUpdates) { super(properties, registryRef, registryEntry, profiler, isClient, debugWorld, seed, maxChainedNeighborUpdates); @@ -80,13 +82,15 @@ protected ServerWorldMixin(MutableWorldProperties properties, RegistryKey @Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameRules;getInt(Lnet/minecraft/world/GameRules$Key;)I")) public void tickInject(BooleanSupplier shouldKeepTicking, CallbackInfo ci) { + // Calculate seconds until awake int sleepingPlayerCount = sleepManager.getSleeping(); + int playerCount = getPlayers().size(); + double sleepingRatio = (double) sleepingPlayerCount / playerCount; + nightTimeStepPerTick = SleepMathUtil.calculateNightTimeStepPerTick( + sleepingRatio, config.sleepSpeedMultiplier, nightTimeStepPerTick); + int timeOfDay = (int) worldProperties.getTimeOfDay() % DAY_LENGTH; // TODO: Don't assume the TPS is 20 - int secondsUntilAwake = Math.abs( - SleepMathUtil.calculateSecondsUntilAwake((int) worldProperties.getTimeOfDay() % DAY_LENGTH, nightTimeStepPerTick, 20)); - - //Gets the remainder of the current time of day, as this number never actually resets each day(from my own testing) - int ticksUntilAwake = SleepMathUtil.calculateTicksUntilAwake((int) worldProperties.getTimeOfDay() % DAY_LENGTH); + int secondsUntilAwake = Math.abs(SleepMathUtil.calculateSecondsUntilAwake(timeOfDay, nightTimeStepPerTick, 20)); // Check if the night has (almost) ended and the weather should be skipped if (secondsUntilAwake <= 2 && shouldSkipWeather) { @@ -100,13 +104,10 @@ public void tickInject(BooleanSupplier shouldKeepTicking, CallbackInfo ci) { } // Fetch config values and do calculations - int playerCount = getPlayers().size(); - double sleepingRatio = (double) sleepingPlayerCount / playerCount; - double sleepingPercentage = sleepingRatio * 100; - - nightTimeStepPerTick = SleepMathUtil.calculateNightTimeStepPerTick(sleepingRatio, config.sleepSpeedMultiplier, nightTimeStepPerTick); nightTimeStepPerTickRounded = (int) Math.round(nightTimeStepPerTick); - var isNight = SleepMathUtil.isNightTime(worldProperties.getTimeOfDay()); + int ticksUntilAwake = Math.abs(SleepMathUtil.calculateTicksUntilAwake(timeOfDay)); + double sleepingPercentage = sleepingRatio * 100; + var isNight = SleepMathUtil.isNightTime(timeOfDay); var nightDayOrThunderstormText = Text.translatable( String.format("%s.text.%s", MOD_ID, worldProperties.isThundering() ? "thunderstorm" : isNight ? "night" : "day")); @@ -160,34 +161,33 @@ public void tickInject(BooleanSupplier shouldKeepTicking, CallbackInfo ci) { new WorldTimeUpdateS2CPacket(worldProperties.getTime(), worldProperties.getTimeOfDay(), doDayLightCycle), getRegistryKey()); // Check if players are still supposed to be sleeping, and send a HUD message if so - if (secondsUntilAwake > 0) { + if (ticksUntilAwake > WAKE_UP_GRACE_PERIOD_TICKS) { shouldSkipWeather = true; if (config.sendSleepingMessage) { sleepMessage = Text.translatable(String.format("%s.text.sleep_message", MOD_ID), sleepingPlayerCount, playerCount).append( nightDayOrThunderstormText); - if(isNight) { + if (isNight) { if (config.showTimeUntilDawn) { sleepMessage.append(Text.translatable(String.format("%s.text.time_until_dawn", MOD_ID), secondsUntilAwake)); } - } else { - if(config.showTimeUntilDusk) { - sleepMessage.append(Text.translatable(String.format("%s.text.time_until_dusk", MOD_ID), secondsUntilAwake)); - } + } else if (config.showTimeUntilDusk) { + sleepMessage.append(Text.translatable(String.format("%s.text.time_until_dusk", MOD_ID), secondsUntilAwake)); } + } - for (ServerPlayerEntity player : players) { - player.sendMessage(sleepMessage, true); - } + for (ServerPlayerEntity player : players) { + player.sendMessage(sleepMessage, true); } } - int tickGrace = 30; //The amount of extra ticks for waking up - maybe useful in cases where TPS is low? - //In my own testing, using just secondsUntilAwake <= 0 seemed to have a few seconds where - //trying to sleep back in the bed would kick the player right out - if (secondsUntilAwake <= 0 && ticksUntilAwake <= tickGrace) { + if (ticksUntilAwake <= WAKE_UP_GRACE_PERIOD_TICKS) { + // Wake up sleeping players this.wakeSleepingPlayers(); + + // Reset night time step per tick, to reset the exponential sleep speed curve calculation + nightTimeStepPerTick = 2; } } diff --git a/src/main/java/com/github/steveplays28/realisticsleep/util/SleepMathUtil.java b/src/main/java/com/github/steveplays28/realisticsleep/util/SleepMathUtil.java index 27756ed..3e6e00b 100644 --- a/src/main/java/com/github/steveplays28/realisticsleep/util/SleepMathUtil.java +++ b/src/main/java/com/github/steveplays28/realisticsleep/util/SleepMathUtil.java @@ -4,12 +4,14 @@ public class SleepMathUtil { public static final int DAY_LENGTH = 24000; - public static final int SUNRISE_WAKE_UP = 23449; - public static final int SUNSET_WAKE_UP = 12449; + public static final int DAWN_WAKE_UP_TIME = 23449; + public static final int DUSK_WAKE_UP_TIME = 12449; + public static final double WAKE_UP_GRACE_PERIOD_TICKS = Math.max(config.sleepSpeedMultiplier, 20); public static double calculateNightTimeStepPerTick(double sleepingRatio, double multiplier, double lastTimeStepPerTick) { return switch (config.sleepSpeedCurve) { case LINEAR -> sleepingRatio * multiplier; + case EXPONENTIAL -> Math.pow(lastTimeStepPerTick, 1 + sleepingRatio * multiplier); }; } @@ -18,7 +20,7 @@ public static int calculateTicksToTimeOfDay(int timeOfDay, int targetTimeOfDay) } public static int calculateTicksUntilAwake(int currentTimeOfDay) { - return calculateTicksToTimeOfDay(currentTimeOfDay, isNightTime(currentTimeOfDay) ? SUNRISE_WAKE_UP : SUNSET_WAKE_UP); + return calculateTicksToTimeOfDay(currentTimeOfDay, isNightTime(currentTimeOfDay) ? DAWN_WAKE_UP_TIME : DUSK_WAKE_UP_TIME); } public static int calculateSecondsUntilAwake(int currentTimeOfDay, double timeStepPerTick, double tps) { @@ -30,6 +32,6 @@ public static double getRandomNumberInRange(double min, double max) { } public static boolean isNightTime(long currentTimeOfDay) { - return currentTimeOfDay % DAY_LENGTH >= SUNSET_WAKE_UP; + return currentTimeOfDay > DUSK_WAKE_UP_TIME && currentTimeOfDay < DAWN_WAKE_UP_TIME; } }