Revert "Remove otmRandom"

This reverts commit 5a016bef

The way OTM uses random generator in its code will quickly cause LCG used in Minecraft to show its bias
because LCG in minecraft samples its highest 48 bits, which gives us at best 2^16 period in the lowest bit returned by LCG.
Which it doesn't sound bad, it quickly causes RNG become biased the quicker/more it is sampled on each tick, especially considering
some may use `level.random.nextInt(chance) == 0` to determine chance of something happening,
which will get extremely biased on heavy RNG congested environment
If we avoid sampling Level's generator this much, we won't suffer from bias in our own code, as well as avoid biasing other mods this much.

The "2^16 period" problem is also might be the reason why Entities get their own instance of RandomSource, and Mob Goals use random exactly the way described above (`nextInt(chance)`), which can and will suffer from bias the moment mob exists in world for more than 2^16 ticks (but actual bias will happen sooner because RNG is not sampled only once per tick, obviously)
This commit is contained in:
DBotThePony 2025-03-15 10:04:16 +07:00
parent bc5ad7f37b
commit b7b2b8095c
Signed by: DBot
GPG Key ID: DCC23B5715498507
31 changed files with 105 additions and 60 deletions

View File

@ -0,0 +1,19 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.Level;
import net.neoforged.fml.ModList;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import ru.dbotthepony.mc.otm.core.IMatteryLevel;
import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource;
@Mixin(Level.class)
public abstract class LevelMixin implements IMatteryLevel {
private final RandomSource otm_random = ModList.get().isLoaded("better_random") ? null : new GJRAND64RandomSource();
@Override
public @Nullable RandomSource getOtmRandom() {
return otm_random;
}
}

View File

@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import ru.dbotthepony.mc.otm.player.IMatteryPlayer;
import ru.dbotthepony.mc.otm.core.IMatteryLevel;
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents;
@Mixin(AbstractHurtingProjectile.class)
@ -24,7 +25,7 @@ public class MixinAbstractHurtingProjectile {
AbstractHurtingProjectile proj = (AbstractHurtingProjectile)(Object)this;
if (cap.isAndroid() && proj.getOwner() != entity) {
entity.level().playSound(entity, proj.blockPosition(), MSoundEvents.INSTANCE.getANDROID_PROJ_PARRY(), SoundSource.PLAYERS, 1.0f, 0.95f + entity.level().getRandom().nextFloat() * 0.1f);
entity.level().playSound(entity, proj.blockPosition(), MSoundEvents.INSTANCE.getANDROID_PROJ_PARRY(), SoundSource.PLAYERS, 1.0f, 0.95f + ((IMatteryLevel) entity.level()).getOtmRandom().nextFloat() * 0.1f);
}
}
}

View File

