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) {
|
private void setup(final FMLCommonSetupEvent event) {
|
||||||
MatteryNetworking.register();
|
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")) {
|
if (ModList.get().isLoaded("mekanism")) {
|
||||||
MinecraftForge.EVENT_BUS.register(QIOKt.class);
|
MinecraftForge.EVENT_BUS.register(QIOKt.class);
|
||||||
|
@ -198,7 +198,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
val ioLimit = ioLimit
|
val ioLimit = ioLimit
|
||||||
|
|
||||||
if (ioLimit != null) {
|
if (ioLimit != null) {
|
||||||
diff = it.extractEnergy(howMuch.min(ioLimit), simulate)
|
diff = it.extractEnergy(howMuch.coerceAtMost(ioLimit), simulate)
|
||||||
} else {
|
} else {
|
||||||
diff = it.extractEnergy(howMuch, simulate)
|
diff = it.extractEnergy(howMuch, simulate)
|
||||||
}
|
}
|
||||||
@ -230,7 +230,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
|
|||||||
val ioLimit = ioLimit
|
val ioLimit = ioLimit
|
||||||
|
|
||||||
if (ioLimit != null) {
|
if (ioLimit != null) {
|
||||||
diff = it.receiveEnergy(howMuch.min(ioLimit), simulate)
|
diff = it.receiveEnergy(howMuch.coerceAtMost(ioLimit), simulate)
|
||||||
} else {
|
} else {
|
||||||
diff = it.receiveEnergy(howMuch, simulate)
|
diff = it.receiveEnergy(howMuch, simulate)
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
if (workFlow) {
|
if (workFlow) {
|
||||||
if (matter.storedMatter < MATTER_EXCHANGE_RATE && graph != null) {
|
if (matter.storedMatter < MATTER_EXCHANGE_RATE && graph != null) {
|
||||||
val extracted = graph.extractMatter(
|
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) {
|
if (extracted > ImpreciseFraction.ZERO) {
|
||||||
@ -277,7 +277,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
val energyExtracted = energy.extractEnergyInner(ENERGY_CONSUMPTION, true)
|
val energyExtracted = energy.extractEnergyInner(ENERGY_CONSUMPTION, true)
|
||||||
|
|
||||||
if (!energyExtracted.isZero) {
|
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) {
|
if (!matter.isZero) {
|
||||||
energy.extractEnergyInner(ENERGY_CONSUMPTION * matter / MATTER_EXCHANGE_RATE, false)
|
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)
|
val energyExtracted = energy.extractEnergyInner(ENERGY_CONSUMPTION, true)
|
||||||
|
|
||||||
if (!energyExtracted.isZero) {
|
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) {
|
if (!matter.isZero) {
|
||||||
this.energy.extractEnergyInner(ENERGY_CONSUMPTION * matter / MATTER_EXCHANGE_RATE,false)
|
this.energy.extractEnergyInner(ENERGY_CONSUMPTION * matter / MATTER_EXCHANGE_RATE,false)
|
||||||
|
@ -159,7 +159,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
// в тик требуется меньше материи, чем её может хранить репликатор
|
// в тик требуется меньше материи, чем её может хранить репликатор
|
||||||
// примем из сети недостающее количество бака материи, или 200 тиков репликации, что меньше
|
// примем из сети недостающее количество бака материи, или 200 тиков репликации, что меньше
|
||||||
val toExtract =
|
val toExtract =
|
||||||
matter.missingMatter.min(drainPerTick.times(DRAIN_MULT))
|
matter.missingMatter.coerceAtMost(drainPerTick.times(DRAIN_MULT))
|
||||||
|
|
||||||
val drain = graph.extractMatter(toExtract, true)
|
val drain = graph.extractMatter(toExtract, true)
|
||||||
|
|
||||||
|
@ -37,8 +37,20 @@ import java.util.*
|
|||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
private class SlotTuple(val slot: Int, val stack: ItemStack)
|
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>()
|
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> {
|
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) {
|
if (scannedMap.stack.count.isPositive) {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(scannedMap.stack, scannedMap.id, count)
|
listener.changeStack(scannedMap, count)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.removeStack(scannedMap.stack, scannedMap.id)
|
listener.removeStack(scannedMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
index.remove(scannedMap.id)
|
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) {
|
private fun addTracked(slot: Int, stack: ItemStack) {
|
||||||
check(scannedMap[slot] == null) { "Already tracking slot $slot" }
|
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
|
var tuple: TrackedTuple? = null
|
||||||
|
|
||||||
for (storedTuple in listing) {
|
for (storedTuple in listing) {
|
||||||
if (ItemStack.isSameItemSameTags(stack, storedTuple.stack.stack)) {
|
if (storedTuple.stack.sameItem(stack)) {
|
||||||
tuple = storedTuple
|
tuple = storedTuple
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -118,7 +140,7 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
|||||||
var oldCount = ImpreciseFraction.ZERO
|
var oldCount = ImpreciseFraction.ZERO
|
||||||
|
|
||||||
if (added) {
|
if (added) {
|
||||||
val storageStack = ItemStackWrapper(stack.copy())
|
val storageStack = ItemStackWrapper(stack)
|
||||||
tuple = TrackedTuple(storageStack, UUID.randomUUID())
|
tuple = TrackedTuple(storageStack, UUID.randomUUID())
|
||||||
index[tuple.id] = tuple
|
index[tuple.id] = tuple
|
||||||
listing.add(tuple)
|
listing.add(tuple)
|
||||||
@ -232,9 +254,9 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
|||||||
if (!amount.isPositive)
|
if (!amount.isPositive)
|
||||||
return ItemStackWrapper.EMPTY
|
return ItemStackWrapper.EMPTY
|
||||||
|
|
||||||
val intAmount = amount.toInt()
|
val intAmount = amount.toLong()
|
||||||
val tuple = index[id] ?: return ItemStackWrapper.EMPTY
|
val tuple = index[id] ?: return ItemStackWrapper.EMPTY
|
||||||
var totalExtracted = 0
|
var totalExtracted = 0L
|
||||||
|
|
||||||
val iter = tuple.children.values.iterator()
|
val iter = tuple.children.values.iterator()
|
||||||
val listCopy = Array(tuple.children.values.size) {
|
val listCopy = Array(tuple.children.values.size) {
|
||||||
@ -244,14 +266,14 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
|||||||
val copy = tuple.stack.copy()
|
val copy = tuple.stack.copy()
|
||||||
|
|
||||||
for (stack in listCopy) {
|
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) {
|
if (extracted.isEmpty) {
|
||||||
// dummy condition
|
// dummy condition
|
||||||
continue
|
continue
|
||||||
} else if (ItemStack.isSameItemSameTags(extracted, tuple.stack.stack)) {
|
} else if (tuple.stack.sameItem(extracted)) {
|
||||||
if (!simulate) {
|
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
|
totalExtracted += extracted.count
|
||||||
@ -269,11 +291,11 @@ private class ItemHandlerComponent(private val parent: IItemHandler) : IStorageC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalExtracted == 0) {
|
if (totalExtracted == 0L) {
|
||||||
return ItemStackWrapper.EMPTY
|
return ItemStackWrapper.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
copy.setCount(totalExtracted)
|
copy.count = ImpreciseFraction(totalExtracted)
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ sealed class ItemEnergyStorageImpl(
|
|||||||
val maxOutput = maxOutput
|
val maxOutput = maxOutput
|
||||||
|
|
||||||
if (maxOutput != null) {
|
if (maxOutput != null) {
|
||||||
howMuch = howMuch.min(maxOutput)
|
howMuch = howMuch.coerceAtMost(maxOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
val batteryLevel = batteryLevel
|
val batteryLevel = batteryLevel
|
||||||
@ -101,7 +101,7 @@ sealed class ItemEnergyStorageImpl(
|
|||||||
val maxInput = maxInput
|
val maxInput = maxInput
|
||||||
|
|
||||||
if (maxInput != null) {
|
if (maxInput != null) {
|
||||||
howMuch = howMuch.min(maxInput)
|
howMuch = howMuch.coerceAtMost(maxInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
val batteryLevel = batteryLevel
|
val batteryLevel = batteryLevel
|
||||||
@ -109,7 +109,7 @@ sealed class ItemEnergyStorageImpl(
|
|||||||
if (batteryLevel >= maxBatteryLevel)
|
if (batteryLevel >= maxBatteryLevel)
|
||||||
return ImpreciseFraction.ZERO
|
return ImpreciseFraction.ZERO
|
||||||
|
|
||||||
val newLevel = (batteryLevel + howMuch).min(maxBatteryLevel)
|
val newLevel = (batteryLevel + howMuch).coerceAtMost(maxBatteryLevel)
|
||||||
val diff = (newLevel - batteryLevel)
|
val diff = (newLevel - batteryLevel)
|
||||||
|
|
||||||
if (!simulate && batteryLevel != newLevel) {
|
if (!simulate && batteryLevel != newLevel) {
|
||||||
@ -173,7 +173,7 @@ sealed class BlockEnergyStorageImpl constructor(
|
|||||||
val maxOutput = maxOutput
|
val maxOutput = maxOutput
|
||||||
|
|
||||||
if (maxOutput != null) {
|
if (maxOutput != null) {
|
||||||
howMuch = howMuch.min(maxOutput)
|
howMuch = howMuch.coerceAtMost(maxOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!batteryLevel.isPositive)
|
if (!batteryLevel.isPositive)
|
||||||
@ -199,13 +199,13 @@ sealed class BlockEnergyStorageImpl constructor(
|
|||||||
val maxInput = maxInput
|
val maxInput = maxInput
|
||||||
|
|
||||||
if (maxInput != null) {
|
if (maxInput != null) {
|
||||||
howMuch = howMuch.min(maxInput)
|
howMuch = howMuch.coerceAtMost(maxInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batteryLevel >= maxBatteryLevel)
|
if (batteryLevel >= maxBatteryLevel)
|
||||||
return ImpreciseFraction.ZERO
|
return ImpreciseFraction.ZERO
|
||||||
|
|
||||||
val newLevel = (batteryLevel + howMuch).min(maxBatteryLevel)
|
val newLevel = (batteryLevel + howMuch).coerceAtMost(maxBatteryLevel)
|
||||||
val diff = (newLevel - batteryLevel)
|
val diff = (newLevel - batteryLevel)
|
||||||
|
|
||||||
if (!simulate && batteryLevel != newLevel) {
|
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
|
received += new - battery
|
||||||
|
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
|
@ -40,10 +40,10 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
|
|
||||||
@Suppress("unchecked_cast")
|
@Suppress("unchecked_cast")
|
||||||
override fun insertStack(stack: T, simulate: Boolean): T {
|
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
|
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) {
|
for (state in listing) {
|
||||||
if (state.stack.sameItem(stack)) {
|
if (state.stack.sameItem(stack)) {
|
||||||
@ -98,10 +98,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
var amount = amount
|
var amount = amount
|
||||||
val get = storedByID[id] ?: return storageType.empty
|
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 = get.stack.maxStackSize ?: get.stack.count
|
||||||
|
|
||||||
amount = amount.min(get.stack.count)
|
amount = amount.coerceAtMost(get.stack.count)
|
||||||
|
|
||||||
if (amount <= ImpreciseFraction.ZERO)
|
if (amount <= ImpreciseFraction.ZERO)
|
||||||
return storageType.empty
|
return storageType.empty
|
||||||
@ -110,8 +113,8 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
copy.count = amount
|
copy.count = amount
|
||||||
|
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
if (amount.compareTo(get.stack.count) == 0) {
|
if (amount == get.stack.count) {
|
||||||
val listing = storedStacks[get.stack.partitionKey]!!
|
val listing = storedStacks[get.stack.longHashCode] ?: throw IllegalStateException("Can't find storage list with partition ${get.stack.longHashCode} for ${get.stack}")
|
||||||
listing.remove(get)
|
listing.remove(get)
|
||||||
storedDifferentStacks--
|
storedDifferentStacks--
|
||||||
|
|
||||||
@ -120,7 +123,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (listing.size == 0) {
|
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
|
val oldCount = get.stack.count
|
||||||
get.stack.shrink(amount)
|
get.stack.shrink(amount)
|
||||||
|
|
||||||
if (!get.stack.count.isZero) {
|
if (get.stack.count.isPositive) {
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(get.stack, get.id, oldCount)
|
listener.changeStack(get.stack, get.id, oldCount)
|
||||||
}
|
}
|
||||||
@ -182,13 +185,13 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
|||||||
val stack = deserializeStack(entry)
|
val stack = deserializeStack(entry)
|
||||||
|
|
||||||
if (stack != null) {
|
if (stack != null) {
|
||||||
storedCount = storedCount.plus(stack.count)
|
storedCount += stack.count
|
||||||
storedDifferentStacks++
|
storedDifferentStacks++
|
||||||
|
|
||||||
val id = entry.getLongArray("id")
|
val id = entry.getLongArray("id")
|
||||||
val tuple = StorageTuple(if (id.size == 2) UUID(id[0], id[1]) else UUID.randomUUID(), stack)
|
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
|
storedByID[tuple.id] = tuple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,12 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
|||||||
|
|
||||||
override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? {
|
override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? {
|
||||||
val tag = CompoundTag()
|
val tag = CompoundTag()
|
||||||
val location = item.stack.stack.item.registryName!!.toString()
|
val location = item.stack.registryName.toString()
|
||||||
|
|
||||||
tag["item"] = location
|
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) {
|
if (itag != null) {
|
||||||
tag["data"] = itag
|
tag["data"] = itag
|
||||||
@ -56,32 +56,22 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun findItems(item: Item): List<IStorageTuple<ItemStackWrapper>> {
|
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()
|
return if (list != null) java.util.List.copyOf(list) else emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findItems(stack: ItemStack): List<IStorageTuple<ItemStackWrapper>> {
|
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) {
|
for (_stack in list) {
|
||||||
if (ItemStack.tagMatches(_stack.stack.stack, stack)) {
|
if (_stack.stack.sameItem(stack)) {
|
||||||
amount++
|
buildList.add(_stack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val build_list = ArrayList<IStorageTuple<ItemStackWrapper>>(amount)
|
return buildList
|
||||||
var i = 0
|
|
||||||
|
|
||||||
for (_stack in list) {
|
|
||||||
if (ItemStack.tagMatches(_stack.stack.stack, stack)) {
|
|
||||||
build_list[i] = _stack
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return build_list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -45,9 +45,9 @@ open class MatterHandlerImpl @JvmOverloads constructor(
|
|||||||
val new: ImpreciseFraction
|
val new: ImpreciseFraction
|
||||||
|
|
||||||
if (maxReceive == null) {
|
if (maxReceive == null) {
|
||||||
new = (storedMatter + howMuch).min(maxStoredMatter)
|
new = (storedMatter + howMuch).coerceAtMost(maxStoredMatter)
|
||||||
} else {
|
} else {
|
||||||
new = (storedMatter + howMuch.min(maxReceive)).min(maxStoredMatter)
|
new = (storedMatter + howMuch.coerceAtMost(maxReceive)).coerceAtMost(maxStoredMatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
val diff = new - storedMatter
|
val diff = new - storedMatter
|
||||||
@ -73,7 +73,7 @@ open class MatterHandlerImpl @JvmOverloads constructor(
|
|||||||
if (maxExtract == null) {
|
if (maxExtract == null) {
|
||||||
new = (storedMatter - howMuch).moreThanZero()
|
new = (storedMatter - howMuch).moreThanZero()
|
||||||
} else {
|
} else {
|
||||||
new = (storedMatter - howMuch.min(maxExtract)).moreThanZero()
|
new = (storedMatter - howMuch.coerceAtMost(maxExtract)).moreThanZero()
|
||||||
}
|
}
|
||||||
|
|
||||||
val diff = storedMatter - new
|
val diff = storedMatter - new
|
||||||
|
@ -6,7 +6,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
|||||||
import mekanism.api.math.MathUtils
|
import mekanism.api.math.MathUtils
|
||||||
import mekanism.common.content.qio.QIOFrequency
|
import mekanism.common.content.qio.QIOFrequency
|
||||||
import mekanism.common.content.qio.QIOFrequency.QIOItemTypeData
|
import mekanism.common.content.qio.QIOFrequency.QIOItemTypeData
|
||||||
import mekanism.common.content.transporter.TransporterManager
|
|
||||||
import mekanism.common.lib.frequency.Frequency
|
import mekanism.common.lib.frequency.Frequency
|
||||||
import mekanism.common.lib.inventory.HashedItem
|
import mekanism.common.lib.inventory.HashedItem
|
||||||
import mekanism.common.tile.qio.TileEntityQIODriveArray
|
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.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
|
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
|
||||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
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.BasicStorageGraphNode
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
|
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
|
||||||
import ru.dbotthepony.mc.otm.storage.*
|
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 {
|
override fun insertStack(stack: ItemStackWrapper, simulate: Boolean): ItemStackWrapper {
|
||||||
// Because there is no simulate method on QIO array, we have to simulate it by ourselves.
|
// 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) {
|
if (!simulate) {
|
||||||
//val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack))
|
//val used = TransporterManager.getToUse(stack.stack, parent.addItem(stack.stack))
|
||||||
val used = parent.addItem(stack.stack)
|
val used = parent.addItem(stack.stack)
|
||||||
|
|
||||||
if (used.count == stack.stack.count) {
|
if (used.count == stack.count.toInt()) {
|
||||||
return stack
|
return stack
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
}
|
}
|
||||||
|
|
||||||
val inserted = stack.copy()
|
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
|
return inserted
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
local = index[id] ?: return ItemStackWrapper.EMPTY // unexpected...
|
local = index[id] ?: return ItemStackWrapper.EMPTY // unexpected...
|
||||||
|
|
||||||
if (simulate) {
|
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())
|
val removed = parent.removeByType(local.mekanismItem, amount.toInt())
|
||||||
@ -106,7 +106,7 @@ private class QIOFrequencyAccess(val parent: QIOFrequency) : IStorageComponent<I
|
|||||||
return ItemStackWrapper.EMPTY
|
return ItemStackWrapper.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
val copy = ItemStackWrapper(removed.copy())
|
val copy = ItemStackWrapper(removed)
|
||||||
|
|
||||||
if (local.stack.count > copy.count) {
|
if (local.stack.count > copy.count) {
|
||||||
// expecting stack to be still present in QIO storage grid
|
// 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) {
|
if (local != null) {
|
||||||
local.mark = mark
|
local.mark = mark
|
||||||
|
|
||||||
if (local.stack.stack.count != MathUtils.clampToInt(value.count)) {
|
if (local.stack.count.toLong() != value.count) {
|
||||||
val oldCount = local.stack.count
|
val oldCount = local.stack.count
|
||||||
local.stack.stack.count = MathUtils.clampToInt(value.count)
|
local.stack.count = value.count.toImpreciseFraction()
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
listener.changeStack(local, oldCount)
|
listener.changeStack(local, oldCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
index[tuple.id] = tuple
|
||||||
|
|
||||||
for (listener in listeners) {
|
for (listener in listeners) {
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package ru.dbotthepony.mc.otm.core
|
package ru.dbotthepony.mc.otm.core
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.core.Vec3i
|
import net.minecraft.core.Vec3i
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
operator fun BlockPos.plus(direction: Vec3i): BlockPos = this.offset(direction)
|
operator fun BlockPos.plus(direction: Vec3i): BlockPos = this.offset(direction)
|
||||||
operator fun BlockPos.plus(direction: Direction): BlockPos = this.offset(direction.normal)
|
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 {
|
fun BlockPos.asVector(): Vector {
|
||||||
return Vector(x + 0.5, y + 0.5, z + 0.5)
|
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.MathContext
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
|
||||||
private val IMPRECISE_CONTEXT = MathContext(17)
|
|
||||||
|
|
||||||
private fun powScale(int: Int): BigInteger {
|
private fun powScale(int: Int): BigInteger {
|
||||||
if (int <= 0)
|
if (int <= 0)
|
||||||
return BigInteger.ONE
|
return BigInteger.ONE
|
||||||
@ -118,6 +116,7 @@ private val GCDs = arrayOf(
|
|||||||
|
|
||||||
private val COMPACT_THRESHOLD = BigInteger("1000000000000000000000")
|
private val COMPACT_THRESHOLD = BigInteger("1000000000000000000000")
|
||||||
|
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
private fun compactTwo(value1: BigInteger, value2: BigInteger, compact: Boolean = true): Fraction {
|
private fun compactTwo(value1: BigInteger, value2: BigInteger, compact: Boolean = true): Fraction {
|
||||||
when (value1.signum()) {
|
when (value1.signum()) {
|
||||||
0 -> return Fraction(value1, value2, compact = compact)
|
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)
|
private data class MagnitudeCrunchResult(val value: BigInteger, val mag: Int)
|
||||||
|
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
private fun magnitude(value: BigInteger): MagnitudeCrunchResult {
|
private fun magnitude(value: BigInteger): MagnitudeCrunchResult {
|
||||||
if (isZero(value))
|
if (isZero(value))
|
||||||
return MagnitudeCrunchResult(value, 0)
|
return MagnitudeCrunchResult(value, 0)
|
||||||
@ -238,9 +238,12 @@ private fun magnitude(value: BigInteger): MagnitudeCrunchResult {
|
|||||||
return MagnitudeCrunchResult(value, mag)
|
return MagnitudeCrunchResult(value, mag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmRecord
|
|
||||||
@Suppress("unused")
|
@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: 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: Int, compact: Boolean = true) : this(BigInteger.valueOf(value.toLong()), compact = compact)
|
||||||
@JvmOverloads constructor(value: Float, compact: Boolean = true) : this(BigDecimal(value.toString()), 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
|
if (isNaN()) return Float.NaN
|
||||||
return (value / divisor).toFloat() + ((value % divisor).toFloat() / divisor.toFloat())
|
return (value / divisor).toFloat() + ((value % divisor).toFloat() / divisor.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toDouble(): Double {
|
override fun toDouble(): Double {
|
||||||
if (isNaN()) return Double.NaN
|
if (isNaN()) return Double.NaN
|
||||||
return (value / divisor).toDouble() + ((value % divisor).toDouble() / divisor.toDouble())
|
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 a = divisor.toDouble()
|
||||||
val b = div[1].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])
|
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")
|
if (isNaN()) throw ArithmeticException("Fraction is not a number")
|
||||||
return (value / divisor).toInt()
|
return (value / divisor).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toLong(): Long {
|
override fun toLong(): Long {
|
||||||
if (isNaN()) throw ArithmeticException("Fraction is not a number")
|
if (isNaN()) throw ArithmeticException("Fraction is not a number")
|
||||||
return (value / divisor).toLong()
|
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
|
@JvmOverloads
|
||||||
fun toBigDecimal(context: MathContext = DEFAULT_MATH_CONTEXT): BigDecimal {
|
fun toBigDecimal(context: MathContext = DEFAULT_MATH_CONTEXT): BigDecimal {
|
||||||
if (isNaN()) throw ArithmeticException("Fraction is not a number")
|
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
|
const val EPSILON = 0.000000000001
|
||||||
|
|
||||||
fun weakEqualDoubles(a: Double, b: Double): Boolean {
|
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_CONTEXT = MathContext(MEANINGFUL_BITS)
|
||||||
private val MEANINGFUL_BITS_BI = BigInteger.valueOf(MEANINGFUL_BITS_LONG)
|
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")
|
@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: 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: 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)
|
@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: Double) : this(BigInteger.valueOf((value - value % 1.0).toLong()), value % 1.0)
|
||||||
constructor(value: String) : this(BigDecimal(value))
|
constructor(value: String) : this(BigDecimal(value))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inexact fractional part of this fraction
|
||||||
|
*/
|
||||||
val decimal: Double
|
val decimal: Double
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exact whole part of this fraction
|
||||||
|
*/
|
||||||
val whole: BigInteger
|
val whole: BigInteger
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -136,8 +160,8 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
|||||||
decimal = frac
|
decimal = frac
|
||||||
}
|
}
|
||||||
|
|
||||||
// дробная часть равна нулю
|
// дробная часть равна или очень близка к нулю
|
||||||
if (decimal == 0.0) {
|
if (weakEqualDoubles(decimal, 0.0)) {
|
||||||
if (!isZero(whole)) {
|
if (!isZero(whole)) {
|
||||||
this.decimal = 0.0
|
this.decimal = 0.0
|
||||||
this.whole = whole
|
this.whole = whole
|
||||||
@ -379,6 +403,11 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
|||||||
return sign
|
return sign
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// exactly zero
|
||||||
|
if (decimal == 0.0)
|
||||||
|
return if (1.0 / decimal > 0) 1 else -1
|
||||||
|
|
||||||
|
// inexactly zero
|
||||||
if (weakEqualDoubles(decimal, 0.0))
|
if (weakEqualDoubles(decimal, 0.0))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -458,31 +487,7 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
|||||||
return ZERO
|
return ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fun max(vararg others: ImpreciseFraction): ImpreciseFraction {
|
override fun toInt(): Int {
|
||||||
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 {
|
|
||||||
if (whole > BI_INT_MAX) {
|
if (whole > BI_INT_MAX) {
|
||||||
return Int.MAX_VALUE
|
return Int.MAX_VALUE
|
||||||
} else if (whole < BI_INT_MIN) {
|
} else if (whole < BI_INT_MIN) {
|
||||||
@ -499,14 +504,36 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
|
|||||||
buff.writeBytes(longToBytesBE(decimal.toBits()))
|
buff.writeBytes(longToBytesBE(decimal.toBits()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toFloat(): Float {
|
override fun toFloat(): Float {
|
||||||
return whole.toFloat() + decimal.toFloat()
|
return whole.toFloat() + decimal.toFloat()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toDouble(): Double {
|
override fun toDouble(): Double {
|
||||||
return whole.toDouble() + decimal
|
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 {
|
fun toFraction(): Fraction {
|
||||||
if (isZero) {
|
if (isZero) {
|
||||||
return Fraction.ZERO
|
return Fraction.ZERO
|
||||||
|
@ -61,7 +61,7 @@ class MatterCapacitorItem : Item {
|
|||||||
|
|
||||||
override fun receiveMatterInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
override fun receiveMatterInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||||
if (isCreative) return howMuch
|
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())
|
val diff = new.minus(matter())
|
||||||
|
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
@ -77,7 +77,7 @@ class MatterCapacitorItem : Item {
|
|||||||
|
|
||||||
override fun extractMatterInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
override fun extractMatterInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||||
if (isCreative) return howMuch
|
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)
|
val diff = matter().minus(new)
|
||||||
|
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
|
@ -58,7 +58,7 @@ class MatterDustItem : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREAT
|
|||||||
if (matterThis >= MAX_MATTER_IN_ITEM)
|
if (matterThis >= MAX_MATTER_IN_ITEM)
|
||||||
return ImpreciseFraction.ZERO
|
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
|
val diff = newMatter - matterThis
|
||||||
|
|
||||||
if (!simulate)
|
if (!simulate)
|
||||||
|
@ -112,7 +112,7 @@ class PlasmaWeaponEnergy(val itemStack: ItemStack, private val innerCapacity: Im
|
|||||||
return totalReceived
|
return totalReceived
|
||||||
}
|
}
|
||||||
|
|
||||||
val newEnergy = (innerBatteryLevel - howMuch).min(innerCapacity)
|
val newEnergy = (innerBatteryLevel - howMuch).coerceAtMost(innerCapacity)
|
||||||
val diff = newEnergy - innerBatteryLevel
|
val diff = newEnergy - innerBatteryLevel
|
||||||
|
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
|
@ -165,8 +165,8 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
if (!extracted.isEmpty) {
|
if (!extracted.isEmpty) {
|
||||||
val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false)
|
val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false)
|
||||||
|
|
||||||
if (remaining.count != extracted.stack.count) {
|
if (remaining.count != extracted.count.toInt()) {
|
||||||
provider.extractStack(state.upstreamId, extracted.stack.count - remaining.count, false)
|
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
|
val amount = carried.count
|
||||||
|
|
||||||
if (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
|
menu.carried = stack
|
||||||
MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||||
menu.setRemoteCarried(menu.carried.copy())
|
menu.setRemoteCarried(menu.carried.copy())
|
||||||
@ -202,7 +202,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
if (action == ClickAction.PRIMARY)
|
if (action == ClickAction.PRIMARY)
|
||||||
state.stack.maxStackSize
|
state.stack.maxStackSize
|
||||||
else
|
else
|
||||||
Math.max(1, state.stack.maxStackSize / 2)
|
1.coerceAtLeast(state.stack.maxStackSize / 2)
|
||||||
|
|
||||||
val extracted = provider.extractStack(state.upstreamId!!, amount, false)
|
val extracted = provider.extractStack(state.upstreamId!!, amount, false)
|
||||||
menu.carried = extracted.stack
|
menu.carried = extracted.stack
|
||||||
|
@ -7,6 +7,18 @@ import net.minecraftforge.registries.ForgeRegistry
|
|||||||
interface IStorageStack {
|
interface IStorageStack {
|
||||||
fun copy(): 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
|
var count: ImpreciseFraction
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
|
|
||||||
@ -17,15 +29,17 @@ interface IStorageStack {
|
|||||||
val maxStackSize: ImpreciseFraction? get() = null
|
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.
|
* 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
|
* This property **must** be equal for two entries if [sameItem] returns true.
|
||||||
* entries if [sameItem] returns true. This value should not change.
|
* This value should not change.
|
||||||
*
|
*
|
||||||
* A good example of key is [ForgeRegistry.getID].
|
* 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.
|
* [other] is the stack to compare.
|
||||||
|
@ -6,40 +6,71 @@ import net.minecraftforge.registries.ForgeRegistries
|
|||||||
import net.minecraftforge.registries.ForgeRegistry
|
import net.minecraftforge.registries.ForgeRegistry
|
||||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
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 {
|
override fun copy(): ItemStackWrapper {
|
||||||
return ItemStackWrapper(stack.copy())
|
return ItemStackWrapper(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCount(value: Int) {
|
override val maxStackSize get() = ImpreciseFraction(item.maxStackSize)
|
||||||
stack.count = value.coerceAtLeast(0)
|
|
||||||
|
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 {
|
override fun sameItem(other: IStorageStack): Boolean {
|
||||||
if (this === other)
|
if (this === other)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
if (other is ItemStackWrapper)
|
if (other is ItemStackWrapper)
|
||||||
return ItemStack.isSameItemSameTags(stack, other.stack)
|
return ItemStack.isSameItemSameTags(item, other.item)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sameItem(other: ItemStack): Boolean {
|
||||||
|
return ItemStack.isSameItemSameTags(item, other)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
@JvmField
|
||||||
val EMPTY = ItemStackWrapper(ItemStack.EMPTY)
|
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
|
package ru.dbotthepony.mc.otm.storage
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||||
|
import ru.dbotthepony.mc.otm.core.Fraction
|
||||||
import java.util.IdentityHashMap
|
import java.util.IdentityHashMap
|
||||||
|
|
||||||
open class StorageStackType<T : IStorageStack>(
|
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
|
* 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 {
|
open fun energyPerOperation(stack: T): ImpreciseFraction {
|
||||||
return stack.count * energyPerOperation
|
return stack.count * energyPerOperation
|
||||||
@ -32,9 +48,17 @@ object StorageRegistry {
|
|||||||
return type
|
return type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refer to [StorageStackType] for explanation of flags.
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <T : IStorageStack> register(identity: Class<T>, empty: T, energyPerOperation: ImpreciseFraction): StorageStackType<T> {
|
fun <T : IStorageStack> register(
|
||||||
return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType<T>
|
identity: Class<T>,
|
||||||
|
empty: T,
|
||||||
|
energyPerOperation: ImpreciseFraction,
|
||||||
|
fractional: Boolean
|
||||||
|
): StorageStackType<T> {
|
||||||
|
return register(StorageStackType(identity, empty, energyPerOperation, fractional)) as StorageStackType<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -119,7 +119,7 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : ISto
|
|||||||
@Suppress("unchecked_cast")
|
@Suppress("unchecked_cast")
|
||||||
override fun addStack(stack: T, id: UUID, provider: IStorageView<T>) {
|
override fun addStack(stack: T, id: UUID, provider: IStorageView<T>) {
|
||||||
check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" }
|
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 local: LocalTuple<T>? = null
|
||||||
var oldCount = ImpreciseFraction.ZERO
|
var oldCount = ImpreciseFraction.ZERO
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : ISto
|
|||||||
|
|
||||||
override fun removeStack(stack: T, id: UUID) {
|
override fun removeStack(stack: T, id: UUID) {
|
||||||
val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id")
|
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.stack.shrink(tuple.obj.count)
|
||||||
tuple.local.tuples.remove(tuple)
|
tuple.local.tuples.remove(tuple)
|
||||||
@ -219,10 +219,13 @@ open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : ISto
|
|||||||
if (tuple == null || amount.isZero)
|
if (tuple == null || amount.isZero)
|
||||||
return this.storageType.empty
|
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
|
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
|
var extracted = ImpreciseFraction.ZERO
|
||||||
val copy = tuple.stack.copy() as T
|
val copy = tuple.stack.copy() as T
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user