More storage work and fixes

This commit is contained in:
DBotThePony 2022-05-14 16:45:00 +07:00
parent 42c0c49782
commit af172dafe8
Signed by: DBot
GPG Key ID: DCC23B5715498507
9 changed files with 212 additions and 203 deletions

View File

@ -1,54 +0,0 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public record StackAddPacket(int id, int stack_id, ItemStack stack) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeRegistryId(stack.getItem());
buffer.writeInt(stack.getCount());
buffer.writeBoolean(stack.getTag() != null);
if (stack.getTag() != null)
buffer.writeNbt(stack.getShareTag());
}
public static StackAddPacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack = buffer.readInt();
var item = buffer.readRegistryIdSafe(Item.class);
var count = buffer.readInt();
var state = new ItemStack(item, count);
if (buffer.readBoolean())
state.readShareTag(buffer.readNbt());
return new StackAddPacket(id, stack, state);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof INetworkedItemViewSupplier supplier && Minecraft.getInstance().player.containerMenu.containerId == id ? supplier.getNetworkedItemView() : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
if (get.state.containsKey(stack_id)) {
throw new IllegalStateException("Item tracker " + id + " already has stack with id of " + stack_id);
}
get.state.put(stack_id, new NetworkedItemView.NetworkedItem(stack_id, stack));
get.clearCache();
});
}
}

View File

@ -1,43 +0,0 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public record StackChangePacket(int id, int stack_id, int new_count) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeInt(new_count);
}
public static StackChangePacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack_id = buffer.readInt();
var new_count = buffer.readInt();
return new StackChangePacket(id, stack_id, new_count);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof INetworkedItemViewSupplier supplier && Minecraft.getInstance().player.containerMenu.containerId == id ? supplier.getNetworkedItemView() : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
var get_state = get.state.get(stack_id);
if (get_state == null) {
throw new IllegalStateException("Item tracker " + id + " has no stack with id of " + stack_id);
}
get_state.stack().setCount(new_count);
get.clearCache();
});
}
}

View File

@ -1,40 +0,0 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public record StackRemovePacket(int id, int stack_id) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
}
public static StackRemovePacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack_id = buffer.readInt();
return new StackRemovePacket(id, stack_id);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof INetworkedItemViewSupplier supplier && Minecraft.getInstance().player.containerMenu.containerId == id ? supplier.getNetworkedItemView() : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
var get_state = get.state.remove(stack_id);
if (get_state == null) {
throw new IllegalStateException("Item tracker " + id + " has no stack with id of " + stack_id);
}
get.clearCache();
});
}
}

View File

@ -191,7 +191,7 @@ public class MatteryNetworking {
next_network_id++, next_network_id++,
StackAddPacket.class, StackAddPacket.class,
StackAddPacket::write, StackAddPacket::write,
StackAddPacket::read, StackAddPacket.Companion::read,
StackAddPacket::play, StackAddPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT) Optional.of(NetworkDirection.PLAY_TO_CLIENT)
); );
@ -200,7 +200,7 @@ public class MatteryNetworking {
next_network_id++, next_network_id++,
StackChangePacket.class, StackChangePacket.class,
StackChangePacket::write, StackChangePacket::write,
StackChangePacket::read, StackChangePacket.Companion::read,
StackChangePacket::play, StackChangePacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT) Optional.of(NetworkDirection.PLAY_TO_CLIENT)
); );
@ -209,7 +209,7 @@ public class MatteryNetworking {
next_network_id++, next_network_id++,
StackRemovePacket.class, StackRemovePacket.class,
StackRemovePacket::write, StackRemovePacket::write,
StackRemovePacket::read, StackRemovePacket.Companion::read,
StackRemovePacket::play, StackRemovePacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT) Optional.of(NetworkDirection.PLAY_TO_CLIENT)
); );

View File

