Revise storage system to rely solely on ImpreciseFraction
This commit is contained in:
parent
ecc7d6e26d
commit
917faffcf9
@ -98,7 +98,7 @@ public final class OverdriveThatMatters {
|
||||
|
||||
private void setup(final FMLCommonSetupEvent event) {
|
||||
MatteryNetworking.register();
|
||||
ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125"));
|
||||
ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125"), false);
|
||||
|
||||
if (ModList.get().isLoaded("mekanism")) {
|
||||
MinecraftForge.EVENT_BUS.register(QIOKt.class);
|
||||
|
@ -198,7 +198,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
||||
val ioLimit = ioLimit
|
||||
|
||||
if (ioLimit != null) {
|
||||
diff = it.extractEnergy(howMuch.min(ioLimit), simulate)
|
||||
diff = it.extractEnergy(howMuch.coerceAtMost(ioLimit), simulate)
|
||||
} else {
|
||||
diff = it.extractEnergy(howMuch, simulate)
|
||||
}
|
||||
@ -230,7 +230,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
||||
val ioLimit = ioLimit
|
||||
|
||||
if (ioLimit != null) {
|
||||
diff = it.receiveEnergy(howMuch.min(ioLimit), simulate)
|
||||
diff = it.receiveEnergy(howMuch.coerceAtMost(ioLimit), simulate)
|
||||
} else {
|
||||
diff = it.receiveEnergy(howMuch, simulate)
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
if (workFlow) {
|
||||
if (matter.storedMatter < MATTER_EXCHANGE_RATE && graph != null) {
|
||||
val extracted = graph.extractMatter(
|
||||
matter.missingMatter.min(MATTER_EXCHANGE_RATE * EXTRACTION_TICKS, capability.missingMatter - matter.storedMatter), true
|
||||
matter.missingMatter.coerceAtMost(MATTER_EXCHANGE_RATE * EXTRACTION_TICKS).coerceAtMost(capability.missingMatter - matter.storedMatter), true
|
||||
)
|
||||
|
||||
if (extracted > ImpreciseFraction.ZERO) {
|
||||
@ -277,7 +277,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
val energyExtracted = energy.extractEnergyInner(ENERGY_CONSUMPTION, true)
|
||||
|
||||
if (!energyExtracted.isZero) {
|
||||
val matter = capability.receiveMatterOuter(MATTER_EXCHANGE_RATE.min(matter.storedMatter) * energyExtracted / ENERGY_CONSUMPTION, true)
|
||||
val matter = capability.receiveMatterOuter(MATTER_EXCHANGE_RATE.coerceAtMost(matter.storedMatter) * energyExtracted / ENERGY_CONSUMPTION, true)
|
||||
|
||||
if (!matter.isZero) {
|
||||
energy.extractEnergyInner(ENERGY_CONSUMPTION * matter / MATTER_EXCHANGE_RATE, false)
|
||||
@ -301,7 +301,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
val energyExtracted = energy.extractEnergyInner(ENERGY_CONSUMPTION, true)
|
||||
|
||||
if (!energyExtracted.isZero) {
|
||||
val matter = capability.extractMatterOuter(MATTER_EXCHANGE_RATE.min(matter.missingMatter) * energyExtracted / ENERGY_CONSUMPTION, true)
|
||||
val matter = capability.extractMatterOuter(MATTER_EXCHANGE_RATE.coerceAtMost(matter.missingMatter) * energyExtracted / ENERGY_CONSUMPTION, true)
|
||||
|
||||
if (!matter.isZero) {
|
||||
this.energy.extractEnergyInner(ENERGY_CONSUMPTION * matter / MATTER_EXCHANGE_RATE,false)
|
||||
|
@ -159,7 +159,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
// в тик требуется меньше материи, чем её может хранить репликатор
|
||||
// примем из сети недостающее количество бака материи, или 200 тиков репликации, что меньше
|
||||
val toExtract =
|
||||
matter.missingMatter.min(drainPerTick.times(DRAIN_MULT))
|
||||
matter.missingMatter.coerceAtMost(drainPerTick.times(DRAIN_MULT))
|
||||
|
||||
val drain = graph.extractMatter(toExtract, true)
|
||||
|
||||
|
@ -37,8 +37,20 @@ import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
private class SlotTuple(val slot: Int, val stack: ItemStack)
|
||||
private class TrackedTuple(val stack: ItemStackWrapper, val id: UUID) {
|
||||
private class TrackedTuple(override val stack: ItemStackWrapper, override val id: UUID) : IStorageTuple<ItemStackWrapper> {
|
||||
val children = Int2ObjectAVLTreeMap<SlotTuple>()
|
||||
|
||||
override fun toString(): String {
|
||||
return "TrackedTuple[$stack $id]"
|
||||
}
|
||||
}
|
||||
|
||||
private fun Long.clamp(): Int {
|
||||
if (this > Int.MAX_VALUE) {
|
||||
return Int.MAX_VALUE
|
||||
}
|
||||
|
||||
return this.toInt()
|
||||
}
|
||||
|
||||
private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent<ItemStackWrapper> {
|
||||
@ -77,14 +89,24 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
||||
|
||||
if (scannedMap.stack.count.isPositive) {
|
||||
for (listener in listeners) {
|
||||
listener.changeStack(scannedMap.stack, scannedMap.id, count)
|
||||
listener.changeStack(scannedMap, count)
|
||||
}
|
||||
} else {
|
||||
for (listener in listeners) {
|
||||
listener.removeStack(scannedMap.stack, scannedMap.id)
|
||||
listener.removeStack(scannedMap)
|
||||
}
|
||||
|
||||
index.remove(scannedMap.id)
|
||||
|
||||
val listing = partitioned[scannedMap.stack.longHashCode] ?: throw IllegalStateException("Item listing is not present for ${scannedMap.stack}")
|
||||
|
||||
if (listing.remove(scannedMap)) {
|
||||
if (listing.isEmpty()) {
|
||||
partitioned.remove(scannedMap.stack.longHashCode)
|
||||
}
|
||||
} else {
|
||||
throw IllegalStateException("Item listing for ${scannedMap.stack} did not contain $scannedMap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,11 +126,11 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
||||
private fun addTracked(slot: Int, stack: ItemStack) {
|
||||
check(scannedMap[slot] == null) { "Already tracking slot $slot" }
|
||||
|
||||
val listing = partitioned.computeIfAbsent(ItemStackWrapper.partitionKey(stack.item), Long2ObjectFunction { ArrayList() })
|
||||
val listing = partitioned.computeIfAbsent(ItemStackWrapper.longHashCode(stack.item), Long2ObjectFunction { ArrayList() })
|
||||
var tuple: TrackedTuple? = null
|
||||
|
||||
for (storedTuple in listing) {
|
||||
if (ItemStack.isSameItemSameTags(stack, storedTuple.stack.stack)) {
|
||||
if (storedTuple.stack.sameItem(stack)) {
|
||||
tuple = storedTuple
|
||||
break
|
||||
}
|
||||
@ -118,7 +140,7 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
||||
var oldCount = ImpreciseFraction.ZERO
|
||||
|
||||
if (added) {
|
||||
val storageStack = ItemStackWrapper(stack.copy())
|
||||
val storageStack = ItemStackWrapper(stack)
|
||||
tuple = TrackedTuple(storageStack, UUID.randomUUID())
|
||||
index[tuple.id] = tuple
|
||||
listing.add(tuple)
|
||||
@ -232,9 +254,9 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
||||
if (!amount.isPositive)
|
||||
return ItemStackWrapper.EMPTY
|
||||
|
||||
val intAmount = amount.toInt()
|
||||
val intAmount = amount.toLong()
|
||||
val tuple = index[id] ?: return ItemStackWrapper.EMPTY
|
||||
var totalExtracted = 0
|
||||
var totalExtracted = 0L
|
||||
|
||||
val iter = tuple.children.values.iterator()
|
||||
val listCopy = Array(tuple.children.values.size) {
|
||||
@ -244,14 +266,14 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
||||
val copy = tuple.stack.copy()
|
||||
|
||||
for (stack in listCopy) {
|
||||
val extracted = parent.extractItem(stack.slot, stack.stack.count.coerceAtMost(intAmount - totalExtracted), true)
|
||||
val extracted = parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), true)
|
||||
|
||||
if (extracted.isEmpty) {
|
||||
// dummy condition
|
||||
continue
|
||||
} else if (ItemStack.isSameItemSameTags(extracted, tuple.stack.stack)) {
|
||||
} else if (tuple.stack.sameItem(extracted)) {
|
||||
if (!simulate) {
|
||||
parent.extractItem(stack.slot, stack.stack.count.coerceAtMost(intAmount - totalExtracted), false)
|
||||
parent.extractItem(stack.slot, stack.stack.count.coerceAtMost((intAmount - totalExtracted).clamp()), false)
|
||||
}
|
||||
|
||||
totalExtracted += extracted.count
|
||||
@ -269,11 +291,11 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
||||
}
|
||||
}
|
||||
|
||||
if (totalExtracted == 0) {
|
||||
if (totalExtracted == 0L) {
|
||||
return ItemStackWrapper.EMPTY
|
||||
}
|
||||
|
||||
copy.setCount(totalExtracted)
|
||||
copy.count = ImpreciseFraction(totalExtracted)
|
||||
return copy
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ sealed class ItemEnergyStorageImpl(
|
||||
val maxOutput = maxOutput
|
||||
|
||||
if (maxOutput != null) {
|
||||
howMuch = howMuch.min(maxOutput)
|
||||
howMuch = howMuch.coerceAtMost(maxOutput)
|
||||
}
|
||||
|
||||
val batteryLevel = batteryLevel
|
||||
@ -101,7 +101,7 @@ sealed class ItemEnergyStorageImpl(
|
||||
val maxInput = maxInput
|
||||
|
||||
if (maxInput != null) {
|
||||
howMuch = howMuch.min(maxInput)
|
||||
howMuch = howMuch.coerceAtMost(maxInput)
|
||||
}
|
||||
|
||||
val batteryLevel = batteryLevel
|
||||
@ -109,7 +109,7 @@ sealed class ItemEnergyStorageImpl(
|
||||
if (batteryLevel >= maxBatteryLevel)
|
||||
return ImpreciseFraction.ZERO
|
||||
|
||||
val newLevel = (batteryLevel + howMuch).min(maxBatteryLevel)
|
||||
val newLevel = (batteryLevel + howMuch).coerceAtMost(maxBatteryLevel)
|
||||
val diff = (newLevel - batteryLevel)
|
||||
|
||||
if (!simulate && batteryLevel != newLevel) {
|
||||
@ -173,7 +173,7 @@ sealed class BlockEnergyStorageImpl constructor(
|
||||
val maxOutput = maxOutput
|
||||
|
||||
if (maxOutput != null) {
|
||||
howMuch = howMuch.min(maxOutput)
|
||||
howMuch = howMuch.coerceAtMost(maxOutput)
|
||||
}
|
||||
|
||||
if (!batteryLevel.isPositive)
|
||||
@ -199,13 +199,13 @@ sealed class BlockEnergyStorageImpl constructor(
|
||||
val maxInput = maxInput
|
||||
|
||||
if (maxInput != null) {
|
||||
howMuch = howMuch.min(maxInput)
|
||||
howMuch = howMuch.coerceAtMost(maxInput)
|
||||
}
|
||||
|
||||
if (batteryLevel >= maxBatteryLevel)
|
||||
return ImpreciseFraction.ZERO
|
||||
|
||||
val newLevel = (batteryLevel + howMuch).min(maxBatteryLevel)
|
||||
val newLevel = (batteryLevel + howMuch).coerceAtMost(maxBatteryLevel)
|
||||
val diff = (newLevel - batteryLevel)
|
||||
|
||||
if (!simulate && batteryLevel != newLevel) {
|
||||
|
@ -382,7 +382,7 @@ open class AndroidCapability(final override val entity: LivingEntity) : ICapabil
|
||||
}
|
||||
}
|
||||
|
||||
val new = (battery + howMuch).min(maxBattery)
|
||||
val new = (battery + howMuch).coerceAtMost(maxBattery)
|
||||
received += new - battery
|
||||
|
||||
if (!simulate) {
|
||||
|
@ -40,10 +40,10 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
override fun insertStack(stack: T, simulate: Boolean): T {
|
||||
val maxInsert = driveCapacity.minus(storedCount).min(stack.count)
|
||||
val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count)
|
||||
if (maxInsert <= ImpreciseFraction.ZERO) return stack
|
||||
|
||||
val listing = storedStacks.computeIfAbsent(stack.partitionKey, Long2ObjectFunction { ArrayList() })
|
||||
val listing = storedStacks.computeIfAbsent(stack.longHashCode, Long2ObjectFunction { ArrayList() })
|
||||
|
||||
for (state in listing) {
|
||||
if (state.stack.sameItem(stack)) {
|
||||
@ -98,10 +98,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
||||
var amount = amount
|
||||
val get = storedByID[id] ?: return storageType.empty
|
||||
|
||||
if (amount <= ImpreciseFraction.ZERO)
|
||||
if (!storageType.fractional)
|
||||
amount = amount.floor()
|
||||
|
||||
if (!amount.isPositive)
|
||||
amount = get.stack.maxStackSize ?: get.stack.count
|
||||
|
||||
amount = amount.min(get.stack.count)
|
||||
amount = amount.coerceAtMost(get.stack.count)
|
||||
|
||||
if (amount <= ImpreciseFraction.ZERO)
|
||||
return storageType.empty
|
||||
@ -110,8 +113,8 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
||||
copy.count = amount
|
||||
|
||||
if (!simulate) {
|
||||
if (amount.compareTo(get.stack.count) == 0) {
|
||||
val listing = storedStacks[get.stack.partitionKey]!!
|
||||
if (amount == get.stack.count) {
|
||||
val listing = storedStacks[get.stack.longHashCode] ?: throw IllegalStateException("Can't find storage list with partition ${get.stack.longHashCode} for ${get.stack}")
|
||||
listing.remove(get)
|
||||
storedDifferentStacks--
|
||||
|
||||
@ -120,7 +123,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
||||
}
|
||||
|
||||
if (listing.size == 0) {
|
||||
storedStacks.remove(get.stack.partitionKey)
|
||||
storedStacks.remove(get.stack.longHashCode)
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +131,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
||||
val oldCount = get.stack.count
|
||||
get.stack.shrink(amount)
|
||||
|
||||
if (!get.stack.count.isZero) {
|
||||
if (get.stack.count.isPositive) {
|
||||
for (listener in listeners) {
|
||||
listener.changeStack(get.stack, get.id, oldCount)
|
||||
}
|
||||
@ -182,13 +185,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
||||
val stack = deserializeStack(entry)
|
||||
|
||||
if (stack != null) {
|
||||
storedCount = storedCount.plus(stack.count)
|
||||
storedCount += stack.count
|
||||
storedDifferentStacks++
|
||||
|
||||
val id = entry.getLongArray("id")
|
||||
val tuple = StorageTuple(if (id.size == 2) UUID(id[0], id[1]) else UUID.randomUUID(), stack)
|
||||
|
||||
storedStacks.computeIfAbsent(stack.partitionKey, Long2ObjectFunction { ArrayList() }).add(tuple)
|
||||
storedStacks.computeIfAbsent(stack.longHashCode, Long2ObjectFunction { ArrayList() }).add(tuple)
|
||||
storedByID[tuple.id] = tuple
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,12 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
||||
|
||||
override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? {
|
||||
val tag = CompoundTag()
|
||||
val location = item.stack.stack.item.registryName!!.toString()
|
||||
val location = item.stack.registryName.toString()
|
||||
|
||||
tag["item"] = location
|
||||
tag["count"] = item.stack.stack.count
|
||||
tag["count"] = item.stack.count.serializeNBT()
|
||||
|
||||
val itag = item.stack.stack.tag
|
||||
val itag = item.stack.item.tag
|
||||
|
||||
if (itag != null) {
|
||||
tag["data"] = itag
|
||||
@ -56,32 +56,22 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
||||
}
|
||||
|
||||
override fun findItems(item: Item): List<IStorageTuple<ItemStackWrapper>> {
|
||||
val list = storedStacks[ItemStackWrapper.partitionKey(item)]
|
||||
val list = storedStacks[ItemStackWrapper.longHashCode(item)]
|
||||
return if (list != null) java.util.List.copyOf(list) else emptyList()
|
||||
}
|
||||
|
||||
override fun findItems(stack: ItemStack): List<IStorageTuple<ItemStackWrapper>> {
|
||||
val list = storedStacks[ItemStackWrapper.partitionKey(stack.item)] ?: return emptyList()
|
||||
val list = storedStacks[ItemStackWrapper.longHashCode(stack.item)] ?: return emptyList()
|
||||
|
||||
var amount = 0
|
||||
val buildList = ArrayList<IStorageTuple<ItemStackWrapper>>()
|
||||
|
||||
for (_stack in list) {
|
||||
if (ItemStack.tagMatches(_stack.stack.stack, stack)) {
|
||||
amount++
|
||||
if (_stack.stack.sameItem(stack)) {
|
||||
buildList.add(_stack)
|
||||
}
|
||||
}
|
||||
|
||||
val build_list = ArrayList<IStorageTuple<ItemStackWrapper>>(amount)
|
||||
var i = 0
|
||||
|
||||
for (_stack in list) {
|
||||
if (ItemStack.tagMatches(_stack.stack.stack, stack)) {
|
||||
build_list[i] = _stack
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return build_list
|
||||
return buildList
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -45,9 +45,9 @@ open class MatterHandlerImpl @JvmOverloads constructor(
|
||||
val new: ImpreciseFraction
|
||||
|
||||
if (maxReceive == null) {
|
||||
new = (storedMatter + howMuch).min(maxStoredMatter)
|
||||
new = (storedMatter + howMuch).coerceAtMost(maxStoredMatter)
|
||||
} else {
|
||||
new = (storedMatter + howMuch.min(maxReceive)).min(maxStoredMatter)
|
||||
new = (storedMatter + howMuch.coerceAtMost(maxReceive)).coerceAtMost(maxStoredMatter)
|
||||
}
|
||||
|
||||
val diff = new - storedMatter
|
||||
@ -73,7 +73,7 @@ open class MatterHandlerImpl @JvmOverloads constructor(
|
||||
if (maxExtract == null) {
|
||||
new = (storedMatter - howMuch).moreThanZero()
|
||||
} else {
|
||||
new = (storedMatter - howMuch.min(maxExtract)).moreThanZero()
|
||||
new = (storedMatter - howMuch.coerceAtMost(maxExtract)).moreThanZero()
|
||||
}
|
||||
|
||||
val diff = storedMatter - new
|
||||
|
@ -6,7 +6,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import mekanism.api.math.MathUtils
|
||||
import mekanism.common.content.qio.QIOFrequency
|
||||
import mekanism.common.content.qio.QIOFrequency.QIOItemTypeData
|
||||
import mekanism.common.content.transporter.TransporterManager
|
||||
import mekanism.common.lib.frequency.Frequency
|
||||
import mekanism.common.lib.inventory.HashedItem
|
||||
import mekanism.common.tile.qio.TileEntityQIODriveArray
|
||||
@ -24,6 +23,7 @@ import ru.dbotthepony.mc.otm.addPostServerTickerOnce
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
|
||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||
import ru.dbotthepony.mc.otm.core.toImpreciseFraction
|
||||
import ru.dbotthepony.mc.otm.graph.storage.BasicStorageGraphNode
|
||||
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
|
||||
import ru.dbotthepony.mc.otm.storage.*
|
||||
@ -58,13 +58,13 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
||||
|
||||
override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper {
|
||||
// Because there is no simulate method on QIO array, we have to simulate it by ourselves.
|
||||
val hash = HashedItem.create(stack.stack)
|
||||
val hash = HashedItem.create(stack.item)
|
||||
|
||||
if (!simulate) {
|
||||
//val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack))
|
||||
val used = parent.addItem(stack.stack)
|
||||
|
||||
if (used.count == stack.stack.count) {
|
||||
if (used.count == stack.count.toInt()) {
|
||||
return stack
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
||||
}
|
||||
|
||||
val inserted = stack.copy()
|
||||
inserted.stack.count = MathUtils.clampToInt(parent.totalItemCountCapacity - parent.totalItemCount).coerceAtMost(stack.stack.count)
|
||||
inserted.count = (parent.totalItemCountCapacity - parent.totalItemCount).toImpreciseFraction().coerceAtMost(stack.count)
|
||||
return inserted
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
||||
local = index[id] ?: return ItemStackWrapper.EMPTY // unexpected...
|
||||
|
||||
if (simulate) {
|
||||
return local.stack.copy().also { it.stack.count = it.stack.count.coerceAtMost(amount.toInt()) }
|
||||
return local.stack.copy().also { it.count = it.count.coerceAtMost(amount) }
|
||||
}
|
||||
|
||||
val removed = parent.removeByType(local.mekanismItem, amount.toInt())
|
||||
@ -106,7 +106,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
||||
return ItemStackWrapper.EMPTY
|
||||
}
|
||||
|
||||
val copy = ItemStackWrapper(removed.copy())
|
||||
val copy = ItemStackWrapper(removed)
|
||||
|
||||
if (local.stack.count > copy.count) {
|
||||
// expecting stack to be still present in QIO storage grid
|
||||
@ -147,16 +147,16 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
||||
if (local != null) {
|
||||
local.mark = mark
|
||||
|
||||
if (local.stack.stack.count != MathUtils.clampToInt(value.count)) {
|
||||
if (local.stack.count.toLong() != value.count) {
|
||||
val oldCount = local.stack.count
|
||||
local.stack.stack.count = MathUtils.clampToInt(value.count)
|
||||
local.stack.count = value.count.toImpreciseFraction()
|
||||
|
||||
for (listener in listeners) {
|
||||
listener.changeStack(local, oldCount)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val tuple = QIOTuple(at, ItemStackWrapper(at.stack.copy().also { it.count = MathUtils.clampToInt(value.count) }), UUID.randomUUID(), mark)
|
||||
val tuple = QIOTuple(at, ItemStackWrapper(at.stack).also { it.count = value.count.toImpreciseFraction() }, UUID.randomUUID(), mark)
|
||||
index[tuple.id] = tuple
|
||||
|
||||
for (listener in listeners) {
|
||||
|
@ -1,8 +1,12 @@
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package ru.dbotthepony.mc.otm.core
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Vec3i
|
||||
import java.math.BigDecimal
|
||||
|
||||
operator fun BlockPos.plus(direction: Vec3i): BlockPos = this.offset(direction)
|
||||
operator fun BlockPos.plus(direction: Direction): BlockPos = this.offset(direction.normal)
|
||||
@ -20,3 +24,188 @@ operator fun Vec3i.times(double: Double): Vector = Vector(x * double, y * double
|
||||
fun BlockPos.asVector(): Vector {
|
||||
return Vector(x + 0.5, y + 0.5, z + 0.5)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type check+cast and sums two numbers.
|
||||
*
|
||||
* Returns the same type as on input (adding Double to Float will not return Double, etc.)
|
||||
*/
|
||||
fun Number.dynPlus(other: Number): Number {
|
||||
return when (this) {
|
||||
is Float -> this + other.toFloat()
|
||||
is Double -> this + other.toDouble()
|
||||
is Int -> this + other.toInt()
|
||||
is Long -> this + other.toLong()
|
||||
is Short -> this + other.toShort()
|
||||
is Byte -> this + other.toByte()
|
||||
|
||||
is ImpreciseFraction -> {
|
||||
when (other) {
|
||||
is Float -> this + other.toFloat()
|
||||
is Double -> this + other.toDouble()
|
||||
is Int, is Byte, is Short -> this + other.toInt()
|
||||
is Long -> this + other.toLong()
|
||||
is ImpreciseFraction -> this + other
|
||||
is Fraction -> this + other.toImpreciseFraction()
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
is Fraction -> {
|
||||
when (other) {
|
||||
is Float -> this + other.toFloat()
|
||||
is Double -> this + other.toDouble()
|
||||
is Int, is Byte, is Short -> this + other.toInt()
|
||||
is Long -> this + other.toLong()
|
||||
is ImpreciseFraction -> this + other.toFraction()
|
||||
is Fraction -> this + other
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("I don't know what to do with ${this::class.qualifiedName} (left hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type check+cast and subtracts two numbers.
|
||||
*
|
||||
* Returns the same type as on input (adding Double to Float will not return Double, etc.)
|
||||
*/
|
||||
fun Number.dynMinus(other: Number): Number {
|
||||
return when (this) {
|
||||
is Float -> this - other.toFloat()
|
||||
is Double -> this - other.toDouble()
|
||||
is Int -> this - other.toInt()
|
||||
is Long -> this - other.toLong()
|
||||
is Short -> this - other.toShort()
|
||||
is Byte -> this - other.toByte()
|
||||
|
||||
is ImpreciseFraction -> {
|
||||
when (other) {
|
||||
is Float -> this - other.toFloat()
|
||||
is Double -> this - other.toDouble()
|
||||
is Int, is Byte, is Short -> this - other.toInt()
|
||||
is Long -> this - other.toLong()
|
||||
is ImpreciseFraction -> this - other
|
||||
is Fraction -> this - other.toImpreciseFraction()
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
is Fraction -> {
|
||||
when (other) {
|
||||
is Float -> this - other.toFloat()
|
||||
is Double -> this - other.toDouble()
|
||||
is Int, is Byte, is Short -> this - other.toInt()
|
||||
is Long -> this - other.toLong()
|
||||
is ImpreciseFraction -> this - other.toFraction()
|
||||
is Fraction -> this - other
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("I don't know what to do with ${this::class.qualifiedName} (left hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type check+cast and multiplies two numbers.
|
||||
*
|
||||
* Returns the same type as on input (adding Double to Float will not return Double, etc.)
|
||||
*/
|
||||
fun Number.dynTimes(other: Number): Number {
|
||||
return when (this) {
|
||||
is Float -> this * other.toFloat()
|
||||
is Double -> this * other.toDouble()
|
||||
is Int -> this * other.toInt()
|
||||
is Long -> this * other.toLong()
|
||||
is Short -> this * other.toShort()
|
||||
is Byte -> this * other.toByte()
|
||||
|
||||
is ImpreciseFraction -> {
|
||||
when (other) {
|
||||
is Float -> this * other.toFloat()
|
||||
is Double -> this * other.toDouble()
|
||||
is Int, is Byte, is Short -> this * other.toInt()
|
||||
is Long -> this * other.toLong()
|
||||
is ImpreciseFraction -> this * other
|
||||
is Fraction -> this * other.toImpreciseFraction()
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
is Fraction -> {
|
||||
when (other) {
|
||||
is Float -> this * other.toFloat()
|
||||
is Double -> this * other.toDouble()
|
||||
is Int, is Byte, is Short -> this * other.toInt()
|
||||
is Long -> this * other.toLong()
|
||||
is ImpreciseFraction -> this * other.toFraction()
|
||||
is Fraction -> this * other
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("I don't know what to do with ${this::class.qualifiedName} (left hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type check+cast and divides two numbers.
|
||||
*
|
||||
* Returns the same type as on input (adding Double to Float will not return Double, etc.)
|
||||
*/
|
||||
fun Number.dynDiv(other: Number): Number {
|
||||
return when (this) {
|
||||
is Float -> this / other.toFloat()
|
||||
is Double -> this / other.toDouble()
|
||||
is Int -> this / other.toInt()
|
||||
is Long -> this / other.toLong()
|
||||
is Short -> this / other.toShort()
|
||||
is Byte -> this / other.toByte()
|
||||
|
||||
is ImpreciseFraction -> {
|
||||
when (other) {
|
||||
is Float -> this / other.toFloat()
|
||||
is Double -> this / other.toDouble()
|
||||
is Int, is Byte, is Short -> this / other.toInt()
|
||||
is Long -> this / other.toLong()
|
||||
is ImpreciseFraction -> this / other
|
||||
is Fraction -> this / other.toImpreciseFraction()
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
is Fraction -> {
|
||||
when (other) {
|
||||
is Float -> this / other.toFloat()
|
||||
is Double -> this / other.toDouble()
|
||||
is Int, is Byte, is Short -> this / other.toInt()
|
||||
is Long -> this / other.toLong()
|
||||
is ImpreciseFraction -> this / other.toFraction()
|
||||
is Fraction -> this / other
|
||||
else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("I don't know what to do with ${this::class.qualifiedName} (left hand operand)")
|
||||
}
|
||||
}
|
||||
|
||||
val Number.isFractional get() = this is Float || this is Double || this is ImpreciseFraction || this is Fraction
|
||||
val Number.isWhole get() = !isFractional
|
||||
|
||||
fun Number.toImpreciseFraction(): ImpreciseFraction {
|
||||
return when (this) {
|
||||
is ImpreciseFraction -> this
|
||||
is Float -> ImpreciseFraction(this)
|
||||
is Double -> ImpreciseFraction(this)
|
||||
is Int -> ImpreciseFraction(this)
|
||||
is Byte -> ImpreciseFraction(this)
|
||||
is Short -> ImpreciseFraction(this)
|
||||
is Long -> ImpreciseFraction(this)
|
||||
is Fraction -> this.toImpreciseFraction()
|
||||
else -> throw ClassCastException("Can not turn $this into ImpreciseFraction")
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,6 @@ import java.math.BigInteger
|
||||
import java.math.MathContext
|
||||
import java.math.RoundingMode
|
||||
|
||||
private val IMPRECISE_CONTEXT = MathContext(17)
|
||||
|
||||
private fun powScale(int: Int): BigInteger {
|
||||
if (int <= 0)
|
||||
return BigInteger.ONE
|
||||
@ -118,6 +116,7 @@ private val GCDs = arrayOf(
|
||||
|
||||
private val COMPACT_THRESHOLD = BigInteger("1000000000000000000000")
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
private fun compactTwo(value1: BigInteger, value2: BigInteger, compact: Boolean = true): Fraction {
|
||||
when (value1.signum()) {
|
||||
0 -> return Fraction(value1, value2, compact = compact)
|
||||
@ -218,6 +217,7 @@ private fun unsignedInt(value: Byte): Int {
|
||||
|
||||
private data class MagnitudeCrunchResult(val value: BigInteger, val mag: Int)
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
private fun magnitude(value: BigInteger): MagnitudeCrunchResult {
|
||||
if (isZero(value))
|
||||
return MagnitudeCrunchResult(value, 0)
|
||||
@ -238,9 +238,12 @@ private fun magnitude(value: BigInteger): MagnitudeCrunchResult {
|
||||
return MagnitudeCrunchResult(value, mag)
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
@Suppress("unused")
|
||||
data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @JvmField val divisor: BigInteger = BigInteger.ONE, @JvmField val compact: Boolean = true) : Comparable<Fraction> {
|
||||
class Fraction @JvmOverloads constructor(
|
||||
val value: BigInteger,
|
||||
val divisor: BigInteger = BigInteger.ONE,
|
||||
val compact: Boolean = true
|
||||
) : Number(), Comparable<Fraction> {
|
||||
@JvmOverloads constructor(value: Long, compact: Boolean = true) : this(BigInteger.valueOf(value), compact = compact)
|
||||
@JvmOverloads constructor(value: Int, compact: Boolean = true) : this(BigInteger.valueOf(value.toLong()), compact = compact)
|
||||
@JvmOverloads constructor(value: Float, compact: Boolean = true) : this(BigDecimal(value.toString()), compact = compact)
|
||||
@ -589,12 +592,12 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @
|
||||
}
|
||||
|
||||
// Преобразования
|
||||
fun toFloat(): Float {
|
||||
override fun toFloat(): Float {
|
||||
if (isNaN()) return Float.NaN
|
||||
return (value / divisor).toFloat() + ((value % divisor).toFloat() / divisor.toFloat())
|
||||
}
|
||||
|
||||
fun toDouble(): Double {
|
||||
override fun toDouble(): Double {
|
||||
if (isNaN()) return Double.NaN
|
||||
return (value / divisor).toDouble() + ((value % divisor).toDouble() / divisor.toDouble())
|
||||
}
|
||||
@ -607,7 +610,7 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @
|
||||
val a = divisor.toDouble()
|
||||
val b = div[1].toDouble()
|
||||
|
||||
if (b == 0.0 || b == -0.0 || !a.isFinite() || !b.isFinite()) {
|
||||
if (b == 0.0 || !a.isFinite() || !b.isFinite()) {
|
||||
return ImpreciseFraction(div[0])
|
||||
}
|
||||
|
||||
@ -650,16 +653,28 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @
|
||||
)
|
||||
}
|
||||
|
||||
fun toInt(): Int {
|
||||
override fun toInt(): Int {
|
||||
if (isNaN()) throw ArithmeticException("Fraction is not a number")
|
||||
return (value / divisor).toInt()
|
||||
}
|
||||
|
||||
fun toLong(): Long {
|
||||
override fun toLong(): Long {
|
||||
if (isNaN()) throw ArithmeticException("Fraction is not a number")
|
||||
return (value / divisor).toLong()
|
||||
}
|
||||
|
||||
override fun toByte(): Byte {
|
||||
return toInt().toByte()
|
||||
}
|
||||
|
||||
override fun toChar(): Char {
|
||||
return toInt().toChar()
|
||||
}
|
||||
|
||||
override fun toShort(): Short {
|
||||
return toInt().toShort()
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun toBigDecimal(context: MathContext = DEFAULT_MATH_CONTEXT): BigDecimal {
|
||||
if (isNaN()) throw ArithmeticException("Fraction is not a number")
|
||||
|
@ -68,6 +68,11 @@ private fun bytesToLongBE(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant value which represent edge of meaningful bits.
|
||||
*
|
||||
* Equals to 0.000000000001
|
||||
*/
|
||||
const val EPSILON = 0.000000000001
|
||||
|
||||
fun weakEqualDoubles(a: Double, b: Double): Boolean {
|
||||
@ -110,8 +115,20 @@ private const val MEANINGFUL_BITS_DOUBLE = MEANINGFUL_BITS_LONG.toDouble()
|
||||
private val MEANINGFUL_BITS_CONTEXT = MathContext(MEANINGFUL_BITS)
|
||||
private val MEANINGFUL_BITS_BI = BigInteger.valueOf(MEANINGFUL_BITS_LONG)
|
||||
|
||||
/**
|
||||
* Imprecise Fraction class for dealing with huge numbers that need fractional part to some extent.
|
||||
*
|
||||
* In essence, this class is pretty much like [BigDecimal], except decimal part of this number is
|
||||
* not guaranteed to be precise (stored as [Double]). The reason behind creation of this class, however,
|
||||
* is to allow infinite precision of [whole] part, while leaving [decimal] part to be rounded in inexact operations.
|
||||
*
|
||||
* This class is value based, however, [equals] and [compareTo] are not doing *exact* comparison. Whole part is always compared
|
||||
* exactly, but decimal part is considered equal if their difference is less than [EPSILON].
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Double = 0.0) : Comparable<ImpreciseFraction> {
|
||||
class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Double = 0.0) : Number(), Comparable<ImpreciseFraction> {
|
||||
@JvmOverloads constructor(whole: Byte, decimal: Double = 0.0) : this(BigInteger.valueOf(whole.toLong()), decimal)
|
||||
@JvmOverloads constructor(whole: Short, decimal: Double = 0.0) : this(BigInteger.valueOf(whole.toLong()), decimal)
|
||||
@JvmOverloads constructor(whole: Int, decimal: Double = 0.0) : this(BigInteger.valueOf(whole.toLong()), decimal)
|
||||
@ -121,7 +138,14 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
||||
constructor(value: Double) : this(BigInteger.valueOf((value - value % 1.0).toLong()), value % 1.0)
|
||||
constructor(value: String) : this(BigDecimal(value))
|
||||
|
||||
/**
|
||||
* Inexact fractional part of this fraction
|
||||
*/
|
||||
val decimal: Double
|
||||
|
||||
/**
|
||||
* Exact whole part of this fraction
|
||||
*/
|
||||
val whole: BigInteger
|
||||
|
||||
init {
|
||||
@ -136,8 +160,8 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
||||
decimal = frac
|
||||
}
|
||||
|
||||
// дробная часть равна нулю
|
||||
if (decimal == 0.0) {
|
||||
// дробная часть равна или очень близка к нулю
|
||||
if (weakEqualDoubles(decimal, 0.0)) {
|
||||
if (!isZero(whole)) {
|
||||
this.decimal = 0.0
|
||||
this.whole = whole
|
||||
@ -379,6 +403,11 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
||||
return sign
|
||||
}
|
||||
|
||||
// exactly zero
|
||||
if (decimal == 0.0)
|
||||
return if (1.0 / decimal > 0) 1 else -1
|
||||
|
||||
// inexactly zero
|
||||
if (weakEqualDoubles(decimal, 0.0))
|
||||
return 0
|
||||
|
||||
@ -458,31 +487,7 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
||||
return ZERO
|
||||
}
|
||||
|
||||
fun max(vararg others: ImpreciseFraction): ImpreciseFraction {
|
||||
var max = this
|
||||
|
||||
for (other in others) {
|
||||
if (max < other) {
|
||||
max = other
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
fun min(vararg others: ImpreciseFraction): ImpreciseFraction {
|
||||
var min = this
|
||||
|
||||
for (other in others) {
|
||||
if (min > other) {
|
||||
min = other
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
}
|
||||
|
||||
fun toInt(): Int {
|
||||
override fun toInt(): Int {
|
||||
if (whole > BI_INT_MAX) {
|
||||
return Int.MAX_VALUE
|
||||
} else if (whole < BI_INT_MIN) {
|
||||
@ -499,14 +504,36 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
||||
buff.writeBytes(longToBytesBE(decimal.toBits()))
|
||||
}
|
||||
|
||||
fun toFloat(): Float {
|
||||
override fun toFloat(): Float {
|
||||
return whole.toFloat() + decimal.toFloat()
|
||||
}
|
||||
|
||||
fun toDouble(): Double {
|
||||
override fun toDouble(): Double {
|
||||
return whole.toDouble() + decimal
|
||||
}
|
||||
|
||||
override fun toByte(): Byte {
|
||||
return toInt().toByte()
|
||||
}
|
||||
|
||||
override fun toChar(): Char {
|
||||
return toInt().toChar()
|
||||
}
|
||||
|
||||
override fun toLong(): Long {
|
||||
if (whole > BI_LONG_MAX) {
|
||||
return Long.MAX_VALUE
|
||||
} else if (whole < BI_LONG_MIN) {
|
||||
return Long.MIN_VALUE
|
||||
}
|
||||
|
||||
return whole.toLong()
|
||||
}
|
||||
|
||||
override fun toShort(): Short {
|
||||
return toInt().toShort()
|
||||
}
|
||||
|
||||
fun toFraction(): Fraction {
|
||||
if (isZero) {
|
||||
return Fraction.ZERO
|
||||
|
@ -61,7 +61,7 @@ class MatterCapacitorItem : Item {
|
||||
|
||||
override fun receiveMatterInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||
if (isCreative) return howMuch
|
||||
val new = matter().plus(howMuch.min(maxInput)).min(storage)
|
||||
val new = matter().plus(howMuch.coerceAtMost(maxInput)).coerceAtMost(storage)
|
||||
val diff = new.minus(matter())
|
||||
|
||||
if (!simulate) {
|
||||
@ -77,7 +77,7 @@ class MatterCapacitorItem : Item {
|
||||
|
||||
override fun extractMatterInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||
if (isCreative) return howMuch
|
||||
val new = matter().minus(howMuch.min(maxOutput)).moreThanZero()
|
||||
val new = matter().minus(howMuch.coerceAtMost(maxOutput)).moreThanZero()
|
||||
val diff = matter().minus(new)
|
||||
|
||||
if (!simulate) {
|
||||
|
@ -58,7 +58,7 @@ class MatterDustItem : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREAT
|
||||
if (matterThis >= MAX_MATTER_IN_ITEM)
|
||||
return ImpreciseFraction.ZERO
|
||||
|
||||
val newMatter = (matterThis + matter).min(MAX_MATTER_IN_ITEM)
|
||||
val newMatter = (matterThis + matter).coerceAtMost(MAX_MATTER_IN_ITEM)
|
||||
val diff = newMatter - matterThis
|
||||
|
||||
if (!simulate)
|
||||
|
@ -112,7 +112,7 @@ class PlasmaWeaponEnergy(val itemStack: ItemStack, private val innerCapacity: Im
|
||||
return totalReceived
|
||||
}
|
||||
|
||||
val newEnergy = (innerBatteryLevel - howMuch).min(innerCapacity)
|
||||
val newEnergy = (innerBatteryLevel - howMuch).coerceAtMost(innerCapacity)
|
||||
val diff = newEnergy - innerBatteryLevel
|
||||
|
||||
if (!simulate) {
|
||||
|
@ -165,8 +165,8 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
||||
if (!extracted.isEmpty) {
|
||||
val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false)
|
||||
|
||||
if (remaining.count != extracted.stack.count) {
|
||||
provider.extractStack(state.upstreamId, extracted.stack.count - remaining.count, false)
|
||||
if (remaining.count != extracted.count.toInt()) {
|
||||
provider.extractStack(state.upstreamId, extracted.count.toInt() - remaining.count, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,7 +180,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
||||
val amount = carried.count
|
||||
|
||||
if (amount == carried.count) {
|
||||
val (stack) = provider.insertStack(ItemStackWrapper(menu.carried), false)
|
||||
val stack = provider.insertStack(ItemStackWrapper(menu.carried), false).stack
|
||||
menu.carried = stack
|
||||
MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||
menu.setRemoteCarried(menu.carried.copy())
|
||||
@ -202,7 +202,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
||||
if (action == ClickAction.PRIMARY)
|
||||
state.stack.maxStackSize
|
||||
else
|
||||
Math.max(1, state.stack.maxStackSize / 2)
|
||||
1.coerceAtLeast(state.stack.maxStackSize / 2)
|
||||
|
||||
val extracted = provider.extractStack(state.upstreamId!!, amount, false)
|
||||
menu.carried = extracted.stack
|
||||
|
@ -7,6 +7,18 @@ import net.minecraftforge.registries.ForgeRegistry
|
||||
interface IStorageStack {
|
||||
fun copy(): IStorageStack
|
||||
|
||||
/**
|
||||
* Despite count being [ImpreciseFraction], far not all items can be "fraction"
|
||||
* into pieces (e.g. ItemStacks).
|
||||
*
|
||||
* Consult [StorageStackType.fractional] to see if item has actual fractions.
|
||||
*
|
||||
* Implementation MUST [ImpreciseFraction.floor] received value if fractions
|
||||
* are not supported, and NOT throw any errors. Writing fractional value is a correct
|
||||
* behavior, and is used in base mod.
|
||||
*
|
||||
* Behavior of writing negative value is undefined.
|
||||
*/
|
||||
var count: ImpreciseFraction
|
||||
val isEmpty: Boolean
|
||||
|
||||
@ -17,15 +29,17 @@ interface IStorageStack {
|
||||
val maxStackSize: ImpreciseFraction? get() = null
|
||||
|
||||
/**
|
||||
* In base mod, a [hashCode] but for use with AVL trees.
|
||||
*
|
||||
* This property represent (almost) unique key used to quickly look up for similar stacks.
|
||||
* Semantic is very similar, but not equal, to [hashCode] of object. This property **must** be equal for two
|
||||
* entries if [sameItem] returns true. This value should not change.
|
||||
* This property **must** be equal for two entries if [sameItem] returns true.
|
||||
* This value should not change.
|
||||
*
|
||||
* A good example of key is [ForgeRegistry.getID].
|
||||
*
|
||||
* Collisions are solved as in open hash map, with linear scan for required item.
|
||||
* Users of this property should prefer binary search trees (e.g. AVL) with this value as key.
|
||||
*/
|
||||
val partitionKey: Long
|
||||
val longHashCode: Long
|
||||
|
||||
/**
|
||||
* [other] is the stack to compare.
|
||||
|
@ -6,40 +6,71 @@ import net.minecraftforge.registries.ForgeRegistries
|
||||
import net.minecraftforge.registries.ForgeRegistry
|
||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||
|
||||
class ItemStackWrapper(val stack: ItemStack) : IStorageStack {
|
||||
operator fun component1() = stack
|
||||
/**
|
||||
* constructors always copy its input.
|
||||
*/
|
||||
class ItemStackWrapper : IStorageStack {
|
||||
/**
|
||||
* [ItemStack] representing what item is this.
|
||||
*
|
||||
* In most cases you want to use [stack] instead.
|
||||
*/
|
||||
val item: ItemStack
|
||||
val registryName get() = item.item.registryName!!
|
||||
override var count: ImpreciseFraction
|
||||
set(value) { field = value.floor() }
|
||||
|
||||
constructor(item: ItemStack) {
|
||||
this.item = item.copy()
|
||||
this.count = ImpreciseFraction(item.count)
|
||||
this.item.count = 1
|
||||
this.longHashCode = longHashCode(this.item.item)
|
||||
}
|
||||
|
||||
constructor(item: ItemStackWrapper) {
|
||||
this.item = item.item.copy()
|
||||
this.count = item.count
|
||||
this.item.count = 1
|
||||
// this.longHashCode = longHashCode(this.item.item)
|
||||
this.longHashCode = item.longHashCode
|
||||
}
|
||||
|
||||
override fun copy(): ItemStackWrapper {
|
||||
return ItemStackWrapper(stack.copy())
|
||||
return ItemStackWrapper(this)
|
||||
}
|
||||
|
||||
fun setCount(value: Int) {
|
||||
stack.count = value.coerceAtLeast(0)
|
||||
override val maxStackSize get() = ImpreciseFraction(item.maxStackSize)
|
||||
|
||||
override val isEmpty: Boolean get() = item.isEmpty || !count.isPositive
|
||||
override val longHashCode: Long
|
||||
|
||||
/**
|
||||
* [ItemStack] with valid amount and which can be modified after
|
||||
*/
|
||||
val stack: ItemStack get() = item.copy().also { it.count = count.toInt() }
|
||||
|
||||
override fun toString(): String {
|
||||
return "ItemStackWrapper[$count $registryName]"
|
||||
}
|
||||
|
||||
override var count: ImpreciseFraction
|
||||
get() = ImpreciseFraction(stack.count)
|
||||
set(value) = setCount(value.toInt())
|
||||
|
||||
override val maxStackSize get() = ImpreciseFraction(stack.maxStackSize)
|
||||
|
||||
override val isEmpty: Boolean = stack.isEmpty
|
||||
override val partitionKey: Long = partitionKey(stack.item)
|
||||
|
||||
override fun sameItem(other: IStorageStack): Boolean {
|
||||
if (this === other)
|
||||
return true
|
||||
|
||||
if (other is ItemStackWrapper)
|
||||
return ItemStack.isSameItemSameTags(stack, other.stack)
|
||||
return ItemStack.isSameItemSameTags(item, other.item)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun sameItem(other: ItemStack): Boolean {
|
||||
return ItemStack.isSameItemSameTags(item, other)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val EMPTY = ItemStackWrapper(ItemStack.EMPTY)
|
||||
|
||||
fun partitionKey(item: Item) = (ForgeRegistries.ITEMS as? ForgeRegistry<Item>)?.getID(item)?.toLong() ?: System.identityHashCode(item).toLong()
|
||||
fun longHashCode(item: Item) = (ForgeRegistries.ITEMS as? ForgeRegistry<Item>)?.getID(item)?.toLong() ?: System.identityHashCode(item).toLong()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ru.dbotthepony.mc.otm.storage
|
||||
|
||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||
import ru.dbotthepony.mc.otm.core.Fraction
|
||||
import java.util.IdentityHashMap
|
||||
|
||||
open class StorageStackType<T : IStorageStack>(
|
||||
@ -10,7 +11,22 @@ open class StorageStackType<T : IStorageStack>(
|
||||
/**
|
||||
* Speculated energy required per operation on stack with size of 1
|
||||
*/
|
||||
open val energyPerOperation: ImpreciseFraction
|
||||
open val energyPerOperation: ImpreciseFraction,
|
||||
|
||||
/**
|
||||
* Whenever is this stack supports fractional part (e.g. 0.5).
|
||||
*
|
||||
* Keep in mind fractions are imprecise and can lead to rounding errors.
|
||||
* [ImpreciseFraction] class attempts to negate most of the issues
|
||||
* (e.g. 0.1 + 0.2 eventually getting its 0....4 part into whole part),
|
||||
* but that is about it.
|
||||
*
|
||||
* On design side note, storage system could have been using [Fraction], but there is an issue:
|
||||
* they are **precise**. Under precise, means that anything that continuously divide/multiply them
|
||||
* they become more and more "irrational", greatly slowing down operations. Worst case scenario:
|
||||
* value is getting divided by [Long.MAX_VALUE] again and again, creating insanely huge divisor.
|
||||
*/
|
||||
open val fractional: Boolean
|
||||
) {
|
||||
open fun energyPerOperation(stack: T): ImpreciseFraction {
|
||||
return stack.count * energyPerOperation
|
||||
@ -32,9 +48,17 @@ object StorageRegistry {
|
||||
return type
|
||||
}
|
||||
|
||||
/**
|
||||
* Refer to [StorageStackType] for explanation of flags.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T : IStorageStack> register(identity: Class<T>, empty: T, energyPerOperation: ImpreciseFraction): StorageStackType<T> {
|
||||
return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType<T>
|
||||
fun <T : IStorageStack> register(
|
||||
identity: Class<T>,
|
||||
empty: T,
|
||||
energyPerOperation: ImpreciseFraction,
|
||||
fractional: Boolean
|
||||
): StorageStackType<T> {
|
||||
return register(StorageStackType(identity, empty, energyPerOperation, fractional)) as StorageStackType<T>
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
@ -119,7 +119,7 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : ISto
|
||||
@Suppress("unchecked_cast")
|
||||
override fun addStack(stack: T, id: UUID, provider: IStorageView<T>) {
|
||||
check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" }
|
||||
val items = partitions.computeIfAbsent(stack.partitionKey, Long2ObjectFunction { ArrayList() })
|
||||
val items = partitions.computeIfAbsent(stack.longHashCode, Long2ObjectFunction { ArrayList() })
|
||||
var local: LocalTuple<T>? = null
|
||||
var oldCount = ImpreciseFraction.ZERO
|
||||
|
||||
@ -172,7 +172,7 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : ISto
|
||||
|
||||
override fun removeStack(stack: T, id: UUID) {
|
||||
val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id")
|
||||
val key = tuple.local.stack.partitionKey
|
||||
val key = tuple.local.stack.longHashCode
|
||||
|
||||
tuple.local.stack.shrink(tuple.obj.count)
|
||||
tuple.local.tuples.remove(tuple)
|
||||
@ -219,10 +219,13 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : ISto
|
||||
if (tuple == null || amount.isZero)
|
||||
return this.storageType.empty
|
||||
|
||||
if (amount <= ImpreciseFraction.MINUS_ONE)
|
||||
if (!storageType.fractional)
|
||||
amount = amount.floor()
|
||||
|
||||
if (!amount.isPositive)
|
||||
amount = tuple.stack.maxStackSize ?: tuple.stack.count
|
||||
|
||||
val toExtract = tuple.stack.count.min(amount)
|
||||
val toExtract = tuple.stack.count.coerceAtMost(amount)
|
||||
var extracted = ImpreciseFraction.ZERO
|
||||
val copy = tuple.stack.copy() as T
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user