Restore Zero Length frozen Age time
Build / build (push) Canceled after 0s

This commit is contained in:
dnorberg committed 2026-09-22 16:42:31 -04:00
1 parent 8bb5e16720
commit 2b18f776f2
8 files changed
+77 -12

No files matched your search

@@ -13,6 +13,7 @@ import net.dnorberg.rivencraft.config.RivencraftConfig;
import net.dnorberg.rivencraft.config.RivencraftClientConfig;
import net.dnorberg.rivencraft.book.AgeDimensionService;
import net.dnorberg.rivencraft.book.AgeWeatherService;
import net.dnorberg.rivencraft.book.AgeCelestialService;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.Mod;
@@ -38,6 +39,7 @@ public final class Rivencraft {
ModCreativeTabs.register(modEventBus);
NeoForge.EVENT_BUS.addListener(BookStandConversionHandler::onRightClickBlock);
NeoForge.EVENT_BUS.addListener(AgeWeatherService::onLevelTickPost);
NeoForge.EVENT_BUS.addListener(AgeCelestialService::onLevelTickPost);
LOGGER.info("Initializing Rivencraft");
}
}
@@ -0,0 +1,31 @@
package net.dnorberg.rivencraft.book;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.server.level.ServerLevel;
import net.neoforged.neoforge.event.tick.LevelTickEvent;
/** Holds Zero Length Age clocks without changing the server-wide daylight gamerule. */
public final class AgeCelestialService {
private static final Map<ServerLevel, Long> FROZEN_TIMES = new WeakHashMap<>();
private AgeCelestialService() {
}
public static void freezeAt(ServerLevel level, long dayTime) {
FROZEN_TIMES.put(level, dayTime);
}
public static void unfreeze(ServerLevel level) {
FROZEN_TIMES.remove(level);
}
public static void onLevelTickPost(LevelTickEvent.Post event) {
if (event.getLevel() instanceof ServerLevel level) {
Long frozenTime = FROZEN_TIMES.get(level);
if (frozenTime != null && level.getDayTime() != frozenTime) {
level.setDayTime(frozenTime);
}
}
}
}
@@ -21,7 +21,7 @@ public record AgeDefinition(
public static final ResourceLocation RANDOM_VANILLA_PROFILE = ResourceLocation.fromNamespaceAndPath(
Rivencraft.MOD_ID, "random_vanilla"
);
public static final int CURRENT_GENERATION_VERSION = 6;
public static final int CURRENT_GENERATION_VERSION = 7;
public AgeDefinition {
Objects.requireNonNull(id, "id");
@@ -103,12 +103,21 @@ public final class AgeDimensionService {
}
private static void applyCelestialClock(ServerLevel level, AgeDefinition age) {
level.setDayTimePerTick((float)(1.0D / age.traits().celestialPeriod()));
if (age.traits().hasStoppedCelestialCycle()) {
level.setDayTimePerTick(-1.0F);
AgeCelestialService.freezeAt(level, level.getDayTime());
} else {
AgeCelestialService.unfreeze(level);
level.setDayTimePerTick((float)(1.0D / age.traits().celestialPeriod()));
}
}
private static void applyInitialCelestialPhase(ServerLevel level, AgeDefinition age) {
long phaseTime = (long)(age.traits().celestialPhase() * Level.TICKS_PER_DAY);
level.setDayTime(phaseTime);
if (age.traits().hasStoppedCelestialCycle()) {
AgeCelestialService.freezeAt(level, phaseTime);
}
}
private static void synchronizeImmersivePortals(MinecraftServer server) {
@@ -13,6 +13,7 @@ public record AgeTraits(
) {
public static final double MIN_CELESTIAL_PERIOD = 0.2D;
public static final double MAX_CELESTIAL_PERIOD = 2.0D;
public static final double STOPPED_CELESTIAL_PERIOD = 0.0D;
public static final float MIN_CELESTIAL_ANGLE = 0.0F;
public static final float MAX_CELESTIAL_ANGLE = 360.0F;
public static final float MIN_CELESTIAL_PHASE = 0.0F;
@@ -27,7 +28,8 @@ public record AgeTraits(
if (biomeDistribution == null || lighting == null || weather == null) {
throw new IllegalArgumentException("Age trait choices cannot be null");
}
if (celestialPeriod < MIN_CELESTIAL_PERIOD || celestialPeriod > MAX_CELESTIAL_PERIOD) {
if (celestialPeriod != STOPPED_CELESTIAL_PERIOD
&& (celestialPeriod < MIN_CELESTIAL_PERIOD || celestialPeriod > MAX_CELESTIAL_PERIOD)) {
throw new IllegalArgumentException("Celestial period is outside the legacy range");
}
if (celestialAngle < MIN_CELESTIAL_ANGLE || celestialAngle >= MAX_CELESTIAL_ANGLE) {
@@ -40,17 +42,27 @@ public record AgeTraits(
public static AgeTraits select(long seed) {
Random random = new Random(seed ^ 0x524956454E435241L);
BiomeDistribution biomeDistribution = randomChoice(random, BiomeDistribution.values());
Lighting lighting = randomChoice(random, Lighting.values());
Weather weather = randomChoice(random, Weather.values());
// Legacy's PERIOD_BASIC grammar gives ModZero rank 2 and its other
// three length modifiers rank 1, producing an effective 1-in-7 weight.
boolean stopped = random.nextInt(7) == 0;
return new AgeTraits(
randomChoice(random, BiomeDistribution.values()),
randomChoice(random, Lighting.values()),
randomChoice(random, Weather.values()),
MIN_CELESTIAL_PERIOD
biomeDistribution,
lighting,
weather,
stopped ? STOPPED_CELESTIAL_PERIOD : MIN_CELESTIAL_PERIOD
+ random.nextDouble() * (MAX_CELESTIAL_PERIOD - MIN_CELESTIAL_PERIOD),
random.nextFloat() * MAX_CELESTIAL_ANGLE,
random.nextFloat()
);
}
public boolean hasStoppedCelestialCycle() {
return celestialPeriod == STOPPED_CELESTIAL_PERIOD;
}
public static <E extends Enum<E>> E parseOrDefault(String value, E fallback) {
try {
@SuppressWarnings("unchecked")
@@ -121,7 +121,8 @@ public final class LinkingGameTests {
"New Ages must record the current generation algorithm version");
helper.assertValueEqual(first.traits(), AgeTraits.select(first.seed()),
"A page-free book must persist the deterministic traits selected from its seed");
helper.assertTrue(first.traits().celestialPeriod() >= AgeTraits.MIN_CELESTIAL_PERIOD
helper.assertTrue(first.traits().hasStoppedCelestialCycle()
|| first.traits().celestialPeriod() >= AgeTraits.MIN_CELESTIAL_PERIOD
&& first.traits().celestialPeriod() <= AgeTraits.MAX_CELESTIAL_PERIOD,
"The celestial period must remain inside Mystcraft Legacy's random range");
helper.assertTrue(first.seed() != another.seed(), "Distinct Age UUIDs must receive distinct random seeds");
@@ -186,9 +187,14 @@ public final class LinkingGameTests {
? 0.25F : 0.0F;
helper.assertTrue(Math.abs(level.dimensionType().ambientLight() - expectedAmbientLight) < 0.0001F,
"The runtime dimension must apply the persisted lighting trait");
float expectedDaySpeed = (float)(1.0D / definition.traits().celestialPeriod());
helper.assertTrue(Math.abs(level.getDayTimePerTick() - expectedDaySpeed) < 0.0001F,
"The runtime dimension must apply the persisted celestial period");
if (definition.traits().hasStoppedCelestialCycle()) {
helper.assertTrue(level.getDayTimePerTick() < 0.0F,
"A Zero Length Age must leave vanilla clock advancement under the freeze controller");
} else {
float expectedDaySpeed = (float)(1.0D / definition.traits().celestialPeriod());
helper.assertTrue(Math.abs(level.getDayTimePerTick() - expectedDaySpeed) < 0.0001F,
"The runtime dimension must apply the persisted celestial period");
}
}
helper.succeed();
}