package net.minecraft.world; import com.mojang.datafixers.util.Pair; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Map; import java.util.Optional; import java.util.function.BiConsumer; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.util.datafix.DataFixTypes; import net.minecraft.world.level.levelgen.PositionalRandomFactory; import net.minecraft.world.level.saveddata.SavedData; import org.slf4j.Logger; public class RandomSequences extends SavedData { private static final Logger LOGGER = LogUtils.getLogger(); private final long worldSeed; private int salt; private boolean includeWorldSeed = true; private boolean includeSequenceId = true; private final Map sequences = new Object2ObjectOpenHashMap<>(); public static SavedData.Factory factory(long seed) { return new SavedData.Factory<>(() -> new RandomSequences(seed), (compoundTag, provider) -> load(seed, compoundTag), DataFixTypes.SAVED_DATA_RANDOM_SEQUENCES); } public RandomSequences(long seed) { this.worldSeed = seed; } public RandomSource get(ResourceLocation location) { RandomSource randomSource = ((RandomSequence)this.sequences.computeIfAbsent(location, this::createSequence)).random(); return new RandomSequences.DirtyMarkingRandomSource(randomSource); } private RandomSequence createSequence(ResourceLocation location) { return this.createSequence(location, this.salt, this.includeWorldSeed, this.includeSequenceId); } private RandomSequence createSequence(ResourceLocation location, int salt, boolean includeWorldSeed, boolean includeSequenceId) { long l = (includeWorldSeed ? this.worldSeed : 0L) ^ salt; return new RandomSequence(l, includeSequenceId ? Optional.of(location) : Optional.empty()); } public void forAllSequences(BiConsumer action) { this.sequences.forEach(action); } public void setSeedDefaults(int salt, boolean includeWorldSeed, boolean includeSequenceId) { this.salt = salt; this.includeWorldSeed = includeWorldSeed; this.includeSequenceId = includeSequenceId; } @Override public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) { tag.putInt("salt", this.salt); tag.putBoolean("include_world_seed", this.includeWorldSeed); tag.putBoolean("include_sequence_id", this.includeSequenceId); CompoundTag compoundTag = new CompoundTag(); this.sequences .forEach( (resourceLocation, randomSequence) -> compoundTag.put( resourceLocation.toString(), (Tag)RandomSequence.CODEC.encodeStart(NbtOps.INSTANCE, randomSequence).result().orElseThrow() ) ); tag.put("sequences", compoundTag); return tag; } private static boolean getBooleanWithDefault(CompoundTag tag, String key, boolean defaultValue) { return tag.contains(key, 1) ? tag.getBoolean(key) : defaultValue; } public static RandomSequences load(long seed, CompoundTag tag) { RandomSequences randomSequences = new RandomSequences(seed); randomSequences.setSeedDefaults( tag.getInt("salt"), getBooleanWithDefault(tag, "include_world_seed", true), getBooleanWithDefault(tag, "include_sequence_id", true) ); CompoundTag compoundTag = tag.getCompound("sequences"); for (String string : compoundTag.getAllKeys()) { try { RandomSequence randomSequence = (RandomSequence)((Pair)RandomSequence.CODEC.decode(NbtOps.INSTANCE, compoundTag.get(string)).result().get()).getFirst(); randomSequences.sequences.put(ResourceLocation.parse(string), randomSequence); } catch (Exception var9) { LOGGER.error("Failed to load random sequence {}", string, var9); } } return randomSequences; } public int clear() { int i = this.sequences.size(); this.sequences.clear(); return i; } public void reset(ResourceLocation sequence) { this.sequences.put(sequence, this.createSequence(sequence)); } public void reset(ResourceLocation sequence, int seed, boolean includeWorldSeed, boolean includeSequenceId) { this.sequences.put(sequence, this.createSequence(sequence, seed, includeWorldSeed, includeSequenceId)); } class DirtyMarkingRandomSource implements RandomSource { private final RandomSource random; DirtyMarkingRandomSource(final RandomSource random) { this.random = random; } @Override public RandomSource fork() { RandomSequences.this.setDirty(); return this.random.fork(); } @Override public PositionalRandomFactory forkPositional() { RandomSequences.this.setDirty(); return this.random.forkPositional(); } @Override public void setSeed(long seed) { RandomSequences.this.setDirty(); this.random.setSeed(seed); } @Override public int nextInt() { RandomSequences.this.setDirty(); return this.random.nextInt(); } @Override public int nextInt(int bound) { RandomSequences.this.setDirty(); return this.random.nextInt(bound); } @Override public long nextLong() { RandomSequences.this.setDirty(); return this.random.nextLong(); } @Override public boolean nextBoolean() { RandomSequences.this.setDirty(); return this.random.nextBoolean(); } @Override public float nextFloat() { RandomSequences.this.setDirty(); return this.random.nextFloat(); } @Override public double nextDouble() { RandomSequences.this.setDirty(); return this.random.nextDouble(); } @Override public double nextGaussian() { RandomSequences.this.setDirty(); return this.random.nextGaussian(); } public boolean equals(Object object) { if (this == object) { return true; } else { return object instanceof RandomSequences.DirtyMarkingRandomSource dirtyMarkingRandomSource ? this.random.equals(dirtyMarkingRandomSource.random) : false; } } } }