Holo sign block is now barely functional

This commit is contained in:
DBotThePony 2023-01-26 20:05:14 +07:00
parent c7e086e9ae
commit f279bcd3d6
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 154 additions and 18 deletions

View File

@ -6,15 +6,33 @@ import ru.dbotthepony.mc.otm.client.screen.panels.Dock
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.TextInputPanel import ru.dbotthepony.mc.otm.client.screen.panels.TextInputPanel
import ru.dbotthepony.mc.otm.menu.HoloSignMenu import ru.dbotthepony.mc.otm.menu.HoloSignMenu
import ru.dbotthepony.mc.otm.milliTime
class HoloSignScreen(menu: HoloSignMenu, inventory: Inventory, title: Component) : MatteryScreen<HoloSignMenu>(menu, title) { class HoloSignScreen(menu: HoloSignMenu, inventory: Inventory, title: Component) : MatteryScreen<HoloSignMenu>(menu, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> { override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel(this, null, 0f, 0f, 200f, 200f, getTitle()) val frame = FramePanel(this, null, 0f, 0f, 200f, 200f, getTitle())
val textbox = TextInputPanel(this, frame) object : TextInputPanel<HoloSignScreen>(this@HoloSignScreen, frame) {
textbox.dock = Dock.FILL init {
textbox.multiLine = true dock = Dock.FILL
textbox.text = "МОГУС\nБОНУС\n\nСУС" multiLine = true
}
private var lastChanges = 0L
override fun onTextChanged(old: String, new: String) {
lastChanges = milliTime + 1000L
menu.textInput.input(new)
}
override fun tick() {
super.tick()
if (milliTime >= lastChanges) {
text = menu.text
}
}
}
return frame return frame
} }

View File

@ -80,14 +80,24 @@ open class TextInputPanel<out S : Screen>(
private val cursorLine = this@TextInputPanel.cursorLine private val cursorLine = this@TextInputPanel.cursorLine
private val cursorCharacter = this@TextInputPanel.cursorCharacter private val cursorCharacter = this@TextInputPanel.cursorCharacter
private val selections = Int2ObjectAVLTreeMap(this@TextInputPanel.selections) private val selections = Int2ObjectAVLTreeMap(this@TextInputPanel.selections)
private val multiLine = this@TextInputPanel.multiLine
fun apply() { fun apply() {
this@TextInputPanel.lines.clear() this@TextInputPanel.lines.clear()
if (this@TextInputPanel.multiLine)
this@TextInputPanel.lines.addAll(lines) this@TextInputPanel.lines.addAll(lines)
else
this@TextInputPanel.lines.add(lines.joinToString(""))
this@TextInputPanel.selections.clear() this@TextInputPanel.selections.clear()
if (this@TextInputPanel.multiLine && multiLine)
this@TextInputPanel.selections.putAll(selections) this@TextInputPanel.selections.putAll(selections)
this@TextInputPanel.cursorCharacter = cursorCharacter this@TextInputPanel.cursorCharacter = cursorCharacter
this@TextInputPanel.cursorLine = cursorLine this@TextInputPanel.cursorLine = cursorLine
triggerChangeCallback()
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -114,18 +124,41 @@ open class TextInputPanel<out S : Screen>(
var debugDraw = false var debugDraw = false
var multiLine = false var multiLine = false
set(value) {
if (field == value) return
if (!value && lines.size > 1) {
val merge = lines.joinToString("")
lines.clear()
lines.add(merge)
triggerChangeCallback()
}
field = value
}
var cursorLine = 0 var cursorLine = 0
var cursorCharacter = 0 var cursorCharacter = 0
var textColor = RGBAColor.WHITE var textColor = RGBAColor.WHITE
var cursorColor = RGBAColor.GREEN var cursorColor = RGBAColor.GREEN
var backgroundColor = RGBAColor.BLACK var backgroundColor = RGBAColor.BLACK
private var oldText = ArrayList<String>()
private var textCache: String? = null private var textCache: String? = null
private val lines = ArrayList<String>() private val lines = ArrayList<String>()
private val selections = Int2ObjectAVLTreeMap<TextSelection>() private val selections = Int2ObjectAVLTreeMap<TextSelection>()
private val undo = ArrayDeque<Snapshot>() private val undo = ArrayDeque<Snapshot>()
private val redo = ArrayDeque<Snapshot>() private val redo = ArrayDeque<Snapshot>()
private fun triggerChangeCallback() {
if (oldText != lines) {
textCache = null
val old = oldText.joinToString("\n")
oldText = ArrayList(lines)
onTextChanged(old, textCache())
}
}
private var snapshotTimer: Long? = null private var snapshotTimer: Long? = null
override fun tick() { override fun tick() {
@ -214,9 +247,11 @@ open class TextInputPanel<out S : Screen>(
} }
operator fun set(index: Int, value: String) { operator fun set(index: Int, value: String) {
if (index < 0) { if (index < 0)
throw IndexOutOfBoundsException("negative index $index") throw IndexOutOfBoundsException("negative index $index")
}
if (!multiLine && index != 0)
throw IllegalStateException("Not accepting newlines")
lines.ensureCapacity(index) lines.ensureCapacity(index)
@ -241,6 +276,9 @@ open class TextInputPanel<out S : Screen>(
} }
fun insertLine(index: Int, value: String = "") { fun insertLine(index: Int, value: String = "") {
if (!multiLine && lines.isNotEmpty())
throw IllegalStateException("Not accepting newlines")
lines.ensureCapacity(index) lines.ensureCapacity(index)
while (lines.size < index) { while (lines.size < index) {
@ -262,9 +300,8 @@ open class TextInputPanel<out S : Screen>(
} }
fun removeLine(index: Int): Boolean { fun removeLine(index: Int): Boolean {
if (index < 0 || index >= lines.size) { if (index < 0 || index >= lines.size)
return false return false
}
if (cursorLine == index) { if (cursorLine == index) {
if (index != 0) if (index != 0)
@ -363,6 +400,8 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = selection.start cursorCharacter = selection.start
} }
} }
triggerChangeCallback()
} }
private fun textCache(): String { private fun textCache(): String {
@ -391,6 +430,7 @@ open class TextInputPanel<out S : Screen>(
undo.clear() undo.clear()
cursorLine = 0 cursorLine = 0
cursorCharacter = 0 cursorCharacter = 0
textCache = null
if (multiLine) { if (multiLine) {
lines.addAll(value.split(NEWLINES)) lines.addAll(value.split(NEWLINES))
@ -636,9 +676,12 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = 0 cursorCharacter = 0
} }
triggerChangeCallback()
return true return true
} else { } else {
killFocus() killFocus()
triggerChangeCallback()
return true return true
} }
} }
@ -718,6 +761,7 @@ open class TextInputPanel<out S : Screen>(
} }
recordHistory() recordHistory()
triggerChangeCallback()
} }
} else { } else {
if (line != null) { if (line != null) {
@ -728,13 +772,14 @@ open class TextInputPanel<out S : Screen>(
val newLine = line.substring(0, line.length - 1) val newLine = line.substring(0, line.length - 1)
moveCursors(cursorLine, cursorCharacter, -1) moveCursors(cursorLine, cursorCharacter, -1)
this[cursorLine] = newLine this[cursorLine] = newLine
recordHistory()
} else { } else {
val newLine = line.substring(0, cursorCharacter - 1) + line.substring(cursorCharacter) val newLine = line.substring(0, cursorCharacter - 1) + line.substring(cursorCharacter)
moveCursors(cursorLine, cursorCharacter, -1) moveCursors(cursorLine, cursorCharacter, -1)
this[cursorLine] = newLine this[cursorLine] = newLine
recordHistory()
} }
recordHistory()
triggerChangeCallback()
} else if (cursorLine > 0) { } else if (cursorLine > 0) {
cursorLine-- cursorLine--
recordHistory() recordHistory()
@ -751,7 +796,7 @@ open class TextInputPanel<out S : Screen>(
} }
if (cursorLine !in 0 until lines.size) { if (cursorLine !in 0 until lines.size) {
cursorLine = lines.size - 1 cursorLine = (lines.size - 1).coerceAtLeast(0)
return true return true
} }
@ -764,6 +809,7 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = 0 cursorCharacter = 0
recordHistory(true) recordHistory(true)
triggerChangeCallback()
return true return true
} }
@ -780,8 +826,6 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = line.length cursorCharacter = line.length
this[cursorLine] = line + bottomLine this[cursorLine] = line + bottomLine
removeLine(cursorLine + 1) removeLine(cursorLine + 1)
recordHistory()
} else { } else {
pushbackSnapshotIfNoTimer() pushbackSnapshotIfNoTimer()
@ -791,10 +835,10 @@ open class TextInputPanel<out S : Screen>(
if (cursorCharacter != 0) if (cursorCharacter != 0)
this.cursorCharacter++ this.cursorCharacter++
recordHistory()
} }
recordHistory()
triggerChangeCallback()
return true return true
} }
@ -860,6 +904,7 @@ open class TextInputPanel<out S : Screen>(
} }
pushbackSnapshot() pushbackSnapshot()
triggerChangeCallback()
return true return true
} }
@ -924,6 +969,10 @@ open class TextInputPanel<out S : Screen>(
override fun charTypedInternal(codepoint: Char, mods: Int): Boolean { override fun charTypedInternal(codepoint: Char, mods: Int): Boolean {
wipeSelection() wipeSelection()
if (!multiLine)
cursorLine = 0
var line = this[cursorLine] var line = this[cursorLine]
if (line == null) { if (line == null) {
@ -942,6 +991,7 @@ open class TextInputPanel<out S : Screen>(
set(cursorLine, line) set(cursorLine, line)
moveCursors(cursorLine, cursorCharacter, 1) moveCursors(cursorLine, cursorCharacter, 1)
recordHistory() recordHistory()
triggerChangeCallback()
return true return true
} }
@ -1171,6 +1221,16 @@ open class TextInputPanel<out S : Screen>(
return true return true
} }
protected open fun onTextChanged(old: String, new: String) {
changeCallback?.invoke(old, new)
}
protected var changeCallback: ((old: String, new: String) -> Unit)? = null
fun onTextChange(callback: (old: String, new: String) -> Unit) {
changeCallback = callback
}
private enum class CharType { private enum class CharType {
SPACES { SPACES {
override fun contains(input: Char): Boolean { override fun contains(input: Char): Boolean {

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.menu
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.Slot import net.minecraft.world.inventory.Slot
import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
import ru.dbotthepony.mc.otm.registry.MMenus import ru.dbotthepony.mc.otm.registry.MMenus
class HoloSignMenu @JvmOverloads constructor( class HoloSignMenu @JvmOverloads constructor(
@ -12,6 +13,11 @@ class HoloSignMenu @JvmOverloads constructor(
) : MatteryMenu(MMenus.HOLO_SIGN, containerId, inventory, tile) { ) : MatteryMenu(MMenus.HOLO_SIGN, containerId, inventory, tile) {
var text by mSynchronizer.string(name = "text") var text by mSynchronizer.string(name = "text")
val textInput = PlayerInput(BinaryStringCodec) {
if (tile is HoloSignBlockEntity)
tile.text = it
}
override val storageSlots: Collection<Slot> override val storageSlots: Collection<Slot>
get() = listOf() get() = listOf()

View File

@ -2,7 +2,10 @@ package ru.dbotthepony.mc.otm.menu
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.mojang.datafixers.util.Pair import com.mojang.datafixers.util.Pair
import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container import net.minecraft.world.Container
@ -12,17 +15,26 @@ import net.minecraft.world.inventory.*
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.enchantment.EnchantmentHelper.hasBindingCurse import net.minecraft.world.item.enchantment.EnchantmentHelper.hasBindingCurse
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.core.util.IStreamCodec
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
import ru.dbotthepony.mc.otm.network.FieldSynchronizer import ru.dbotthepony.mc.otm.network.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.network.MenuFieldPacket import ru.dbotthepony.mc.otm.network.MenuFieldPacket
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
import ru.dbotthepony.mc.otm.network.packetHandled
import ru.dbotthepony.mc.otm.network.sender
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.* import java.util.*
import java.util.function.Supplier
import kotlin.collections.ArrayList
abstract class MatteryMenu @JvmOverloads protected constructor( abstract class MatteryMenu @JvmOverloads protected constructor(
menuType: MenuType<*>?, menuType: MenuType<*>?,
@ -43,6 +55,45 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
private val _playerExoSuitSlots = ArrayList<MatterySlot>() private val _playerExoSuitSlots = ArrayList<MatterySlot>()
private val _playerCombinedInventorySlots = ArrayList<MatterySlot>() private val _playerCombinedInventorySlots = ArrayList<MatterySlot>()
private val playerInputs = ArrayList<PlayerInput<*>>()
class PlayerInputPacket(val containerId: Int, val inputId: Int, val payload: ByteArray) : MatteryPacket {
constructor(buff: FriendlyByteBuf) : this(buff.readVarInt(), buff.readVarInt(), ByteArray(buff.readableBytes()).also { buff.readBytes(it) })
override fun write(buff: FriendlyByteBuf) {
buff.writeVarInt(containerId)
buff.writeVarInt(inputId)
buff.writeBytes(payload)
}
override fun play(context: Supplier<NetworkEvent.Context>) {
context.packetHandled = true
val menu = context.sender?.containerMenu as? MatteryMenu ?: return
if (menu.containerId != containerId) return
val input = menu.playerInputs.getOrNull(inputId) ?: return
if (!input.allowSpectators && context.sender!!.isSpectator) return
input.invoke(input.codec.read(DataInputStream(FastByteArrayInputStream(payload))))
}
}
inner class PlayerInput<V>(val codec: IStreamCodec<V>, val allowSpectators: Boolean = false, val handler: (V) -> Unit) {
val id = playerInputs.size
init {
playerInputs.add(this)
}
fun input(value: V) {
val stream = FastByteArrayOutputStream()
codec.write(DataOutputStream(stream), value)
MenuNetworkChannel.sendToServer(PlayerInputPacket(containerId, id, stream.array.copyOfRange(0, stream.length)))
}
internal fun invoke(value: Any?) {
handler.invoke(value as V)
}
}
/** /**
* inventory + exosuit + hotbar (in this order) * inventory + exosuit + hotbar (in this order)
*/ */

View File

@ -65,7 +65,7 @@ class SetCarriedPacket(val item: ItemStack) : MatteryPacket {
} }
object MenuNetworkChannel : MatteryNetworkChannel( object MenuNetworkChannel : MatteryNetworkChannel(
version = "1", version = "2",
name = "menu" name = "menu"
) { ) {
fun register() { fun register() {
@ -86,6 +86,7 @@ object MenuNetworkChannel : MatteryNetworkChannel(
add(NumberPlayerInputPacket::class.java, NumberPlayerInputPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER) add(NumberPlayerInputPacket::class.java, NumberPlayerInputPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
add(OneWayPlayerInputPacket::class.java, OneWayPlayerInputPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER) add(OneWayPlayerInputPacket::class.java, OneWayPlayerInputPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
add(BooleanPlayerInputPacket::class.java, BooleanPlayerInputPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER) add(BooleanPlayerInputPacket::class.java, BooleanPlayerInputPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
add(MatteryMenu.PlayerInputPacket::class.java, MatteryMenu::PlayerInputPacket, NetworkDirection.PLAY_TO_SERVER)
// menu specific // menu specific