Exopack arbitrary inventory slots charging
This commit is contained in:
parent
344364520d
commit
37f4b77994
@ -118,6 +118,7 @@ private fun sounds(provider: MatteryLanguageProvider) {
|
||||
private fun misc(provider: MatteryLanguageProvider) {
|
||||
with(provider.english) {
|
||||
gui("help.slot_filters", "Hold CTRL to setup slot filters")
|
||||
gui("help.slot_charging", "Hold ALT to switch slot charging")
|
||||
|
||||
misc("needs_no_power", "Requires no power to operate")
|
||||
|
||||
|
@ -126,6 +126,7 @@ private fun sounds(provider: MatteryLanguageProvider) {
|
||||
private fun misc(provider: MatteryLanguageProvider) {
|
||||
with(provider.russian) {
|
||||
gui("help.slot_filters", "Удерживайте CTRL для настройки фильтрации слотов")
|
||||
gui("help.slot_charging", "Удерживайте ALT для переключения зарядки слотов")
|
||||
|
||||
misc("needs_no_power", "Не требует энергии для работы")
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
package ru.dbotthepony.mc.otm.capability
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.commands.Commands
|
||||
import net.minecraft.commands.arguments.EntityArgument
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.nbt.ByteTag
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.IntTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.network.chat.Component
|
||||
@ -72,6 +75,7 @@ import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.config.AndroidConfig
|
||||
import ru.dbotthepony.mc.otm.config.ExopackConfig
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.container.get
|
||||
import ru.dbotthepony.mc.otm.container.iterator
|
||||
import ru.dbotthepony.mc.otm.container.stream
|
||||
import ru.dbotthepony.mc.otm.container.vanishCursedItems
|
||||
@ -80,14 +84,18 @@ import ru.dbotthepony.mc.otm.core.collect.UUIDIntModifiersMap
|
||||
import ru.dbotthepony.mc.otm.core.collect.filter
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.minus
|
||||
import ru.dbotthepony.mc.otm.core.nbt.getByteList
|
||||
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
|
||||
import ru.dbotthepony.mc.otm.core.nbt.getIntList
|
||||
import ru.dbotthepony.mc.otm.core.nbt.getStringList
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.IntValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.Savetables
|
||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||
import ru.dbotthepony.mc.otm.core.util.UUIDValueCodec
|
||||
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
|
||||
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
|
||||
import ru.dbotthepony.mc.otm.network.*
|
||||
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
|
||||
@ -224,6 +232,27 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
synchronizer.Field(null, ItemValueCodec.nullable)
|
||||
}
|
||||
|
||||
val regularSlotChargeFlag = immutableList(Inventory.INVENTORY_SIZE + 4) {
|
||||
synchronizer.bool()
|
||||
}
|
||||
|
||||
private fun slotChargeToDefault() {
|
||||
// броня
|
||||
regularSlotChargeFlag[36].boolean = true
|
||||
regularSlotChargeFlag[37].boolean = true
|
||||
regularSlotChargeFlag[38].boolean = true
|
||||
regularSlotChargeFlag[39].boolean = true
|
||||
}
|
||||
|
||||
init {
|
||||
slotChargeToDefault()
|
||||
}
|
||||
|
||||
val exoPackSlotsChargeFlag by synchronizer.Set(
|
||||
codec = VarIntValueCodec,
|
||||
backingSet = IntAVLTreeSet(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Exopack container, which actually store items inside Exopack
|
||||
*/
|
||||
@ -808,6 +837,18 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
tag["regularSlotChargeFlag"] = ListTag().also {
|
||||
for (flag in regularSlotChargeFlag) {
|
||||
it.add(ByteTag.valueOf(flag.boolean))
|
||||
}
|
||||
}
|
||||
|
||||
tag["exoPackSlotsChargeFlag"] = ListTag().also {
|
||||
for (value in exoPackSlotsChargeFlag) {
|
||||
it.add(IntTag.valueOf(value))
|
||||
}
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
@ -818,6 +859,14 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
filter.value = null
|
||||
}
|
||||
|
||||
for (flag in regularSlotChargeFlag) {
|
||||
flag.boolean = false
|
||||
}
|
||||
|
||||
exoPackSlotsChargeFlag.clear()
|
||||
|
||||
slotChargeToDefault()
|
||||
|
||||
val regularSlotFilters = tag.getStringList("regularSlotFilters")
|
||||
|
||||
for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) {
|
||||
@ -826,6 +875,16 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR
|
||||
}
|
||||
|
||||
val regularSlotChargeFlag = tag.getByteList("regularSlotChargeFlag")
|
||||
|
||||
for (i in 0 until regularSlotChargeFlag.size.coerceAtMost(this.regularSlotChargeFlag.size)) {
|
||||
this.regularSlotChargeFlag[i].boolean = regularSlotChargeFlag[i].asInt > 0
|
||||
}
|
||||
|
||||
for (v in tag.getIntList("exoPackSlotsChargeFlag")) {
|
||||
this.exoPackSlotsChargeFlag.add(v.asInt)
|
||||
}
|
||||
|
||||
// iterations
|
||||
deathLog.clear()
|
||||
|
||||
@ -965,9 +1024,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
if (isExoPackSmeltingInstalled)
|
||||
smelters.forEach { it.think() }
|
||||
|
||||
if (!exoPackChargeSlots.isEmpty && exoPackEnergy.batteryLevel.isPositive) {
|
||||
if (exoPackEnergy.batteryLevel.isPositive) {
|
||||
var available = exoPackEnergy.extractEnergy(exoPackEnergy.batteryLevel, true)
|
||||
|
||||
if (!exoPackChargeSlots.isEmpty) {
|
||||
for (item in exoPackChargeSlots) {
|
||||
if (item.isNotEmpty) {
|
||||
item.energy?.let {
|
||||
@ -978,6 +1038,39 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (available.isPositive) {
|
||||
for ((i, flag) in regularSlotChargeFlag.withIndex()) {
|
||||
if (flag.boolean) {
|
||||
val item = ply.inventory[i]
|
||||
|
||||
if (item.isNotEmpty) {
|
||||
item.energy?.let {
|
||||
available -= exoPackEnergy.extractEnergy(it.receiveEnergy(available, false), false)
|
||||
}
|
||||
|
||||
if (!available.isPositive) break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (available.isPositive) {
|
||||
for (slot in exoPackSlotsChargeFlag) {
|
||||
if (slot in 0 until exoPackContainer.containerSize) {
|
||||
val item = exoPackContainer[slot]
|
||||
|
||||
if (item.isNotEmpty) {
|
||||
item.energy?.let {
|
||||
available -= exoPackEnergy.extractEnergy(it.receiveEnergy(available, false), false)
|
||||
}
|
||||
|
||||
if (!available.isPositive) break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ fun Window.isKeyDown(key: Int) = InputConstants.isKeyDown(window, key)
|
||||
|
||||
val Window.isShiftDown get() = isKeyDown(InputConstants.KEY_LSHIFT) || isKeyDown(InputConstants.KEY_RSHIFT)
|
||||
val Window.isCtrlDown get() = isKeyDown(InputConstants.KEY_RCONTROL) || isKeyDown(InputConstants.KEY_LCONTROL)
|
||||
val Window.isAltDown get() = isKeyDown(InputConstants.KEY_RALT) || isKeyDown(InputConstants.KEY_LALT)
|
||||
|
||||
object ShiftPressedCond : BooleanSupplier {
|
||||
override fun getAsBoolean(): Boolean {
|
||||
|
@ -13,7 +13,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.*
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||
@ -55,7 +55,7 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
|
||||
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
||||
val frame = FramePanel(this, width = 200f, height = FRAME_BASE_HEIGHT + inventoryRows * AbstractSlotPanel.SIZE, title = this.title)
|
||||
|
||||
frame.makeHelpButton().addSlotFiltersHelp()
|
||||
frame.makeHelpButton().addSlotFiltersHelp().tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
|
||||
|
||||
val hotbarStrip = EditablePanel(this, frame, height = 18f)
|
||||
hotbarStrip.dock = Dock.BOTTOM
|
||||
@ -92,7 +92,7 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
|
||||
mainInventoryLine.dock = Dock.BOTTOM
|
||||
|
||||
for (slot in menu.playerHotbarSlots) {
|
||||
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also {
|
||||
InventorySlotPanel(this, hotbarStrip, slot).also {
|
||||
it.dock = Dock.LEFT
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +1,42 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.gui.Font
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.client.ForgeHooksClient
|
||||
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Background
|
||||
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Foreground
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.lwjgl.opengl.GL13
|
||||
import ru.dbotthepony.mc.otm.config.ClientConfig
|
||||
import ru.dbotthepony.mc.otm.client.moveMousePosScaled
|
||||
import ru.dbotthepony.mc.otm.client.render.clearDepth
|
||||
import ru.dbotthepony.mc.otm.client.render.translation
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
|
||||
import ru.dbotthepony.mc.otm.config.ClientConfig
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.math.component1
|
||||
import ru.dbotthepony.mc.otm.core.math.component2
|
||||
import ru.dbotthepony.mc.otm.core.math.component3
|
||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import java.util.Collections
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayDeque
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.MutableSet
|
||||
import kotlin.collections.indices
|
||||
import kotlin.collections.isNotEmpty
|
||||
import kotlin.collections.withIndex
|
||||
|
||||
/**
|
||||
* This class encapsulate most of logic for handling EditablePanel and it's children.
|
||||
@ -151,12 +152,13 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
||||
if (menu.playerInventorySlots.isNotEmpty() && menu.autoCreateInventoryFrame) {
|
||||
if (menu.playerExoSuitSlots.isEmpty()) {
|
||||
inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel)
|
||||
inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
|
||||
|
||||
val hotbarStrip = EditablePanel(this, inventoryFrame, height = AbstractSlotPanel.SIZE)
|
||||
hotbarStrip.dock = Dock.BOTTOM
|
||||
|
||||
for (slot in menu.playerHotbarSlots) {
|
||||
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also {
|
||||
InventorySlotPanel(this, hotbarStrip, slot).also {
|
||||
it.dock = Dock.LEFT
|
||||
}
|
||||
}
|
||||
@ -174,6 +176,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
||||
}
|
||||
} else {
|
||||
inventoryFrame = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH_EXTENDED, BASE_INVENTORY_FRAME_HEIGHT + AbstractSlotPanel.SIZE * inventoryRows, inventory.displayName).also(this::addPanel)
|
||||
inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
|
||||
|
||||
inventoryScrollbar = DiscreteScrollBarPanel(this, inventoryFrame, { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) }, {
|
||||
_, old, new ->
|
||||
@ -206,7 +209,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
||||
hotbarStrip.dock = Dock.BOTTOM
|
||||
|
||||
for (slot in menu.playerHotbarSlots) {
|
||||
UserFilteredSlotPanel.of(this, hotbarStrip, slot).also {
|
||||
InventorySlotPanel(this, hotbarStrip, slot).also {
|
||||
it.dock = Dock.LEFT
|
||||
}
|
||||
}
|
||||
@ -249,7 +252,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
|
||||
for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) {
|
||||
val slot = menu.playerCombinedInventorySlots[offset + i]
|
||||
|
||||
object : UserFilteredSlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, slot) {
|
||||
object : InventorySlotPanel<MatteryScreen<*>, MatteryMenu.InventorySlot>(this@MatteryScreen, canvas, slot) {
|
||||
init {
|
||||
dock = Dock.LEFT
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FoldableSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel
|
||||
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton
|
||||
@ -64,10 +65,10 @@ open class PlayerEquipmentPanel<S : MatteryScreen<*>>(
|
||||
|
||||
for ((slot, cosmeticSlot) in armorSlots) {
|
||||
if (cosmeticSlot == null) {
|
||||
SlotPanel(screen, armorSlotsStrip, slot).dock = Dock.TOP
|
||||
InventorySlotPanel(screen, armorSlotsStrip, slot).dock = Dock.TOP
|
||||
} else {
|
||||
FoldableSlotPanel(screen, armorSlotsStrip,
|
||||
SlotPanel(screen, null, slot).also { CosmeticToggleButton(screen, it, slot.type) },
|
||||
InventorySlotPanel(screen, null, slot).also { CosmeticToggleButton(screen, it, slot.type) },
|
||||
listOf(SlotPanel(screen, null, cosmeticSlot))).dock = Dock.TOP
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.slot
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.world.item.Item
|
||||
import ru.dbotthepony.mc.otm.client.isAltDown
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.playGuiClickSound
|
||||
import ru.dbotthepony.mc.otm.client.render.MatterySprite
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
|
||||
open class InventorySlotPanel<out S : MatteryScreen<*>, out T : MatteryMenu.InventorySlot>(
|
||||
screen: S,
|
||||
parent: EditablePanel<*>?,
|
||||
slot: T,
|
||||
x: Float = 0f,
|
||||
y: Float = 0f,
|
||||
noItemIcon: MatterySprite? = null
|
||||
) : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, SIZE, SIZE, noItemIcon) {
|
||||
override var slotFilter: Item?
|
||||
get() = slot.filter?.get()
|
||||
set(value) { slot.filter?.accept(value) }
|
||||
|
||||
override fun renderSlotBackground(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
|
||||
|
||||
if (slot.chargeFlag?.get() == true) {
|
||||
Widgets18.BATTERY_SLOT_BACKGROUND.render(graphics, 0f, 0f, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
|
||||
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isAltDown) {
|
||||
val chargeFlag = slot.chargeFlag
|
||||
|
||||
if (chargeFlag == null) {
|
||||
return super.mouseClickedInner(x, y, button)
|
||||
} else {
|
||||
chargeFlag.accept(!chargeFlag.get())
|
||||
playGuiClickSound()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseClickedInner(x, y, button)
|
||||
}
|
||||
}
|
@ -262,20 +262,40 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
return super.isSameInventory(other)
|
||||
}
|
||||
|
||||
var chargeFlag: GetterSetter<Boolean>? = null
|
||||
private set
|
||||
|
||||
init {
|
||||
val mattery = ply.matteryPlayer
|
||||
|
||||
if (mattery != null) {
|
||||
if (container === inventory && slotIndex in mattery.regularSlotFilters.indices) {
|
||||
if (container === inventory) {
|
||||
if (slotIndex in mattery.regularSlotFilters.indices)
|
||||
filter = GetterSetter.of(
|
||||
getter = { mattery.regularSlotFilters[slotIndex].value },
|
||||
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.INVENTORY, slotIndex, it)) }
|
||||
)
|
||||
|
||||
if (slotIndex in mattery.regularSlotChargeFlag.indices) {
|
||||
val input = booleanInput(true) { mattery.regularSlotChargeFlag[slotIndex].boolean = it }
|
||||
|
||||
chargeFlag = GetterSetter.of(
|
||||
getter = { mattery.regularSlotChargeFlag[slotIndex].boolean },
|
||||
setter = input::input
|
||||
)
|
||||
}
|
||||
} else if (container === mattery.exoPackContainer) {
|
||||
filter = GetterSetter.of(
|
||||
getter = { mattery.exoPackContainer.getSlotFilter(slotIndex) },
|
||||
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) }
|
||||
)
|
||||
|
||||
val input = booleanInput(true) { if (it) mattery.exoPackSlotsChargeFlag.add(slotIndex) else mattery.exoPackSlotsChargeFlag.remove(slotIndex) }
|
||||
|
||||
chargeFlag = GetterSetter.of(
|
||||
getter = { slotIndex in mattery.exoPackSlotsChargeFlag },
|
||||
setter = input::input
|
||||
)
|
||||
} else {
|
||||
filter = null
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
package ru.dbotthepony.mc.otm.network.synchronizer
|
||||
|
||||
enum class MapAction {
|
||||
enum class ChangesetAction {
|
||||
CLEAR, ADD, REMOVE
|
||||
}
|
@ -349,6 +349,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
// use LinkedList because it is ensured memory is freed on LinkedList#clear
|
||||
private val mapBacklogs = Reference2ObjectOpenHashMap<Map<*, *>, LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>()
|
||||
private val setBacklogs = Reference2ObjectOpenHashMap<Set<*>, LinkedList<Pair<Any?, (DataOutputStream) -> Unit>>>()
|
||||
|
||||
var unused: Boolean = false
|
||||
private set
|
||||
@ -401,6 +402,24 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
})
|
||||
}
|
||||
|
||||
internal fun <K, V> removeMapBacklog(map: Map<K, V>) {
|
||||
mapBacklogs.remove(map)
|
||||
}
|
||||
|
||||
internal fun <V> getSetBacklog(set: Set<V>): LinkedList<Pair<Any?, (DataOutputStream) -> Unit>> {
|
||||
if (unused) {
|
||||
return LinkedList()
|
||||
}
|
||||
|
||||
return setBacklogs.computeIfAbsent(set, Reference2ObjectFunction {
|
||||
LinkedList()
|
||||
})
|
||||
}
|
||||
|
||||
internal fun <V> removeSetBacklog(set: Set<V>) {
|
||||
setBacklogs.remove(set)
|
||||
}
|
||||
|
||||
fun collectNetworkPayload(): FastByteArrayOutputStream? {
|
||||
if (unused || dirtyFields.isEmpty()) {
|
||||
return null
|
||||
@ -1527,6 +1546,246 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
}
|
||||
}
|
||||
|
||||
inner class Set<E>(
|
||||
private val codec: IStreamCodec<E>,
|
||||
private val backingSet: MutableSet<E>,
|
||||
private val callback: ((changes: Collection<SetChangeset<E>>) -> Unit)? = null,
|
||||
) : AbstractField<MutableSet<E>>() {
|
||||
private var isRemote = false
|
||||
private var isDirty = false
|
||||
|
||||
private fun pushBacklog(element: E, action: (DataOutputStream) -> Unit) {
|
||||
check(!isRemote) { "Field marked as remote" }
|
||||
|
||||
val pair = element to action
|
||||
|
||||
forEachEndpoint {
|
||||
val list = it.getSetBacklog(this)
|
||||
val iterator = list.listIterator()
|
||||
|
||||
for (value in iterator) {
|
||||
if (value.first == element) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
list.addLast(pair)
|
||||
}
|
||||
}
|
||||
|
||||
override fun observe(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
if (!isRemoved) {
|
||||
forEachEndpoint { it.removeSetBacklog(this) }
|
||||
}
|
||||
|
||||
super.remove()
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
check(!isRemoved) { "Field was removed" }
|
||||
notifyEndpoints(this)
|
||||
isDirty = true
|
||||
}
|
||||
|
||||
override fun markDirty(endpoint: Endpoint) {
|
||||
super.markDirty(endpoint)
|
||||
|
||||
endpoint.getSetBacklog(this).let {
|
||||
it.clear()
|
||||
it.add(null to ClearBacklogEntry)
|
||||
|
||||
for (value in backingSet) {
|
||||
it.add(value to { it.write(ChangesetAction.ADD.ordinal + 1); codec.write(it, value) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val value: MutableSet<E> = object : MutableSet<E> {
|
||||
override fun add(element: E): Boolean {
|
||||
if (backingSet.add(element)) {
|
||||
if (!isRemote) {
|
||||
markDirty()
|
||||
|
||||
pushBacklog(element) {
|
||||
it.write(ChangesetAction.ADD.ordinal + 1)
|
||||
codec.write(it, element)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<E>): Boolean {
|
||||
var any = false
|
||||
elements.forEach { any = add(it) || any }
|
||||
return any
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
if (backingSet.isNotEmpty()) {
|
||||
backingSet.clear()
|
||||
|
||||
if (!isRemote) {
|
||||
markDirty()
|
||||
|
||||
forEachEndpoint {
|
||||
it.getSetBacklog(this@Set).let {
|
||||
it.clear()
|
||||
it.add(null to ClearBacklogEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<E> {
|
||||
return object : MutableIterator<E> {
|
||||
private val parent = backingSet.iterator()
|
||||
private var lastElement: Any? = Mark
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return parent.hasNext()
|
||||
}
|
||||
|
||||
override fun next(): E {
|
||||
return parent.next().also { lastElement = it }
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
parent.remove()
|
||||
val lastElement = lastElement
|
||||
|
||||
if (lastElement !== Mark) {
|
||||
this.lastElement = Mark
|
||||
|
||||
if (!isRemote) {
|
||||
markDirty()
|
||||
|
||||
pushBacklog(lastElement as E) {
|
||||
it.write(ChangesetAction.REMOVE.ordinal + 1)
|
||||
codec.write(it, lastElement as E)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(element: E): Boolean {
|
||||
if (backingSet.remove(element)) {
|
||||
if (!isRemote) {
|
||||
markDirty()
|
||||
|
||||
pushBacklog(element) {
|
||||
it.write(ChangesetAction.REMOVE.ordinal + 1)
|
||||
codec.write(it, element)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean {
|
||||
var any = false
|
||||
elements.forEach { any = remove(it) || any }
|
||||
return any
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean {
|
||||
var any = false
|
||||
|
||||
val iterator = iterator()
|
||||
|
||||
for (value in iterator) {
|
||||
if (value !in elements) {
|
||||
any = true
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = backingSet.size
|
||||
|
||||
override fun contains(element: E): Boolean {
|
||||
return element in backingSet
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<E>): Boolean {
|
||||
return backingSet.containsAll(elements)
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return backingSet.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
||||
val list = endpoint.getSetBacklog(this)
|
||||
|
||||
for (value in list) {
|
||||
value.second.invoke(stream)
|
||||
}
|
||||
|
||||
stream.write(0)
|
||||
list.clear()
|
||||
isDirty = false
|
||||
}
|
||||
|
||||
override fun read(stream: DataInputStream) {
|
||||
if (!isRemote) {
|
||||
isRemote = true
|
||||
forEachEndpoint { it.removeSetBacklog(this) }
|
||||
}
|
||||
|
||||
isDirty = false
|
||||
|
||||
var action = stream.read()
|
||||
val changeset = LinkedList<SetChangeset<E>>()
|
||||
|
||||
while (action != 0) {
|
||||
if ((action - 1) !in ChangesetActionList.indices) {
|
||||
throw IllegalArgumentException("Unknown changeset action with index ${action - 1}")
|
||||
}
|
||||
|
||||
when (ChangesetActionList[action - 1]) {
|
||||
ChangesetAction.CLEAR -> {
|
||||
changeset.add(SetChangeset(ChangesetAction.CLEAR, null))
|
||||
backingSet.clear()
|
||||
}
|
||||
|
||||
ChangesetAction.ADD -> {
|
||||
val read = codec.read(stream)
|
||||
changeset.add(SetChangeset(ChangesetAction.ADD, read))
|
||||
backingSet.add(read)
|
||||
}
|
||||
|
||||
ChangesetAction.REMOVE -> {
|
||||
val read = codec.read(stream)
|
||||
changeset.add(SetChangeset(ChangesetAction.REMOVE, read))
|
||||
backingSet.remove(read)
|
||||
}
|
||||
}
|
||||
|
||||
action = stream.read()
|
||||
}
|
||||
|
||||
callback?.invoke(changeset)
|
||||
}
|
||||
}
|
||||
|
||||
inner class Map<K, V>(
|
||||
private val keyCodec: IStreamCodec<K>,
|
||||
private val valueCodec: IStreamCodec<V>,
|
||||
@ -1545,17 +1804,19 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
}
|
||||
|
||||
private fun pushBacklog(key: Any?, value: (DataOutputStream) -> Unit) {
|
||||
val pair = key to value
|
||||
|
||||
forEachEndpoint {
|
||||
val list = it.getMapBacklog(this)
|
||||
val iterator = list.listIterator()
|
||||
|
||||
for (pair in iterator) {
|
||||
if (pair.first == key) {
|
||||
for (e in iterator) {
|
||||
if (e.first == key) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
list.addLast(key to value)
|
||||
list.addLast(pair)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1582,7 +1843,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
val valueCopy = valueCodec.copy(value)
|
||||
|
||||
pushBacklog(key) {
|
||||
it.write(MapAction.ADD.ordinal + 1)
|
||||
it.write(ChangesetAction.ADD.ordinal + 1)
|
||||
keyCodec.write(it, key)
|
||||
valueCodec.write(it, valueCopy)
|
||||
}
|
||||
@ -1631,7 +1892,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
val valueCopy = valueCodec.copy(value)
|
||||
|
||||
val action = { it: DataOutputStream ->
|
||||
it.write(MapAction.ADD.ordinal + 1)
|
||||
it.write(ChangesetAction.ADD.ordinal + 1)
|
||||
keyCodec.write(it, key)
|
||||
valueCodec.write(it, valueCopy)
|
||||
}
|
||||
@ -1660,7 +1921,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
val valueCopy = valueCodec.copy(value)
|
||||
|
||||
backlog.add(key to {
|
||||
it.write(MapAction.ADD.ordinal + 1)
|
||||
it.write(ChangesetAction.ADD.ordinal + 1)
|
||||
keyCodec.write(it, key)
|
||||
valueCodec.write(it, valueCopy)
|
||||
})
|
||||
@ -1707,7 +1968,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
pushBacklog(key) {
|
||||
@Suppress("BlockingMethodInNonBlockingContext") // false positive
|
||||
it.write(MapAction.ADD.ordinal + 1)
|
||||
it.write(ChangesetAction.ADD.ordinal + 1)
|
||||
keyCodec.write(it, key)
|
||||
valueCodec.write(it, valueCopy)
|
||||
}
|
||||
@ -1735,7 +1996,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
pushBacklog(key) {
|
||||
@Suppress("BlockingMethodInNonBlockingContext") // false positive
|
||||
it.write(MapAction.REMOVE.ordinal + 1)
|
||||
it.write(ChangesetAction.REMOVE.ordinal + 1)
|
||||
keyCodec.write(it, keyCopy)
|
||||
}
|
||||
|
||||
@ -1767,7 +2028,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
override fun read(stream: DataInputStream) {
|
||||
if (!isRemote) {
|
||||
isRemote = true
|
||||
clearBacklog()
|
||||
forEachEndpoint { it.removeMapBacklog(this) }
|
||||
observingBackingMap?.clear()
|
||||
}
|
||||
|
||||
@ -1777,27 +2038,27 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
var readAction = stream.read() - 1
|
||||
|
||||
while (readAction != -1) {
|
||||
if (readAction >= MapActionList.size) {
|
||||
if (readAction >= ChangesetActionList.size) {
|
||||
throw IndexOutOfBoundsException("Unknown map action with ID $readAction")
|
||||
}
|
||||
|
||||
when (MapActionList[readAction]) {
|
||||
MapAction.CLEAR -> {
|
||||
when (ChangesetActionList[readAction]) {
|
||||
ChangesetAction.CLEAR -> {
|
||||
backingMap.clear()
|
||||
changeset.add(ClearMapChangeset)
|
||||
}
|
||||
|
||||
MapAction.ADD -> {
|
||||
ChangesetAction.ADD -> {
|
||||
val key = keyCodec.read(stream)
|
||||
val value = valueCodec.read(stream)
|
||||
backingMap[key] = value
|
||||
changeset.add(MapChangeset(MapAction.ADD, key, value))
|
||||
changeset.add(MapChangeset(ChangesetAction.ADD, key, value))
|
||||
}
|
||||
|
||||
MapAction.REMOVE -> {
|
||||
ChangesetAction.REMOVE -> {
|
||||
val key = keyCodec.read(stream)
|
||||
backingMap.remove(key)
|
||||
changeset.add(MapChangeset(MapAction.REMOVE, key, null))
|
||||
changeset.add(MapChangeset(ChangesetAction.REMOVE, key, null))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1860,9 +2121,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
return i
|
||||
}
|
||||
|
||||
private object Mark
|
||||
|
||||
companion object {
|
||||
private val ClearBacklogEntry = { stream: DataOutputStream -> stream.write(MapAction.CLEAR.ordinal + 1) }
|
||||
private val MapActionList = MapAction.values()
|
||||
private val ClearMapChangeset = MapChangeset(MapAction.CLEAR, null, null)
|
||||
private val ClearBacklogEntry = { stream: DataOutputStream -> stream.write(ChangesetAction.CLEAR.ordinal + 1) }
|
||||
private val ChangesetActionList = ChangesetAction.values()
|
||||
private val ClearMapChangeset = MapChangeset(ChangesetAction.CLEAR, null, null)
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
package ru.dbotthepony.mc.otm.network.synchronizer
|
||||
|
||||
data class MapChangeset<out K, out V>(
|
||||
val action: MapAction,
|
||||
val action: ChangesetAction,
|
||||
val key: K?,
|
||||
val value: V?
|
||||
) {
|
||||
inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit) {
|
||||
when (action) {
|
||||
MapAction.ADD -> add.invoke(key!!, value!!)
|
||||
MapAction.REMOVE -> remove.invoke(key!!)
|
||||
ChangesetAction.ADD -> add.invoke(key!!, value!!)
|
||||
ChangesetAction.REMOVE -> remove.invoke(key!!)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun map(add: (K, V) -> Unit, remove: (K) -> Unit, clear: () -> Unit) {
|
||||
when (action) {
|
||||
MapAction.CLEAR -> clear.invoke()
|
||||
MapAction.ADD -> add.invoke(key!!, value!!)
|
||||
MapAction.REMOVE -> remove.invoke(key!!)
|
||||
ChangesetAction.CLEAR -> clear.invoke()
|
||||
ChangesetAction.ADD -> add.invoke(key!!, value!!)
|
||||
ChangesetAction.REMOVE -> remove.invoke(key!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package ru.dbotthepony.mc.otm.network.synchronizer
|
||||
|
||||
data class SetChangeset<out V>(
|
||||
val action: ChangesetAction,
|
||||
val value: V?
|
||||
) {
|
||||
inline fun map(add: (V) -> Unit, remove: (V) -> Unit) {
|
||||
when (action) {
|
||||
ChangesetAction.ADD -> add.invoke(value!!)
|
||||
ChangesetAction.REMOVE -> remove.invoke(value!!)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun map(add: (V) -> Unit, remove: (V) -> Unit, clear: () -> Unit) {
|
||||
when (action) {
|
||||
ChangesetAction.CLEAR -> clear.invoke()
|
||||
ChangesetAction.ADD -> add.invoke(value!!)
|
||||
ChangesetAction.REMOVE -> remove.invoke(value!!)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user