Replace RandomSequence
Some checks are pending
Build / build (push) Waiting to run

This commit is contained in:
DBotThePony 2025-03-10 20:16:30 +07:00
parent 0ae2319ddc
commit d28e2d857c
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 259 additions and 5 deletions

View File

@ -1,8 +1,10 @@
package ru.dbotthepony.mc.prng;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.fml.common.Mod;
@Mod(BetterRandom.MOD_ID)
public final class BetterRandom {
public static final String MOD_ID = "better_random";
public static final String SAVEDATA_LOCATION = "better_random_sequences";
}

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.mc.prng;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.RandomSequence;
import net.minecraft.world.level.levelgen.RandomSupport;
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public final class BetterRandomSequence extends RandomSequence {
private static final XoroshiroRandomSource DUMMY = new XoroshiroRandomSource(0, 0);
public final GJRAND64RandomSource actualRandom;
public BetterRandomSequence(GJRAND64RandomSource random) {
super(DUMMY);
this.actualRandom = random;
}
public BetterRandomSequence(long seed, @Nullable ResourceLocation location) {
this(seed, Optional.ofNullable(location));
}
public BetterRandomSequence(long seed, Optional<ResourceLocation> location) {
super(DUMMY);
RandomSupport.Seed128bit mixSeed = RandomSupport.upgradeSeedTo128bitUnmixed(seed);
if (location.isPresent())
mixSeed = mixSeed.xor(seedForKey(location.get()));
actualRandom = new GJRAND64RandomSource(mixSeed.mixed());
}
@Override
public @NotNull RandomSource random() {
return actualRandom;
}
}

View File

@ -0,0 +1,109 @@
package ru.dbotthepony.mc.prng;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.RandomSequence;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.level.saveddata.SavedData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Optional;
import java.util.function.BiConsumer;
public final class BetterRandomSequences extends RandomSequences {
private static final Logger LOGGER = LogManager.getLogger();
public BetterRandomSequences(long seed) {
super(seed);
}
public BetterRandomSequences(long seed, CompoundTag tag, HolderLookup.Provider provider) {
super(seed);
includeWorldSeed = tag.getBoolean("includeWorldSeed");
includeSequenceId = tag.getBoolean("includeSequenceId");
salt = tag.getInt("salt");
var compound = tag.getCompound("sequences");
for (var key : compound.getAllKeys()) {
try {
var location = ResourceLocation.parse(key);
var rng = GJRAND64RandomSource.CODEC.decode(NbtOps.INSTANCE, compound.get(key)).map(Pair::getFirst).getOrThrow();
betterSequences.put(location, new BetterRandomSequence(rng));
} catch (RuntimeException err) {
LOGGER.error("Unable to deserialize random sequence at {}", key, err);
}
}
}
private final HashMap<ResourceLocation, BetterRandomSequence> betterSequences = new HashMap<>();
@Override
public @NotNull CompoundTag save(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
// super.save(tag, registries);
tag.putBoolean("includeWorldSeed", includeWorldSeed);
tag.putBoolean("includeSequenceId", includeSequenceId);
tag.putInt("salt", salt);
var compound = new CompoundTag();
tag.put("sequences", compound);
for (var entry : betterSequences.entrySet()) {
compound.put(entry.getKey().toString(), GJRAND64RandomSource.CODEC.encodeStart(NbtOps.INSTANCE, entry.getValue().actualRandom).getOrThrow());
}
return tag;
}
@Override
public @NotNull RandomSource get(@NotNull ResourceLocation location) {
return new DirtyMarkingRandomSource(betterSequences.computeIfAbsent(location, this::createSequence).actualRandom);
}
private BetterRandomSequence createSequence(ResourceLocation location) {
return this.createSequence(location, this.salt, this.includeWorldSeed, this.includeSequenceId);
}
private BetterRandomSequence createSequence(ResourceLocation location, int salt, boolean includeWorldSeed, boolean includeSequenceId) {
long i = (includeWorldSeed ? this.worldSeed : 0L) ^ (long)salt;
return new BetterRandomSequence(i, includeSequenceId ? Optional.of(location) : Optional.empty());
}
@Override
public void forAllSequences(@NotNull BiConsumer<ResourceLocation, RandomSequence> action) {
betterSequences.forEach(action);
}
@Override
public int clear() {
int i = betterSequences.size();
betterSequences.clear();
return i;
}
@Override
public void reset(@NotNull ResourceLocation sequence) {
betterSequences.put(sequence, createSequence(sequence));
}
@Override
public void reset(@NotNull ResourceLocation sequence, int seed, boolean includeWorldSeed, boolean includeSequenceId) {
betterSequences.put(sequence, createSequence(sequence, seed, includeWorldSeed, includeSequenceId));
}
public static SavedData.Factory<BetterRandomSequences> betterFactory(final long seed) {
return new SavedData.Factory<>(
() -> new BetterRandomSequences(seed),
(tag, provider) -> new BetterRandomSequences(seed, tag, provider),
null
);
}
}

View File