@ -44,7 +44,7 @@ import ru.dbotthepony.mc.otm.core.math.getSphericalBlockPositions
import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.isClient
import ru.dbotthepony.mc.otm.matter.MatterManager
import ru.dbotthepony.mc.otm.registry.MDamageTypes
@ -271,7 +271,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
}
// шанс 1% что черная дыра потеряет 0.1 MtU каждую секунду * силу гравитации дыры ^ -1
if (level.random.nextDouble() < 0.01 * 0.05 * (1 / gravitationStrength)) {
if (level.otmRandom.nextDouble() < 0.01 * 0.05 * (1 / gravitationStrength)) {
this.mass += HAWKING_MASS_LOSE_STEP
}

View File

@ -34,7 +34,7 @@ import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.core.multiblock.MultiblockStatus
import ru.dbotthepony.mc.otm.core.multiblock.shapedMultiblock
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.util.InvalidableLazy
import ru.dbotthepony.mc.otm.menu.tech.BlackHoleGeneratorMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -86,8 +86,8 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
multiblock?.blockEntities(MatterHatchBlockEntity.INPUT_TAG)?.iterator()?.map { it.matter }?.toList() ?: listOf()
}
val energy = CombinedProfiledEnergyStorage(FlowDirection.NONE, energyTarget::value, { level?.random })
val matter = CombinedProfiledMatterStorage(FlowDirection.NONE, matterTarget::value, { level?.random })
val energy = CombinedProfiledEnergyStorage(FlowDirection.NONE, energyTarget::value, { level?.otmRandom })
val matter = CombinedProfiledMatterStorage(FlowDirection.NONE, matterTarget::value, { level?.otmRandom })
enum class Mode(val label: Component, val tooltip: Component) {
TARGET_MASS(TranslatableComponent("otm.gui.black_hole_generator.sustain.mode"), TranslatableComponent("otm.gui.black_hole_generator.sustain.desc")),

View File

@ -14,7 +14,7 @@ import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.shuffle
import ru.dbotthepony.mc.otm.graph.GraphNodeList
import ru.dbotthepony.mc.otm.onceServer
@ -627,7 +627,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
}
fun receiveEnergy(howMuch: Decimal, simulate: Boolean, fromNode: EnergyCableBlockEntity.Node, fromSide: RelativeSide): Decimal {
livelyNodesList.shuffle(fromNode.blockEntity.level!!.random)
livelyNodesList.shuffle(fromNode.blockEntity.level!!.otmRandom)
val itr = livelyNodesList.iterator()
var received = Decimal.ZERO

View File

@ -32,7 +32,7 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
@ -71,7 +71,7 @@ class CargoCrateBlockEntity(
if (interactingPlayers++ == 0 && level != null && !isRemoved && level.getBlockState(blockPos).block is CargoCrateBlock) {
level.setBlock(blockPos, blockState.setValue(CargoCrateBlock.IS_OPEN, true), Block.UPDATE_CLIENTS)
level.playSound(null, blockPos, MSoundEvents.CARGO_CRATE_OPEN, SoundSource.BLOCKS, 1f, 0.8f + level.random.nextFloat() * 0.2f)
level.playSound(null, blockPos, MSoundEvents.CARGO_CRATE_OPEN, SoundSource.BLOCKS, 1f, 0.8f + level.otmRandom.nextFloat() * 0.2f)
level.gameEvent(GameEvent.CONTAINER_OPEN, blockPos, GameEvent.Context.of(blockState))
}
}

View File

@ -24,7 +24,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
@ -126,7 +126,7 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
return JobContainer.success(
DecomposerJob(
(level?.random?.nextDouble() ?: 1.0) <= 0.2 * upgrades.failureMultiplier,
(level?.otmRandom?.nextDouble() ?: 1.0) <= 0.2 * upgrades.failureMultiplier,
matter.matter,
matter.complexity * MachinesConfig.MATTER_DECOMPOSER.workTimeMultiplier
)

View File

@ -28,7 +28,7 @@ import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.graph.matter.MatterNode
import ru.dbotthepony.mc.otm.matter.IMatterValue
@ -264,7 +264,7 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
return
}
if (failureChance * upgrades.failureMultiplier <= 0.0 || level!!.random.nextDouble() >= failureChance * upgrades.failureMultiplier)
if (failureChance * upgrades.failureMultiplier <= 0.0 || level!!.otmRandom.nextDouble() >= failureChance * upgrades.failureMultiplier)
repairProgress += progressPerTick
energy.extractEnergy(energyConsumption * (progressPerTick / thisProgressPerTick), false)

View File

@ -24,7 +24,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.graph.matter.MatterGraph
import ru.dbotthepony.mc.otm.item.matter.MatterDustItem
import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu
@ -117,7 +117,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
stack.shrink(1)
container.setChanged(0)
val actualMatter = dustMatter.matter * (0.4 + level!!.random.nextDouble() * 0.6)
val actualMatter = dustMatter.matter * (0.4 + level!!.otmRandom.nextDouble() * 0.6)
return JobContainer.success(
RecyclerJob(

View File

@ -28,7 +28,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.graph.matter.MatterNode
@ -184,7 +184,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
task = allocation.task.id,
matterValue = matter.matter,
pattern = Optional.ofNullable(allocation.pattern),
asDust = (level?.random?.nextDouble() ?: 1.0) * upgrades.failureMultiplier > (allocation.pattern?.researchPercent ?: 2.0),
asDust = (level?.otmRandom?.nextDouble() ?: 1.0) * upgrades.failureMultiplier > (allocation.pattern?.researchPercent ?: 2.0),
ticks = ticks,
))
}

