Network storage items as ItemStackWrappers

so they can have counts of more than 2bil
Add some formatting for biginteger
This commit is contained in:
DBotThePony 2022-06-30 14:06:38 +07:00
parent 11f416c601
commit 5833448584
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 205 additions and 84 deletions

View File

@ -416,6 +416,8 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("item_monitor.amount.one", "Exactly one item")
gui("item_monitor.amount.stack", "Exactly one stack. This is the behavior you see in AE2 and Refined Storage")
gui("item_monitor.amount.full", "Stack of ingredients. Craft until reaching stack size of one of ingredients. If at least one of inputs is not stackable then 'one stack' mode is used instead")
gui("stored_amount", "Exact amount stored: %s")
}
}

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.client.screen
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.player.Inventory
@ -50,27 +51,41 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
val grid = GridPanel(this, frame, 28f, 16f, GRID_WIDTH * 18f, GRID_HEIGHT * 18f, GRID_WIDTH, GRID_HEIGHT)
val scroll_bar = ScrollBarPanel(this, frame, 192f, 14f, 92f)
scroll_bar.setupRowMultiplier { menu.view.itemCount / GRID_WIDTH }
val scrollBar = ScrollBarPanel(this, frame, 192f, 14f, 92f)
scrollBar.setupRowMultiplier { menu.view.itemCount / GRID_WIDTH }
views.add(grid)
views.add(scroll_bar)
views.add(scrollBar)
for (i in 0 until GRID_WIDTH * GRID_HEIGHT) {
object : AbstractSlotPanel(this@DriveViewerScreen, grid, 0f, 0f) {
override fun getItemStack(): ItemStack {
val index = i + scroll_bar.getScroll(menu.view.sortedView.size / GRID_WIDTH)
return menu.view.sortedView.getOrNull(index)?.stack ?: ItemStack.EMPTY
val index = i + scrollBar.getScroll(menu.view.sortedView.size / GRID_WIDTH)
return menu.view.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY
}
override fun mouseScrolledInner(mouse_x: Double, mouse_y: Double, scroll: Double): Boolean {
return scroll_bar.mouseScrolledInner(mouse_x, mouse_y, scroll)
return scrollBar.mouseScrolledInner(mouse_x, mouse_y, scroll)
}
override fun mouseClickedInner(mouse_x: Double, mouse_y: Double, mouse_click_type: Int): Boolean {
val index = i + scroll_bar.getScroll(GRID_WIDTH)
val index = i + scrollBar.getScroll(GRID_WIDTH)
menu.view.mouseClick(index, mouse_click_type)
return true
}
override fun innerRender(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float) {
renderSlotBackground(stack, mouse_x, mouse_y, flag)
renderRegular(stack, getItemStack(), "")
}
override fun getItemStackTooltip(stack: ItemStack): List<Component> {
return super.getItemStackTooltip(stack).also {
it as MutableList<Component>
val index = i + scrollBar.getScroll(menu.view.sortedView.size / GRID_WIDTH)
val realStack = menu.view.sortedView.getOrNull(index)!!.stack
it.add(TranslatableComponent("otm.gui.item_amount", realStack.count.toString()))
}
}
}
}

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.client.screen
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.player.Inventory
@ -11,17 +12,10 @@ import ru.dbotthepony.mc.otm.client.render.SkinGrid
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.core.equalDownDivision
import ru.dbotthepony.mc.otm.core.formatReadableNumber
import ru.dbotthepony.mc.otm.menu.ItemMonitorMenu
import ru.dbotthepony.mc.otm.next
import ru.dbotthepony.mc.otm.prev
private fun div(a: Int, b: Int): Int {
if (a % b == 0) {
return a / b - 1
}
return a / b
}
import java.text.NumberFormat
class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Component) :
MatteryScreen<ItemMonitorMenu>(menu, inventory, title) {
@ -42,7 +36,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
frame.width = 178f + frame.dockPadding.left + frame.dockPadding.right
val viewScrollBar = ScrollBarPanel(this, topPanel, 28f + ITEM_GRID_WIDTH * 18f + 2f, 16f, ITEM_GRID_HEIGHT * 18f)
viewScrollBar.setupRowMultiplier { div(menu.view.itemCount, ITEM_GRID_WIDTH) }
viewScrollBar.setupRowMultiplier { equalDownDivision(menu.view.itemCount, ITEM_GRID_WIDTH) }
viewScrollBar.dock = Dock.RIGHT
viewScrollBar.setDockMargin(left = 2f)
@ -53,8 +47,8 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
for (i in 0 until ITEM_GRID_WIDTH * ITEM_GRID_HEIGHT) {
object : AbstractSlotPanel(this@ItemMonitorScreen, gridPanel) {
override fun getItemStack(): ItemStack {
val index = i + viewScrollBar.getScroll(div(menu.view.itemCount, ITEM_GRID_WIDTH)) * ITEM_GRID_WIDTH
return menu.view.sortedView.getOrNull(index)?.stack ?: ItemStack.EMPTY
val index = i + viewScrollBar.getScroll(equalDownDivision(menu.view.itemCount, ITEM_GRID_WIDTH)) * ITEM_GRID_WIDTH
return menu.view.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY
}
override fun mouseScrolledInner(mouse_x: Double, mouse_y: Double, scroll: Double): Boolean {
@ -62,10 +56,24 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
}
override fun mouseClickedInner(mouse_x: Double, mouse_y: Double, mouse_click_type: Int): Boolean {
val index = i + viewScrollBar.getScroll(div(menu.view.itemCount, ITEM_GRID_WIDTH)) * ITEM_GRID_WIDTH
val index = i + viewScrollBar.getScroll(equalDownDivision(menu.view.itemCount, ITEM_GRID_WIDTH)) * ITEM_GRID_WIDTH
menu.view.mouseClick(index, mouse_click_type)
return true
}
override fun innerRender(stack: PoseStack, mouse_x: Float, mouse_y: Float, flag: Float) {
renderSlotBackground(stack, mouse_x, mouse_y, flag)
renderRegular(stack, getItemStack(), "")
}
override fun getItemStackTooltip(stack: ItemStack): List<Component> {
return super.getItemStackTooltip(stack).also {
it as MutableList<Component>
val index = i + viewScrollBar.getScroll(menu.view.sortedView.size / DriveViewerScreen.GRID_WIDTH)
val realStack = menu.view.sortedView.getOrNull(index)!!.stack
it.add(TranslatableComponent("otm.gui.stored_amount", realStack.count.formatReadableNumber()).withStyle(ChatFormatting.DARK_GRAY))
}
}
}
}

View File

@ -8,6 +8,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import net.minecraftforge.client.RenderProperties
import org.lwjgl.opengl.GL11
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.RGBAColor
import ru.dbotthepony.mc.otm.client.render.RenderHelper
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
@ -47,7 +48,7 @@ abstract class AbstractSlotPanel @JvmOverloads constructor(
screen.itemRenderer.blitOffset = accumulatedDepth - 100 // force item to draw only 50 units "above" background
screen.itemRenderer.renderAndDecorateItem(
Minecraft.getInstance().player,
requireNotNull(minecraft.player) { "yo, dude, what the fuck" },
itemstack,
0,
0,

View File

@ -6,7 +6,10 @@ 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
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf
import java.math.BigInteger
operator fun BlockPos.plus(direction: Vec3i): BlockPos = this.offset(direction)
operator fun BlockPos.plus(direction: Direction): BlockPos = this.offset(direction.normal)
@ -217,3 +220,20 @@ fun Byte.toImpreciseFraction() = ImpreciseFraction(this)
fun Short.toImpreciseFraction() = ImpreciseFraction(this)
fun Long.toImpreciseFraction() = ImpreciseFraction(this)
fun ImpreciseFraction.toImpreciseFraction() = this
fun BigInteger(tag: Tag?): BigInteger {
if (tag !is ByteArrayTag)
return BigInteger.ZERO
return BigInteger(tag.asByteArray)
}
fun BigInteger.serializeNBT(): ByteArrayTag {
return ByteArrayTag(toByteArray())
}
fun FriendlyByteBuf.writeBigInteger(value: BigInteger) {
writeByteArray(value.toByteArray())
}
fun FriendlyByteBuf.readBigInteger(byteLimit: Int = 128) = BigInteger(readByteArray(byteLimit))

View File

@ -0,0 +1,42 @@
package ru.dbotthepony.mc.otm.core
import java.math.BigInteger
fun BigInteger.formatReadableNumber(): String {
if (isZero) {
return "0"
}
val strValue = toString()
val absLength = strValue.length - (if (isNegative) 1 else 0)
val remainder = absLength % 3
var groups = absLength / 3
if (remainder == 0) {
groups--
}
val buffer = CharArray((if (remainder == 0) 3 else remainder) + groups * 4 + if (isNegative) 1 else 0)
var c = 0
var index = buffer.size - 1
for (i in strValue.length - 1 downTo (if (isNegative) 1 else 0)) {
c++
if (c == 4) {
buffer[index] = ' '
index--
c = 1
}
buffer[index] = strValue[i]
index--
}
if (isNegative) {
buffer[index] = '-'
}
return String(buffer)
}

View File

@ -1,20 +1,16 @@
package ru.dbotthepony.mc.otm.core
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.Tag
import java.math.BigInteger
inline val BigInteger.isZero get() = this == BigInteger.ZERO
inline val BigInteger.isPositive get() = this > BigInteger.ZERO
inline val BigInteger.isNegative get() = this < BigInteger.ZERO
fun BigInteger.serializeNBT(): ByteArrayTag {
return ByteArrayTag(toByteArray())
@Suppress("SameParameterValue")
fun equalDownDivision(a: Int, b: Int): Int {
if (a % b == 0) {
return a / b - 1
}
fun BigInteger(tag: Tag?): BigInteger {
if (tag !is ByteArrayTag)
return BigInteger.ZERO
return BigInteger(tag.asByteArray)
return a / b
}

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.menu.data
import com.mojang.blaze3d.platform.InputConstants
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.FriendlyByteBuf
@ -10,11 +9,11 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.ClickAction
import net.minecraft.world.inventory.ClickType
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.readBigInteger
import ru.dbotthepony.mc.otm.core.writeBigInteger
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.network.MatteryNetworking
import ru.dbotthepony.mc.otm.network.SetCarriedPacket
@ -23,15 +22,11 @@ import java.math.BigInteger
import java.util.*
import java.util.function.Supplier
class StackAddPacket(val id: Int, val stack_id: Int, val stack: ItemStack) {
class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStackWrapper) {
fun write(buffer: FriendlyByteBuf) {
buffer.writeInt(containerId)
buffer.writeInt(id)
buffer.writeInt(stack_id)
buffer.writeRegistryId(stack.item)
buffer.writeInt(stack.count)
buffer.writeBoolean(stack.shareTag != null)
if (stack.tag != null) buffer.writeNbt(stack.shareTag)
buffer.writeBigItem(stack)
}
fun play(context: Supplier<NetworkEvent.Context>) {
@ -39,17 +34,17 @@ class StackAddPacket(val id: Int, val stack_id: Int, val stack: ItemStack) {
context.get().enqueueWork {
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
if (get.containerId != id)
if (get.containerId != containerId)
return@enqueueWork
val view = (get as? INetworkedItemViewSupplier)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
val view = (get as? INetworkedItemViewSupplier)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $containerId")
if (view.localState.containsKey(stack_id)) {
throw IllegalStateException("Item tracker $id already has stack with id of $stack_id")
if (view.localState.containsKey(id)) {
throw IllegalStateException("Item tracker $containerId already has stack with id of $id")
}
val state = NetworkedItemView.NetworkedItem(stack_id, stack)
view.localState[stack_id] = state
val state = NetworkedItemView.NetworkedItem(id, stack)
view.localState[id] = state
/*val iterator = view.sortedView.iterator().withIndex()
var lastCompare = 0
@ -77,22 +72,19 @@ class StackAddPacket(val id: Int, val stack_id: Int, val stack: ItemStack) {
companion object {
fun read(buffer: FriendlyByteBuf): StackAddPacket {
val containerId = buffer.readInt()
val id = buffer.readInt()
val stack = buffer.readInt()
val item = buffer.readRegistryIdSafe(Item::class.java)
val count = buffer.readInt()
val state = ItemStack(item, count)
if (buffer.readBoolean()) state.readShareTag(buffer.readNbt())
return StackAddPacket(id, stack, state)
val item = buffer.readBigItem()
return StackAddPacket(containerId, id, item)
}
}
}
class StackChangePacket(val id: Int, val stackID: Int, val newCount: Int) {
class StackChangePacket(val id: Int, val stackID: Int, val newCount: BigInteger) {
fun write(buffer: FriendlyByteBuf) {
buffer.writeInt(id)
buffer.writeInt(stackID)
buffer.writeInt(newCount)
buffer.writeBigInteger(newCount)
}
fun play(context: Supplier<NetworkEvent.Context>) {
@ -115,7 +107,7 @@ class StackChangePacket(val id: Int, val stackID: Int, val newCount: Int) {
fun read(buffer: FriendlyByteBuf): StackChangePacket {
val id = buffer.readInt()
val stackID = buffer.readInt()
val newCount = buffer.readInt()
val newCount = buffer.readBigInteger()
return StackChangePacket(id, stackID, newCount)
}
}
@ -155,7 +147,7 @@ class StackRemovePacket(val id: Int, val stackID: Int) {
* Creates a virtual, slotless container for Player to interaction with.
*/
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStackWrapper> {
data class NetworkedItem constructor(val id: Int, val stack: ItemStack, val upstreamId: UUID? = null)
data class NetworkedItem constructor(val id: Int, val stack: ItemStackWrapper, val upstreamId: UUID? = null)
override val storageType: StorageStackType<ItemStackWrapper>
get() = ITEM_STORAGE
@ -188,7 +180,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
fun resort() {
sortedView.sortWith { a, b ->
return@sortWith sorter.compare(a.stack, b.stack)
return@sortWith sorter.compare(a.stack.item, b.stack.item)
}
}
@ -230,8 +222,21 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
val itemCount get() = localState.values.size
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) = addObject(stack.stack, id)
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) = changeObject(id, stack.count.toInt())
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageProvider<ItemStackWrapper>) {
check(!upstreamState.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" }
val state = NetworkedItem(nextItemID++, stack.copy(), id)
this.localState[state.id] = state
upstreamState[id] = state
network { StackAddPacket(menu.containerId, state.id, state.stack) }
}
override fun changeStack(stack: ItemStackWrapper, id: UUID, oldCount: BigInteger) {
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
get.stack.count = stack.count
network { StackChangePacket(menu.containerId, get.id, stack.count) }
}
protected fun network(fn: () -> Any) {
if (!remote) {
@ -248,22 +253,6 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
protected var nextItemID = 0
fun addObject(stack: ItemStack, id_upstream: UUID) {
check(!upstreamState.containsKey(id_upstream)) { "Already tracking ItemStack with upstream id $id_upstream!" }
val state = NetworkedItem(nextItemID++, stack.copy(), id_upstream)
this.localState[state.id] = state
upstreamState[id_upstream] = state
network { StackAddPacket(menu.containerId, state.id, stack) }
}
fun changeObject(id_upstream: UUID, new_count: Int) {
val get = upstreamState[id_upstream] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id_upstream!")
get.stack.count = new_count
network { StackChangePacket(menu.containerId, get.id, new_count) }
}
fun clear() {
sortedView.clear()
upstreamState.clear()
@ -293,9 +282,8 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
if (stack_id < 0 || !ply.abilities.instabuild) return
val state = get(stack_id) ?: return
val copy: ItemStack = state.stack.copy()
val copy = state.stack.stack.also { it.count = it.maxStackSize }
copy.count = Math.min(copy.count, copy.maxStackSize)
ply.containerMenu.carried = copy
MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(ply.containerMenu.carried))
ply.containerMenu.setRemoteCarried(ply.containerMenu.carried.copy())
@ -308,9 +296,9 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
val amount =
if (action == ClickAction.PRIMARY)
state.stack.maxStackSize
state.stack.item.maxStackSize
else
1.coerceAtLeast(state.stack.maxStackSize / 2)
1.coerceAtLeast(state.stack.item.maxStackSize / 2)
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), true)
@ -352,9 +340,9 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
val amount =
if (action == ClickAction.PRIMARY)
state.stack.maxStackSize
state.stack.item.maxStackSize
else
(state.stack.count / 2).coerceAtMost(state.stack.maxStackSize / 2).coerceAtLeast(1)
(state.stack.stack.count / 2).coerceAtMost(state.stack.item.maxStackSize / 2).coerceAtLeast(1)
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false)
menu.carried = extracted.stack

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.storage
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraftforge.registries.ForgeRegistries
@ -7,6 +8,8 @@ import net.minecraftforge.registries.ForgeRegistry
import org.jetbrains.annotations.ApiStatus
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.isPositive
import ru.dbotthepony.mc.otm.core.readBigInteger
import ru.dbotthepony.mc.otm.core.writeBigInteger
import java.math.BigInteger
/**
@ -86,8 +89,22 @@ class ItemStackWrapper : IStorageStack {
return "ItemStackWrapper[$count $registryName]"
}
fun write(buff: FriendlyByteBuf) {
buff.writeItem(item)
buff.writeBigInteger(count)
}
companion object {
@JvmField
val EMPTY = ItemStackWrapper(ItemStack.EMPTY)
fun read(buff: FriendlyByteBuf): ItemStackWrapper {
val item = buff.readItem()
val count = buff.readBigInteger()
return ItemStackWrapper(item, copy = false).also { it.count = count }
}
}
}
fun FriendlyByteBuf.writeBigItem(value: ItemStackWrapper) = value.write(this)
fun FriendlyByteBuf.readBigItem() = ItemStackWrapper.read(this)

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.mc.otm.tests
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import ru.dbotthepony.mc.otm.core.formatReadableNumber
import java.math.BigInteger
object FormattingTests {
@Test
@DisplayName("BigInteger formatting")
fun biginteger() {
assertEquals("0", BigInteger("0").formatReadableNumber())
assertEquals("45", BigInteger("45").formatReadableNumber())
assertEquals("-45", BigInteger("-45").formatReadableNumber())
assertEquals("0", BigInteger("-0").formatReadableNumber())
assertEquals("100", BigInteger("100").formatReadableNumber())
assertEquals("-100", BigInteger("-100").formatReadableNumber())
assertEquals("999", BigInteger("999").formatReadableNumber())
assertEquals("1 999", BigInteger("1999").formatReadableNumber())
assertEquals("8 992", BigInteger("8992").formatReadableNumber())
assertEquals("-8 992", BigInteger("-8992").formatReadableNumber())
assertEquals("100 200", BigInteger("100200").formatReadableNumber())
assertEquals("-100 200", BigInteger("-100200").formatReadableNumber())
assertEquals("-1 100 200", BigInteger("-1100200").formatReadableNumber())
assertEquals("1 100 200", BigInteger("1100200").formatReadableNumber())
assertEquals("2 730 250 200", BigInteger("2730250200").formatReadableNumber())
assertEquals("1 222 730 250 200", BigInteger("1222730250200").formatReadableNumber())
}
}