Compare commits
3 Commits
72277eb71c
...
6e88427eef
Author | SHA1 | Date | |
---|---|---|---|
6e88427eef | |||
84e5ad9f2b | |||
8f509dcffb |
@ -133,6 +133,8 @@ dependencies {
|
|||||||
// For more info:
|
// For more info:
|
||||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||||
|
|
||||||
|
// compileOnly(project(":c2me"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// This block of code expands all declared replace properties in the specified resource targets.
|
// This block of code expands all declared replace properties in the specified resource targets.
|
||||||
|
@ -34,7 +34,7 @@ mod_name=Better Random
|
|||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=BSD 2 Clause
|
mod_license=BSD 2 Clause
|
||||||
# The mod version. See https://semver.org/
|
# The mod version. See https://semver.org/
|
||||||
mod_version=1.2.1
|
mod_version=1.3.0
|
||||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
# This should match the base package used for the mod sources.
|
# This should match the base package used for the mod sources.
|
||||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||||
|
@ -9,3 +9,5 @@ pluginManagement {
|
|||||||
plugins {
|
plugins {
|
||||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// include("c2me")
|
||||||
|
@ -1,9 +1,27 @@
|
|||||||
package ru.dbotthepony.mc.prng;
|
package ru.dbotthepony.mc.prng;
|
||||||
|
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.neoforged.fml.ModList;
|
||||||
import net.neoforged.fml.common.Mod;
|
import net.neoforged.fml.common.Mod;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@Mod(BetterRandom.MOD_ID)
|
@Mod(BetterRandom.MOD_ID)
|
||||||
public final class BetterRandom {
|
public final class BetterRandom {
|
||||||
public static final String MOD_ID = "better_random";
|
public static final String MOD_ID = "better_random";
|
||||||
public static final String SAVEDATA_LOCATION = "better_random_sequences";
|
public static final String SAVEDATA_LOCATION = "better_random_sequences";
|
||||||
|
|
||||||
|
private static RandomSource createWorldRandomC2ME(Supplier<Thread> thread) {
|
||||||
|
// TODO: actually implement proper integration
|
||||||
|
// also, this shouldn't be synchronized, per say, in this way, but instead throw an exception when
|
||||||
|
// concurrently accessed (to match C2ME and vanilla behavior)
|
||||||
|
return new SynchronizedGJRAND64RandomSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RandomSource createWorldRandom(Supplier<Thread> thread) {
|
||||||
|
if (ModList.get().isLoaded("c2me"))
|
||||||
|
return createWorldRandomC2ME(thread);
|
||||||
|
|
||||||
|
return new GJRAND64RandomSource();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.random.RandomGenerator;
|
import java.util.random.RandomGenerator;
|
||||||
|
|
||||||
public final class GJRAND64RandomSource implements RandomGenerator, RandomSource {
|
public class GJRAND64RandomSource implements RandomGenerator, RandomSource {
|
||||||
public static final Codec<GJRAND64RandomSource> CODEC = RecordCodecBuilder.create(it -> {
|
public static final Codec<GJRAND64RandomSource> CODEC = RecordCodecBuilder.create(it -> {
|
||||||
return it.group(
|
return it.group(
|
||||||
Codec.LONG.fieldOf("s0").forGetter(o -> o.s0),
|
Codec.LONG.fieldOf("s0").forGetter(o -> o.s0),
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
package ru.dbotthepony.mc.prng;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.HashCommon;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
||||||
|
import net.minecraft.world.level.levelgen.RandomSupport;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
public class SynchronizedGJRAND64RandomSource extends GJRAND64RandomSource {
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
public SynchronizedGJRAND64RandomSource() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SynchronizedGJRAND64RandomSource(long seed) {
|
||||||
|
super(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SynchronizedGJRAND64RandomSource(long seed0, long seed1) {
|
||||||
|
super(seed0, seed1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SynchronizedGJRAND64RandomSource(RandomSupport.Seed128bit seed) {
|
||||||
|
super(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long nextLong() {
|
||||||
|
lock.lock();
|
||||||
|
long result = super.nextLong();
|
||||||
|
lock.unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSeed(long l) {
|
||||||
|
lock.lock();
|
||||||
|
super.setSeed(l);
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextGaussian() {
|
||||||
|
lock.lock();
|
||||||
|
double value = super.nextGaussian();
|
||||||
|
lock.unlock();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RandomSource fork() {
|
||||||
|
return new SynchronizedGJRAND64RandomSource(nextLong(), nextLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull PositionalRandomFactory forkPositional() {
|
||||||
|
return new Positional(nextLong(), nextLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Positional implements PositionalRandomFactory {
|
||||||
|
private final long seed0;
|
||||||
|
private final long seed1;
|
||||||
|
|
||||||
|
public Positional(long seed0, long seed1) {
|
||||||
|
this.seed0 = seed0;
|
||||||
|
this.seed1 = seed1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RandomSource fromHashOf(@NotNull String s) {
|
||||||
|
long seed = HashCommon.murmurHash3((long) s.hashCode() & 0xFFFFFFFFL);
|
||||||
|
return new SynchronizedGJRAND64RandomSource(seed0 ^ Long.rotateLeft(seed, 32), seed1 ^ seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RandomSource fromSeed(long l) {
|
||||||
|
return new SynchronizedGJRAND64RandomSource(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RandomSource at(int x, int y, int z) {
|
||||||
|
long seed = Mth.getSeed(x, y, z);
|
||||||
|
return new SynchronizedGJRAND64RandomSource(seed0 ^ Long.rotateLeft(seed, 32), seed1 ^ seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parityConfigString(@NotNull StringBuilder stringBuilder) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,34 @@
|
|||||||
package ru.dbotthepony.mc.prng.mixin;
|
package ru.dbotthepony.mc.prng.mixin;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import ru.dbotthepony.mc.prng.BetterRandom;
|
||||||
|
|
||||||
@Mixin(Level.class)
|
@Mixin(Level.class)
|
||||||
public abstract class LevelMixin {
|
public abstract class LevelMixin {
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
@Final // ensure it is on top-level
|
||||||
|
@Redirect(
|
||||||
|
method = "<init>",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/util/RandomSource;create()Lnet/minecraft/util/RandomSource;"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public RandomSource ensureBetterRandom() {
|
||||||
|
return BetterRandom.createWorldRandom(() -> thread);
|
||||||
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public BlockPos getBlockRandomPos(int x, int y, int z, int yMask) {
|
public BlockPos getBlockRandomPos(int x, int y, int z, int yMask) {
|
||||||
long value = ((Level) (Object) this).random.nextLong();
|
long value = ((Level) (Object) this).random.nextLong();
|
||||||
|
@ -4,29 +4,39 @@ import net.minecraft.util.RandomSource;
|
|||||||
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
||||||
import net.minecraft.world.level.levelgen.WorldgenRandom;
|
import net.minecraft.world.level.levelgen.WorldgenRandom;
|
||||||
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
|
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import ru.dbotthepony.mc.prng.GJRAND64RandomSource;
|
import ru.dbotthepony.mc.prng.GJRAND64RandomSource;
|
||||||
|
|
||||||
@Mixin(WorldgenRandom.class)
|
@Mixin(WorldgenRandom.class)
|
||||||
public abstract class WorldgenRandomMixin {
|
public abstract class WorldgenRandomMixin {
|
||||||
@Shadow
|
@Shadow
|
||||||
protected RandomSource randomSource;
|
private RandomSource randomSource;
|
||||||
|
|
||||||
// quite lazy solution because if someone genuinely want to get lcg they wont be able to
|
// This is a mixed bag os a solution.
|
||||||
@Inject(
|
// On one hand, we cover all cases where WorldgenRandom getting called within vanilla code,
|
||||||
|
// on other hand it also detours and redirects calls done by mods, which *may* delibarately use LCG or Xoroshiro128++
|
||||||
|
// But we also need to consider that mods will likely just copy/do what vanilla code does, and hence
|
||||||
|
// they expect code to follow same behavior as vanilla, so in such case us redirecting PRNG is completely desirable
|
||||||
|
@Redirect(
|
||||||
method = "<init>(Lnet/minecraft/util/RandomSource;)V",
|
method = "<init>(Lnet/minecraft/util/RandomSource;)V",
|
||||||
at = @At("TAIL")
|
at = @At(
|
||||||
|
value = "FIELD",
|
||||||
|
target = "randomSource:Lnet/minecraft/util/RandomSource;",
|
||||||
|
opcode = Opcodes.PUTFIELD
|
||||||
|
)
|
||||||
)
|
)
|
||||||
public void constructorMix(RandomSource randomSource, CallbackInfo info) {
|
public void constructorMix(WorldgenRandom self, RandomSource randomSource) {
|
||||||
if (randomSource instanceof LegacyRandomSource rng) {
|
if (randomSource instanceof LegacyRandomSource rng) {
|
||||||
this.randomSource = new GJRAND64RandomSource(rng.seed.get());
|
this.randomSource = new GJRAND64RandomSource(rng.seed.get());
|
||||||
} else if (randomSource instanceof XoroshiroRandomSource rng) {
|
} else if (randomSource instanceof XoroshiroRandomSource rng) {
|
||||||
this.randomSource = new GJRAND64RandomSource(rng.randomNumberGenerator.seedHi, rng.randomNumberGenerator.seedLo);
|
this.randomSource = new GJRAND64RandomSource(rng.randomNumberGenerator.seedHi, rng.randomNumberGenerator.seedLo);
|
||||||
|
} else {
|
||||||
|
this.randomSource = randomSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ protected net.minecraft.world.RandomSequences includeSequenceId
|
|||||||
public net.minecraft.world.RandomSequences$DirtyMarkingRandomSource
|
public net.minecraft.world.RandomSequences$DirtyMarkingRandomSource
|
||||||
public net.minecraft.world.RandomSequences$DirtyMarkingRandomSource <init>(Lnet/minecraft/world/RandomSequences;Lnet/minecraft/util/RandomSource;)V
|
public net.minecraft.world.RandomSequences$DirtyMarkingRandomSource <init>(Lnet/minecraft/world/RandomSequences;Lnet/minecraft/util/RandomSource;)V
|
||||||
|
|
||||||
protected-f net.minecraft.world.level.levelgen.WorldgenRandom randomSource
|
private-f net.minecraft.world.level.levelgen.WorldgenRandom randomSource
|
||||||
public net.minecraft.world.level.levelgen.LegacyRandomSource seed
|
public net.minecraft.world.level.levelgen.LegacyRandomSource seed
|
||||||
public net.minecraft.world.level.levelgen.XoroshiroRandomSource randomNumberGenerator
|
public net.minecraft.world.level.levelgen.XoroshiroRandomSource randomNumberGenerator
|
||||||
public net.minecraft.world.level.levelgen.Xoroshiro128PlusPlus seedLo
|
public net.minecraft.world.level.levelgen.Xoroshiro128PlusPlus seedLo
|
||||||
|
Loading…
Reference in New Issue
Block a user