@ -1,9 +1,10 @@
package ru.dbotthepony.mc.prng;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.HashCommon;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian;
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
import net.minecraft.world.level.levelgen.RandomSupport;
import org.jetbrains.annotations.NotNull;
@ -11,12 +12,39 @@ import org.jetbrains.annotations.NotNull;
import java.util.random.RandomGenerator;
public final class GJRAND64RandomSource implements RandomGenerator, RandomSource {
public static final Codec<GJRAND64RandomSource> CODEC = RecordCodecBuilder.create(it -> {
return it.group(
Codec.LONG.fieldOf("s0").forGetter(o -> o.s0),
Codec.LONG.fieldOf("s1").forGetter(o -> o.s1),
Codec.LONG.fieldOf("s2").forGetter(o -> o.s2),
Codec.LONG.fieldOf("s3").forGetter(o -> o.s3),
Codec.DOUBLE.fieldOf("nextNextGaussian").forGetter(o -> o.nextNextGaussian),
Codec.BOOL.fieldOf("haveNextNextGaussian").forGetter(o -> o.haveNextNextGaussian)
).apply(it, GJRAND64RandomSource::new);
});
private long s0;
private long s1;
private long s2;
private long s3;
private double nextNextGaussian;
private boolean haveNextNextGaussian;
private final MarsagliaPolarGaussian gaussian = new MarsagliaPolarGaussian(this);
private GJRAND64RandomSource(
long s0,
long s1,
long s2,
long s3,
double nextNextGaussian,
boolean haveNextNextGaussian
) {
this.s0 = s0;
this.s1 = s1;
this.s2 = s2;
this.s3 = s3;
this.nextNextGaussian = nextNextGaussian;
this.haveNextNextGaussian = haveNextNextGaussian;
}
public GJRAND64RandomSource() {
reinitialize(RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed());
@ -30,6 +58,10 @@ public final class GJRAND64RandomSource implements RandomGenerator, RandomSource
reinitialize(seed0, seed1);
}
public GJRAND64RandomSource(RandomSupport.Seed128bit seed) {
this(seed.seedHi(), seed.seedLo());
}
private void reinitialize(long seed) {
reinitialize(seed, 0L);
}
@ -54,7 +86,7 @@ public final class GJRAND64RandomSource implements RandomGenerator, RandomSource
@Override
public void setSeed(long l) {
reinitialize(l);
gaussian.reset();
haveNextNextGaussian = false;
}
@Override
@ -89,7 +121,24 @@ public final class GJRAND64RandomSource implements RandomGenerator, RandomSource
@Override
public double nextGaussian() {
return gaussian.nextGaussian();
if (this.haveNextNextGaussian) {
this.haveNextNextGaussian = false;
return this.nextNextGaussian;
} else {
double d0;
double d1;
double d2;
do {
d0 = 2.0 * nextDouble() - 1.0;
d1 = 2.0 * nextDouble() - 1.0;
d2 = Mth.square(d0) + Mth.square(d1);
} while (d2 >= 1.0 || d2 == 0.0);
double d3 = Math.sqrt(-2.0 * Math.log(d2) / d2);
this.nextNextGaussian = d1 * d3;
this.haveNextNextGaussian = true;
return d0 * d3;
}
}
@Override

View File

@ -0,0 +1,45 @@
package ru.dbotthepony.mc.prng.mixin;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.prng.BetterRandom;
import ru.dbotthepony.mc.prng.BetterRandomSequences;
import java.util.Objects;
@Mixin(ServerLevel.class)
public abstract class ServerLevelMixin {
@Nullable
private BetterRandomSequences betterRandomSequences;
private ServerLevel self$brng() {
return (ServerLevel) (Object) this;
}
@Overwrite(remap = false)
public RandomSource getRandomSequence(ResourceLocation location) {
return getRandomSequences().get(location);
}
@Overwrite(remap = false)
public RandomSequences getRandomSequences() {
if (this.betterRandomSequences == null) {
ServerLevel overworld = Objects.requireNonNull(
self$brng()
.getServer()
.getLevel(Level.OVERWORLD), "MinecraftServer lacks an Overworld Level, cannot continue. (exception thrown by BetterRandom)");
this.betterRandomSequences = overworld
.getDataStorage()
.computeIfAbsent(BetterRandomSequences.betterFactory(overworld.getSeed()), BetterRandom.SAVEDATA_LOCATION);
}
return this.betterRandomSequences;
}
}

View File

@ -0,0 +1,7 @@
protected net.minecraft.world.RandomSequences worldSeed
protected net.minecraft.world.RandomSequences salt
protected net.minecraft.world.RandomSequences includeWorldSeed
protected net.minecraft.world.RandomSequences includeSequenceId
public net.minecraft.world.RandomSequences$DirtyMarkingRandomSource
public net.minecraft.world.RandomSequences$DirtyMarkingRandomSource <init>(Lnet/minecraft/world/RandomSequences;Lnet/minecraft/util/RandomSource;)V

View File

@ -6,7 +6,8 @@
"minVersion": "0.8",
"mixins": [
"RandomSourceMixin",
"LevelMixin"
"LevelMixin",
"ServerLevelMixin"
],
"client": []
}