@ -5,6 +5,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.RegistryManager import net.minecraftforge.registries.RegistryManager
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
@ -45,13 +46,13 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
} }
override fun deserializeStack(tag: CompoundTag): ItemStackWrapper? { override fun deserializeStack(tag: CompoundTag): ItemStackWrapper? {
val item = RegistryManager.ACTIVE.getRegistry(Item::class.java).getValue(ResourceLocation(tag.getString("item"))) val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(tag.getString("item")))
if (item != null && item !== Items.AIR) { if (item != null && item !== Items.AIR) {
val count = tag.getInt("count") val count = ImpreciseFraction.deserializeNBT(tag["count"])
val itemstack = ItemStack(item, count) val itemstack = ItemStack(item, 1)
itemstack.tag = tag["data"] as? CompoundTag? itemstack.tag = tag["data"] as? CompoundTag
return ItemStackWrapper(itemstack) return ItemStackWrapper(itemstack, copy = false).also { it.count = count }
} }
return null return null

View File

@ -61,12 +61,8 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
for (i in 0 until GRID_WIDTH * GRID_HEIGHT) { for (i in 0 until GRID_WIDTH * GRID_HEIGHT) {
object : AbstractSlotPanel(this@DriveViewerScreen, grid, 0f, 0f) { object : AbstractSlotPanel(this@DriveViewerScreen, grid, 0f, 0f) {
override fun getItemStack(): ItemStack { override fun getItemStack(): ItemStack {
val index = i + scroll_bar.getScroll(menu.view.getItems().size / GRID_WIDTH) val index = i + scroll_bar.getScroll(menu.view.sortedView.size / GRID_WIDTH)
val list = menu.view.getItems() return menu.view.sortedView.getOrNull(index)?.stack ?: ItemStack.EMPTY
return if (index >= list.size) {
ItemStack.EMPTY
} else list[index].stack
} }
override fun mouseScrolledInner(mouse_x: Double, mouse_y: Double, scroll: Double): Boolean { override fun mouseScrolledInner(mouse_x: Double, mouse_y: Double, scroll: Double): Boolean {

View File

@ -25,8 +25,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
object : AbstractSlotPanel(this@ItemMonitorScreen, gridPanel) { object : AbstractSlotPanel(this@ItemMonitorScreen, gridPanel) {
override fun getItemStack(): ItemStack { override fun getItemStack(): ItemStack {
val index = i + scrollBar.getScroll(menu.view.getItemCount() / GRID_WIDTH) val index = i + scrollBar.getScroll(menu.view.getItemCount() / GRID_WIDTH)
val list = menu.view.getItems() return menu.view.sortedView.getOrNull(index)?.stack ?: ItemStack.EMPTY
return if (index >= list.size) ItemStack.EMPTY else list[index].stack
} }
override fun mouseScrolledInner(mouse_x: Double, mouse_y: Double, scroll: Double): Boolean { override fun mouseScrolledInner(mouse_x: Double, mouse_y: Double, scroll: Double): Boolean {

View File

@ -27,9 +27,9 @@ class DriveViewerMenu @JvmOverloads constructor(
) : MatteryPoweredMenu( ) : MatteryPoweredMenu(
MMenus.DRIVE_VIEWER, containerID, inventory, tile MMenus.DRIVE_VIEWER, containerID, inventory, tile
), INetworkedItemViewSupplier { ), INetworkedItemViewSupplier {
@JvmField val view: NetworkedItemView val view: NetworkedItemView
private val powered: PoweredVirtualComponent<ItemStackWrapper>? private val powered: PoweredVirtualComponent<ItemStackWrapper>?
@JvmField val driveSlot: MatterySlot val driveSlot: MatterySlot
private var lastDrive: IMatteryDrive<ItemStackWrapper>? = null private var lastDrive: IMatteryDrive<ItemStackWrapper>? = null
override fun getNetworkedItemView() = view override fun getNetworkedItemView() = view

View File

@ -1,12 +1,17 @@
package ru.dbotthepony.mc.otm.menu.data package ru.dbotthepony.mc.otm.menu.data
import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.platform.InputConstants
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.ClickAction import net.minecraft.world.inventory.ClickAction
import net.minecraft.world.inventory.ClickType import net.minecraft.world.inventory.ClickType
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
@ -17,29 +22,185 @@ import ru.dbotthepony.mc.otm.storage.IStorageListener
import ru.dbotthepony.mc.otm.storage.IStorageView import ru.dbotthepony.mc.otm.storage.IStorageView
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import java.util.* import java.util.*
import java.util.function.Supplier
/** class StackAddPacket(val id: Int, val stack_id: Int, val stack: ItemStack) {
* Creates a virtual, slotless container for Player's interaction with. fun write(buffer: FriendlyByteBuf) {
*/ buffer.writeInt(id)
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageListener<ItemStackWrapper> { buffer.writeInt(stack_id)
@JvmRecord buffer.writeRegistryId(stack.item)
data class NetworkedItem @JvmOverloads constructor(val id: Int, val stack: ItemStack, val upstreamId: UUID? = null) buffer.writeInt(stack.count)
buffer.writeBoolean(stack.shareTag != null)
@JvmField protected var next_stack_id = 0 if (stack.tag != null) buffer.writeNbt(stack.shareTag)
@JvmField val state = HashMap<Int, NetworkedItem>()
@JvmField protected val upstream_state = HashMap<UUID, NetworkedItem>()
@JvmField protected val backlog = ArrayList<Any>()
operator fun get(id: Int): NetworkedItem? {
return state[id]
} }
private var cachedView: List<NetworkedItem>? = null fun play(context: Supplier<NetworkEvent.Context>) {
@JvmField protected var provider: IStorageComponent<ItemStackWrapper>? = null context.get().packetHandled = true
context.get().enqueueWork {
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
if (get.containerId != id)
return@enqueueWork
val view = (get as? INetworkedItemViewSupplier)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
if (view.localState.containsKey(stack_id)) {
throw IllegalStateException("Item tracker $id already has stack with id of $stack_id")
}
val state = NetworkedItemView.NetworkedItem(stack_id, stack)
view.localState[stack_id] = state
/*val iterator = view.sortedView.iterator().withIndex()
var lastCompare = 0
var hit = false
while (iterator.hasNext()) {
val (i, existing) = iterator.next()
val cmp = view.sorter.compare(existing.stack, stack)
if (cmp != lastCompare && lastCompare != 0) {
hit = true
view.sortedView.add(i, state)
} else if (cmp != lastCompare) {
lastCompare = cmp
}
}
if (!hit) {*/
view.sortedView.add(state)
//}
view.resort()
}
}
companion object {
fun read(buffer: FriendlyByteBuf): StackAddPacket {
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)
}
}
}
class StackChangePacket(val id: Int, val stackID: Int, val newCount: Int) {
fun write(buffer: FriendlyByteBuf) {
buffer.writeInt(id)
buffer.writeInt(stackID)
buffer.writeInt(newCount)
}
fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
if (get.containerId != id)
return@enqueueWork
val view = (get as? INetworkedItemViewSupplier)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
val state = view.localState[stackID] ?: throw IllegalStateException("No such stack with id $stackID in $view")
state.stack.count = newCount
view.resort()
}
}
companion object {
fun read(buffer: FriendlyByteBuf): StackChangePacket {
val id = buffer.readInt()
val stackID = buffer.readInt()
val newCount = buffer.readInt()
return StackChangePacket(id, stackID, newCount)
}
}
}
class StackRemovePacket(val id: Int, val stackID: Int) {
fun write(buffer: FriendlyByteBuf) {
buffer.writeInt(id)
buffer.writeInt(stackID)
}
fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
if (get.containerId != id)
return@enqueueWork
val view = (get as? INetworkedItemViewSupplier)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
val obj = view.localState.remove(stackID) ?: throw IllegalStateException("No such stack with id $stackID in $view")
view.sortedView.remove(obj)
view.resort()
}
}
companion object {
fun read(buffer: FriendlyByteBuf): StackRemovePacket {
val id = buffer.readInt()
val stackID = buffer.readInt()
return StackRemovePacket(id, stackID)
}
}
}
/**
* Creates a virtual, slotless container for Player to interaction with.
*/
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageListener<ItemStackWrapper> {
data class NetworkedItem constructor(val id: Int, val stack: ItemStack, val upstreamId: UUID? = null)
protected var nextStackID = 0
// this (how client see and interact with)
val localState = Int2ObjectAVLTreeMap<NetworkedItem>()
val sortedView = LinkedList<NetworkedItem>()
var sorter: Comparator<ItemStack> = NAME_SORTER
companion object {
val NAME_SORTER = Comparator<ItemStack> { o1, o2 ->
val cmp = o1.displayName.string.compareTo(o2.displayName.string)
if (cmp != 0)
return@Comparator cmp
return@Comparator o1.item.registryName.toString().compareTo(o2.item.registryName.toString())
}
val COUNT_SORTER = Comparator<ItemStack> { o1, o2 ->
val cmp = o1.count.compareTo(o2.count)
if (cmp != 0)
return@Comparator cmp
return@Comparator o1.item.registryName.toString().compareTo(o2.item.registryName.toString())
}
}
fun resort() {
sortedView.sortWith { a, b ->
return@sortWith sorter.compare(a.stack, b.stack)
}
}
// parent (e.g. VirtualComponent)
protected val upstreamState = HashMap<UUID, NetworkedItem>()
protected val networkBacklog = ArrayList<Any>()
operator fun get(id: Int): NetworkedItem? = localState[id]
protected var provider: IStorageComponent<ItemStackWrapper>? = null
fun mouseClick(index: Int, mouse_click_type: Int) { fun mouseClick(index: Int, mouse_click_type: Int) {
val list = getItems() val list = sortedView
val action = val action =
if (mouse_click_type == InputConstants.MOUSE_BUTTON_LEFT) ClickAction.PRIMARY else ClickAction.SECONDARY if (mouse_click_type == InputConstants.MOUSE_BUTTON_LEFT) ClickAction.PRIMARY else ClickAction.SECONDARY
@ -65,16 +226,8 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
provider?.removeListenerAuto(this) provider?.removeListenerAuto(this)
} }
fun clearCache() {
cachedView = null
}
fun getItems(): List<NetworkedItem> {
return if (cachedView != null) cachedView!! else java.util.List.copyOf(state.values).also { cachedView = it }
}
fun getItemCount(): Int { fun getItemCount(): Int {
return state.values.size return localState.values.size
} }
override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView<ItemStackWrapper>) = addObject(stack.stack, id) override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView<ItemStackWrapper>) = addObject(stack.stack, id)
@ -82,60 +235,57 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
protected fun network(fn: () -> Any) { protected fun network(fn: () -> Any) {
if (!remote) { if (!remote) {
backlog.add(fn()) networkBacklog.add(fn())
} }
} }
override fun removeStack(stack: ItemStackWrapper, id: UUID) { override fun removeStack(stack: ItemStackWrapper, id: UUID) {
val get = upstream_state[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!") val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
upstream_state.remove(id) upstreamState.remove(id)
state.remove(get.id) localState.remove(get.id)
network { StackRemovePacket(menu.containerId, get.id) } network { StackRemovePacket(menu.containerId, get.id) }
clearCache()
} }
fun addObject(stack: ItemStack, id_upstream: UUID) { fun addObject(stack: ItemStack, id_upstream: UUID) {
check(!upstream_state.containsKey(id_upstream)) { "Already tracking ItemStack with upstream id $id_upstream!" } check(!upstreamState.containsKey(id_upstream)) { "Already tracking ItemStack with upstream id $id_upstream!" }
val state = NetworkedItem(next_stack_id++, stack.copy(), id_upstream) val state = NetworkedItem(nextStackID++, stack.copy(), id_upstream)
this.state[state.id] = state this.localState[state.id] = state
upstream_state[id_upstream] = state upstreamState[id_upstream] = state
network { StackAddPacket(menu.containerId, state.id, stack) } network { StackAddPacket(menu.containerId, state.id, stack) }
clearCache()
} }
fun changeObject(id_upstream: UUID, new_count: Int) { fun changeObject(id_upstream: UUID, new_count: Int) {
val get = upstream_state[id_upstream] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id_upstream!") val get = upstreamState[id_upstream] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id_upstream!")
get.stack.count = new_count get.stack.count = new_count
network { StackChangePacket(menu.containerId, get.id, new_count) } network { StackChangePacket(menu.containerId, get.id, new_count) }
clearCache()
} }
fun clear() { fun clear() {
clearCache() sortedView.clear()
upstream_state.clear() upstreamState.clear()
state.clear() localState.clear()
if (!remote) { if (!remote) {
backlog.clear() networkBacklog.clear()
backlog.add(ClearPacket(menu.containerId)) networkBacklog.add(ClearPacket(menu.containerId))
} }
} }
fun network() { fun network() {
check(!remote) { "Not a server" } check(!remote) { "Not a server" }
val consumer = PacketDistributor.PLAYER.with { ply as ServerPlayer } val consumer = PacketDistributor.PLAYER.with { ply as ServerPlayer }
for (packet in backlog) MatteryNetworking.CHANNEL.send(consumer, packet) for (packet in networkBacklog) MatteryNetworking.CHANNEL.send(consumer, packet)
backlog.clear() networkBacklog.clear()
} }
fun playerInteract(packet: InteractPacket) { fun playerInteract(packet: InteractPacket) {
val provider = provider ?: return val provider = provider ?: return
val click = packet.click() val click = packet.click
val action = packet.action() val action = packet.action
val stack_id = packet.stack_id() val stack_id = packet.stack_id
if (click == ClickType.CLONE) { if (click == ClickType.CLONE) {
if (stack_id < 0 || !ply.abilities.instabuild) return if (stack_id < 0 || !ply.abilities.instabuild) return