View File

@ -35,7 +35,7 @@ import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.maybe
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu
import ru.dbotthepony.mc.otm.recipe.MatteryCookingRecipe
import ru.dbotthepony.mc.otm.recipe.MicrowaveRecipe
@ -153,7 +153,7 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
recipe.getResultItem(level.registryAccess()).copyWithCount(toProcess),
recipe.workTime * config.workTimeMultiplier,
config.energyConsumption * toProcess,
experience = recipe.experience.sample(level.random) * toProcess))
experience = recipe.experience.sample(level.otmRandom) * toProcess))
}
}

View File

@ -17,6 +17,7 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.getEntitiesInEllipsoid
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.shuffle
import ru.dbotthepony.mc.otm.menu.tech.AndroidChargerMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -57,7 +58,7 @@ class AndroidChargerBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
val ents = level.getEntitiesInEllipsoid(blockPos.center, Vec3(MachinesConfig.AndroidCharger.RADIUS_WIDTH, MachinesConfig.AndroidCharger.RADIUS_HEIGHT, MachinesConfig.AndroidCharger.RADIUS_WIDTH)) { it is Player }
ents.shuffle(level.random)
ents.shuffle(level.otmRandom)
for ((ent) in ents) {
val ply = (ent as Player).matteryPlayer

View File

@ -17,6 +17,7 @@ import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.shuffle
import ru.dbotthepony.mc.otm.core.util.countingLazy
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
@ -89,7 +90,7 @@ class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Ma
val y = blockPos.y.toDouble()
val z = blockPos.z.toDouble()
for (ent in level.getEntitiesOfClass(ServerPlayer::class.java, AABB(x, y, z, x + 1.0, y + 2.0, z + 1.0)).shuffle(level.random)) {
for (ent in level.getEntitiesOfClass(ServerPlayer::class.java, AABB(x, y, z, x + 1.0, y + 2.0, z + 1.0)).shuffle(level.otmRandom)) {
if (ent.matteryPlayer.isAndroid)
moveEnergy(energy, ent.matteryPlayer.androidEnergy, amount = energy.batteryLevel, simulate = false, ignoreFlowRestrictions = true)
}

View File

@ -23,7 +23,7 @@ import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.shuffle
import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -60,7 +60,7 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
if (!howMuch.isPositive)
return Decimal.ZERO
containerSlotIndices.shuffle(level!!.random)
containerSlotIndices.shuffle(level!!.otmRandom)
var summ = Decimal.ZERO
var remaining = howMuch

View File

@ -24,7 +24,7 @@ import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess
import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockListener
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.util.InvalidableLazy
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -43,7 +43,7 @@ class EnergyInterfaceBlockEntity(
multiblocks.keys.iterator().flatMap { it.blockEntities(TARGET).iterator() }.map { it.energyInterfaceTarget }.toList()
}
val energy = CombinedProfiledEnergyStorage(FlowDirection.input(isInput), targets::value, { level?.random })
val energy = CombinedProfiledEnergyStorage(FlowDirection.input(isInput), targets::value, { level?.otmRandom })
override fun onAddedToMultiblock(multiblock: IMultiblockAccess) {
check(!isRemoved) { "Block was removed" }

View File

@ -28,7 +28,7 @@ import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.getEntitiesInEllipsoid
import ru.dbotthepony.mc.otm.core.lookupOrThrow
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.util.countingLazy
import ru.dbotthepony.mc.otm.item.consumables.EssenceCapsuleItem
import ru.dbotthepony.mc.otm.item.EssenceServoItem
@ -173,7 +173,7 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
val diff = dmgPerExp - repairPoints.toFloat()
if (diff > 0f) {
repairPoints += if ((level?.random?.nextFloat() ?: 1f) <= diff) 1 else 0
repairPoints += if ((level?.otmRandom?.nextFloat() ?: 1f) <= diff) 1 else 0
}
experienceStored -= 1
@ -202,7 +202,7 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
level!!.playSound(null, ent.x, ent.y, ent.z,
SoundEvents.EXPERIENCE_ORB_PICKUP, SoundSource.BLOCKS,
0.1F, 0.5F + level!!.random.nextFloat() * 0.25F
0.1F, 0.5F + level!!.otmRandom.nextFloat() * 0.25F
)
}

View File

@ -21,7 +21,7 @@ import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.maybe
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes
@ -90,7 +90,7 @@ class PlatePressBlockEntity(
recipe.getResultItem(level.registryAccess()).copyWithCount(toProcess),
recipe.workTime * MachinesConfig.PLATE_PRESS.workTimeMultiplier,
MachinesConfig.PLATE_PRESS.energyConsumption * toProcess,
experience = recipe.experience.sample(level.random) * toProcess))
experience = recipe.experience.sample(level.otmRandom) * toProcess))
}
override fun tick() {

View File

@ -0,0 +1,17 @@
package ru.dbotthepony.mc.otm.core
import net.minecraft.util.RandomSource
import net.minecraft.world.level.Level
import net.neoforged.fml.ModList
interface IMatteryLevel {
/**
* OTM provided [RandomSource], which has better statistical parameters
*
* Original Minecraft use LCG, which may show bad behavior when repeatedly sampled *a lot*,
* which is what [Level]'s random is used for. OTM provided PRNG should behave better in this scenario.
*/
val otmRandom: RandomSource?
}
val Level.otmRandom: RandomSource get() = (this as IMatteryLevel).otmRandom ?: random

View File

@ -19,7 +19,7 @@ import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.menu.decorative.MinecartCargoCrateMenu
import ru.dbotthepony.mc.otm.registry.game.MItems
@ -90,7 +90,7 @@ class MinecartCargoCrate(
if (interactingPlayers++ == 0) {
if (!isRemoved) {
level().playSound(null, this, MSoundEvents.CARGO_CRATE_OPEN, SoundSource.BLOCKS, 1f, 0.8f + level().random.nextFloat() * 0.2f)
level().playSound(null, this, MSoundEvents.CARGO_CRATE_OPEN, SoundSource.BLOCKS, 1f, 0.8f + level().otmRandom.nextFloat() * 0.2f)
this.gameEvent(GameEvent.CONTAINER_OPEN, player)
PiglinAi.angerNearbyPiglins(player, true)
}

View File

@ -7,7 +7,7 @@ import net.minecraft.world.item.Items
import net.neoforged.bus.api.SubscribeEvent
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.registry.game.MItems
@ -17,8 +17,8 @@ object WitheredSkeletonSpawnHandler {
val entity = event.entity
if (entity is WitherSkeleton) {
val giveHelmet = event.level.random.nextFloat() < ServerConfig.WITHER_SKELETON_HELMET_CHANCE
val giveSword = event.level.random.nextFloat() < ServerConfig.WITHER_SKELETON_SWORD_CHANCE
val giveHelmet = event.level.otmRandom.nextFloat() < ServerConfig.WITHER_SKELETON_HELMET_CHANCE
val giveSword = event.level.otmRandom.nextFloat() < ServerConfig.WITHER_SKELETON_SWORD_CHANCE
if (giveHelmet) {
if (!entity.hasItemInSlot(EquipmentSlot.HEAD))

View File

@ -185,7 +185,7 @@ class CrudeBatteryItem : BatteryItem(ItemsConfig.Batteries.CRUDE) {
if (player is ServerPlayer) {
if (!mattery.androidEnergy.item.isEmpty) {
mattery.androidEnergy.item.getCapability(Capabilities.EnergyStorage.ITEM)?.let {
it.extractEnergy((it.maxEnergyStored * level.random.nextFloat() * .2f).roundToInt(), false)
it.extractEnergy((it.maxEnergyStored * level.otmRandom.nextFloat() * .2f).roundToInt(), false)
}
mattery.dropBattery()
@ -195,7 +195,7 @@ class CrudeBatteryItem : BatteryItem(ItemsConfig.Batteries.CRUDE) {
copyStack.count = 1
mattery.androidEnergy.item = copyStack
val extraDamageMult = level.random.nextFloat()
val extraDamageMult = level.otmRandom.nextFloat()
player.hurt(MatteryDamageSource(level.registryAccess().damageType(MDamageTypes.EMP), inflictor = itemStack), 1.5f + extraDamageMult * 3.5f)
val debuffDuration = 100 + (100 * (1f - extraDamageMult)).roundToInt()

View File

@ -17,6 +17,7 @@ import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.util.getLevelFromXp
import ru.dbotthepony.mc.otm.item.MatteryItem
import ru.dbotthepony.mc.otm.registry.game.MDataComponentTypes
@ -80,7 +81,7 @@ class EssenceCapsuleItem(private val digital: Boolean) : MatteryItem(Properties(
} else {
if (level is ServerLevel) {
level.levelEvent(2002, player.blockPosition(), PotionContents.getColor(Potions.WATER))
ExperienceOrb.award(level, player.position(), (exp * (.5 + level.random.nextFloat() * .25)).toInt())
ExperienceOrb.award(level, player.position(), (exp * (.5 + level.otmRandom.nextFloat() * .25)).toInt())
}
}

View File

@ -9,7 +9,7 @@ import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.entity.BreadMonster
import ru.dbotthepony.mc.otm.item.MatteryItem
@ -39,7 +39,7 @@ class ImperfectBreadItem(properties: Properties) : MatteryItem(properties) {
// roll multiple times so multiple bread monsters can spawn on tick
// and also chance be less biased
for (i in 0 until stack.count.coerceAtMost(16)) {
if (entity.level().random.nextFloat() < 0.001f) {
if (entity.level().otmRandom.nextFloat() < 0.001f) {
val ent = BreadMonster(entity.level())
ent.position = entity.position
entity.level().addFreshEntity(ent)

View File

@ -84,7 +84,7 @@ class ExplosiveHammerItem(durability: Int = 512) : Item(Properties().stacksTo(1)
itemStack.hurtAndBreak(8, level, player) {}
if (isPrimed(itemStack)) {
itemStack.hurtAndBreak(level.random.nextInt(1, 20), level, player) {}
itemStack.hurtAndBreak(level.otmRandom.nextInt(1, 20), level, player) {}
unprime(itemStack)
val (ex, ey, ez) = Vector.atCenterOf(player.blockPosition())
@ -226,7 +226,7 @@ class ExplosiveHammerItem(durability: Int = 512) : Item(Properties().stacksTo(1)
val copy = itemStack.copy()
itemStack.hurtAndBreak(level.random.nextInt(1, 20), attacker, EquipmentSlot.MAINHAND)
itemStack.hurtAndBreak(level.otmRandom.nextInt(1, 20), attacker, EquipmentSlot.MAINHAND)
if (!itemStack.isEmpty && attacker.random.nextDouble() <= ToolsConfig.ExplosiveHammer.FLY_OFF_CHANCE) {
attacker.setItemInHand(hand, ItemStack.EMPTY)

View File

@ -32,6 +32,7 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.math.nextVariance
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.item.MatteryItem
import ru.dbotthepony.mc.otm.item.addSimpleDescription
@ -96,10 +97,10 @@ class EnergySwordItem : MatteryItem(Properties().stacksTo(1).rarity(Rarity.RARE)
itemStack.getCapability(MatteryCapability.ITEM_ENERGY)?.let {
if (it.extractEnergyExact(ENERGY_PER_SWING, false)) {
it.extractEnergy(attacker.level().random.nextVariance(ENERGY_PER_SWING_VARIANCE), false)
it.extractEnergy(attacker.level().otmRandom.nextVariance(ENERGY_PER_SWING_VARIANCE), false)
victim.matteryPlayer?.let {
if (it.isAndroid && it.androidEnergy.extractEnergyExact(ENERGY_ZAP, false)) {
it.androidEnergy.extractEnergy(attacker.level().random.nextVariance(ENERGY_ZAP_VARIANCE), false)
it.androidEnergy.extractEnergy(attacker.level().otmRandom.nextVariance(ENERGY_ZAP_VARIANCE), false)
victim.hurt(MatteryDamageSource(attacker.level().registryAccess().damageType(MDamageTypes.EMP), attacker, itemStack), 8f)
}
}
@ -142,12 +143,12 @@ class EnergySwordItem : MatteryItem(Properties().stacksTo(1).rarity(Rarity.RARE)
if (blockState.`is`(BlockTags.SWORD_EFFICIENT)) {
if (energy?.extractEnergyExact(PLANT_POWER_COST, false) == true)
energy.extractEnergyExact(user.level().random.nextVariance(PLANT_POWER_COST_VARIANCE), false)
energy.extractEnergyExact(user.level().otmRandom.nextVariance(PLANT_POWER_COST_VARIANCE), false)
}
if (blockState.`is`(Blocks.COBWEB)) {
if (energy?.extractEnergyExact(COBWEB_POWER_COST, false) == true)
energy.extractEnergyExact(user.level().random.nextVariance(COBWEB_POWER_COST_VARIANCE), false)
energy.extractEnergyExact(user.level().otmRandom.nextVariance(COBWEB_POWER_COST_VARIANCE), false)
}
}

View File

@ -32,6 +32,7 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.math.nextVariance
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.item.MatteryItem
import ru.dbotthepony.mc.otm.item.addSimpleDescription
@ -96,10 +97,10 @@ class FallingSunItem : MatteryItem(Properties().stacksTo(1).rarity(Rarity.EPIC))
itemStack.getCapability(MatteryCapability.ITEM_ENERGY)?.let {
if (it.extractEnergyExact(ENERGY_PER_SWING, false)) {
it.extractEnergy(attacker.level().random.nextVariance(ENERGY_PER_SWING_VARIANCE), false)
it.extractEnergy(attacker.level().otmRandom.nextVariance(ENERGY_PER_SWING_VARIANCE), false)
victim.matteryPlayer?.let {
if (it.isAndroid && it.androidEnergy.extractEnergyExact(ENERGY_ZAP, false)) {
it.androidEnergy.extractEnergy(attacker.level().random.nextVariance(ENERGY_ZAP_VARIANCE), false)
it.androidEnergy.extractEnergy(attacker.level().otmRandom.nextVariance(ENERGY_ZAP_VARIANCE), false)
victim.hurt(MatteryDamageSource(attacker.level().registryAccess().damageType(MDamageTypes.EMP), attacker, itemStack), 8f)
}
}
@ -142,12 +143,12 @@ class FallingSunItem : MatteryItem(Properties().stacksTo(1).rarity(Rarity.EPIC))
if (blockState.`is`(BlockTags.SWORD_EFFICIENT)) {
if (energy?.extractEnergyExact(PLANT_POWER_COST, false) == true)
energy.extractEnergyExact(user.level().random.nextVariance(PLANT_POWER_COST_VARIANCE), false)
energy.extractEnergyExact(user.level().otmRandom.nextVariance(PLANT_POWER_COST_VARIANCE), false)
}
if (blockState.`is`(Blocks.COBWEB)) {
if (energy?.extractEnergyExact(COBWEB_POWER_COST, false) == true)
energy.extractEnergyExact(user.level().random.nextVariance(COBWEB_POWER_COST_VARIANCE), false)
energy.extractEnergyExact(user.level().otmRandom.nextVariance(COBWEB_POWER_COST_VARIANCE), false)
}
}

View File

@ -24,6 +24,7 @@ import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.math.toRadians
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.core.readItem
import ru.dbotthepony.mc.otm.core.writeItem
@ -445,7 +446,7 @@ class ExopackSmokePacket(val player: UUID) : CustomPacketPayload {
z += kotlin.math.sin(deg) * -0.4
val level = ply.level()
val random = level.random
val random = level.otmRandom
for (i in 0 .. random.nextInt(2, 4))
level.addParticle(

View File

@ -13,7 +13,7 @@ import net.neoforged.neoforge.network.handling.IPayloadContext
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.random
import ru.dbotthepony.mc.otm.core.otmRandom
class SmokeParticlesPacket(val x: Double, val y: Double, val z: Double) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
@ -24,7 +24,7 @@ class SmokeParticlesPacket(val x: Double, val y: Double, val z: Double) : Custom
fun play(context: IPayloadContext) {
minecraft.player?.level()?.let {
makeSmoke(x, y, z, it.random, it)
makeSmoke(x, y, z, it.otmRandom, it)
}
}

View File

@ -528,7 +528,7 @@ class MatteryPlayer(val ply: Player) {
override fun onJobTick(status: JobStatus<ItemJob>) {
super.onJobTick(status)
if (isExopackVisible && ply.level().random.nextFloat() <= 0.05f) {
if (isExopackVisible && ply.level().otmRandom.nextFloat() <= 0.05f) {
PacketDistributor.sendToPlayersTrackingEntityAndSelf(ply, ExopackSmokePacket(ply.uuid))
}
}
@ -1319,7 +1319,7 @@ class MatteryPlayer(val ply: Player) {
pos.mul(RenderSystem.getProjectionMatrix())
pos.mul(poseStack.last().pose())
makeSmoke(cam.x + pos.x, cam.y + pos.y, cam.z + pos.z, ply.level().random, ply.level())
makeSmoke(cam.x + pos.x, cam.y + pos.y, cam.z + pos.z, ply.level().otmRandom, ply.level())
}
}

View File

@ -49,6 +49,7 @@ import ru.dbotthepony.mc.otm.core.math.rotateXDegrees
import ru.dbotthepony.mc.otm.core.math.rotateYDegrees
import ru.dbotthepony.mc.otm.core.math.shortestDistanceBetween
import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.milliTime
import ru.dbotthepony.mc.otm.registry.game.AndroidFeatures
import ru.dbotthepony.mc.otm.triggers.EnderTeleporterFallDeathTrigger
@ -302,7 +303,7 @@ class EnderTeleporterFeature(capability: MatteryPlayer) : AndroidActiveFeature(A
val event = EventHooks.onEnderTeleport(ply, blockPos.x + 0.5, blockPos.y.toDouble(), blockPos.z + 0.5)
if (event.isCanceled) {
(ply as ServerPlayer).connection.send(ClientboundSoundEntityPacket(SoundEvents.ITEM_BREAK.holder, SoundSource.PLAYERS, ply, 0.3f, 0.5f, ply.level().random.nextLong()))
(ply as ServerPlayer).connection.send(ClientboundSoundEntityPacket(SoundEvents.ITEM_BREAK.holder, SoundSource.PLAYERS, ply, 0.3f, 0.5f, ply.level().otmRandom.nextLong()))
return false
}
@ -310,9 +311,9 @@ class EnderTeleporterFeature(capability: MatteryPlayer) : AndroidActiveFeature(A
lastTeleport = ply.server!!.tickCount
android.androidEnergy.extractEnergy(PlayerConfig.EnderTeleporter.ENERGY_COST, false)
ply.level().playSound(null, ply, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 0.3f, 0.8f + ply.level().random.nextFloat() * 0.4f)
ply.level().playSound(null, ply, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 0.3f, 0.8f + ply.level().otmRandom.nextFloat() * 0.4f)
ply.teleportTo(event.targetX, event.targetY, event.targetZ)
ply.level().playSound(null, ply, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1f, 0.8f + ply.level().random.nextFloat() * 0.4f)
ply.level().playSound(null, ply, SoundEvents.ENDERMAN_TELEPORT, SoundSource.PLAYERS, 1f, 0.8f + ply.level().otmRandom.nextFloat() * 0.4f)
ply.deltaMovement = Vector(0.0, 0.0, 0.0)
ply.resetFallDistance()

View File

@ -17,7 +17,8 @@
"HopperBlockEntityMixin",
"DispenserBlockEntityMixin",
"GuiGraphicsMixin",
"BlockStateBaseMixin"
"BlockStateBaseMixin",
"LevelMixin"
],
"client": [
"MixinGameRenderer",