Skip to content

Commit

Permalink
feat: Add exponential sleep speed curve, rework wake up timing and gr…
Browse files Browse the repository at this point in the history
…ace period

Also improved how night time is determined.
  • Loading branch information
Steveplays28 committed Oct 8, 2023
1 parent 4c16156 commit 99ce98f
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ public class RealisticSleepConfig implements ConfigData {
public long tickDelay = -1;

public enum SleepSpeedCurve {
@ConfigEntry.Gui.Tooltip LINEAR
LINEAR, EXPONENTIAL
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -72,21 +73,24 @@ public abstract class ServerWorldMixin extends World {
@Shadow
public abstract List<ServerPlayerEntity> getPlayers();

@Shadow protected abstract void wakeSleepingPlayers();
@Shadow
protected abstract void wakeSleepingPlayers();

protected ServerWorldMixin(MutableWorldProperties properties, RegistryKey<World> registryRef, RegistryEntry<DimensionType> registryEntry, Supplier<Profiler> profiler, boolean isClient, boolean debugWorld, long seed, int maxChainedNeighborUpdates) {
super(properties, registryRef, registryEntry, profiler, isClient, debugWorld, seed, maxChainedNeighborUpdates);
}

@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) {
Expand All @@ -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"));

Expand Down Expand Up @@ -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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
}

Expand All @@ -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) {
Expand All @@ -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;
}
}

0 comments on commit 99ce98f

Please sign in to comment.