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.TextInputPanel
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) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel(this, null, 0f, 0f, 200f, 200f, getTitle())
val textbox = TextInputPanel(this, frame)
textbox.dock = Dock.FILL
textbox.multiLine = true
textbox.text = "МОГУС\nБОНУС\n\nСУС"
object : TextInputPanel<HoloSignScreen>(this@HoloSignScreen, frame) {
init {
dock = Dock.FILL
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
}

View File

@ -80,14 +80,24 @@ open class TextInputPanel<out S : Screen>(
private val cursorLine = this@TextInputPanel.cursorLine
private val cursorCharacter = this@TextInputPanel.cursorCharacter
private val selections = Int2ObjectAVLTreeMap(this@TextInputPanel.selections)
private val multiLine = this@TextInputPanel.multiLine
fun apply() {
this@TextInputPanel.lines.clear()
this@TextInputPanel.lines.addAll(lines)
if (this@TextInputPanel.multiLine)
this@TextInputPanel.lines.addAll(lines)
else
this@TextInputPanel.lines.add(lines.joinToString(""))
this@TextInputPanel.selections.clear()
this@TextInputPanel.selections.putAll(selections)
if (this@TextInputPanel.multiLine && multiLine)
this@TextInputPanel.selections.putAll(selections)
this@TextInputPanel.cursorCharacter = cursorCharacter
this@TextInputPanel.cursorLine = cursorLine
triggerChangeCallback()
}
override fun equals(other: Any?): Boolean {
@ -114,18 +124,41 @@ open class TextInputPanel<out S : Screen>(
var debugDraw = 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 cursorCharacter = 0
var textColor = RGBAColor.WHITE
var cursorColor = RGBAColor.GREEN
var backgroundColor = RGBAColor.BLACK
private var oldText = ArrayList<String>()
private var textCache: String? = null
private val lines = ArrayList<String>()
private val selections = Int2ObjectAVLTreeMap<TextSelection>()
private val undo = 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
override fun tick() {
@ -214,9 +247,11 @@ open class TextInputPanel<out S : Screen>(
}
operator fun set(index: Int, value: String) {
if (index < 0) {
if (index < 0)
throw IndexOutOfBoundsException("negative index $index")
}
if (!multiLine && index != 0)
throw IllegalStateException("Not accepting newlines")
lines.ensureCapacity(index)
@ -241,6 +276,9 @@ open class TextInputPanel<out S : Screen>(
}
fun insertLine(index: Int, value: String = "") {
if (!multiLine && lines.isNotEmpty())
throw IllegalStateException("Not accepting newlines")
lines.ensureCapacity(index)
while (lines.size < index) {
@ -262,9 +300,8 @@ open class TextInputPanel<out S : Screen>(
}
fun removeLine(index: Int): Boolean {
if (index < 0 || index >= lines.size) {
if (index < 0 || index >= lines.size)
return false
}
if (cursorLine == index) {
if (index != 0)
@ -363,6 +400,8 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = selection.start
}
}
triggerChangeCallback()
}
private fun textCache(): String {
@ -391,6 +430,7 @@ open class TextInputPanel<out S : Screen>(
undo.clear()
cursorLine = 0
cursorCharacter = 0
textCache = null
if (multiLine) {
lines.addAll(value.split(NEWLINES))
@ -636,9 +676,12 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = 0
}
triggerChangeCallback()
return true
} else {
killFocus()
triggerChangeCallback()
return true
}
}
@ -718,6 +761,7 @@ open class TextInputPanel<out S : Screen>(
}
recordHistory()
triggerChangeCallback()
}
} else {
if (line != null) {
@ -728,13 +772,14 @@ open class TextInputPanel<out S : Screen>(
val newLine = line.substring(0, line.length - 1)
moveCursors(cursorLine, cursorCharacter, -1)
this[cursorLine] = newLine
recordHistory()
} else {
val newLine = line.substring(0, cursorCharacter - 1) + line.substring(cursorCharacter)
moveCursors(cursorLine, cursorCharacter, -1)
this[cursorLine] = newLine
recordHistory()
}
recordHistory()
triggerChangeCallback()
} else if (cursorLine > 0) {
cursorLine--
recordHistory()
@ -751,7 +796,7 @@ open class TextInputPanel<out S : Screen>(
}
if (cursorLine !in 0 until lines.size) {
cursorLine = lines.size - 1
cursorLine = (lines.size - 1).coerceAtLeast(0)
return true
}
@ -764,6 +809,7 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = 0
recordHistory(true)
triggerChangeCallback()
return true
}
@ -780,8 +826,6 @@ open class TextInputPanel<out S : Screen>(
cursorCharacter = line.length
this[cursorLine] = line + bottomLine
removeLine(cursorLine + 1)
recordHistory()
} else {
pushbackSnapshotIfNoTimer()
@ -791,10 +835,10 @@ open class TextInputPanel<out S : Screen>(
if (cursorCharacter != 0)
this.cursorCharacter++
recordHistory()
}
recordHistory()
triggerChangeCallback()
return true
}
@ -860,6 +904,7 @@ open class TextInputPanel<out S : Screen>(
}
pushbackSnapshot()
triggerChangeCallback()
return true
}
@ -924,6 +969,10 @@ open class TextInputPanel<out S : Screen>(
override fun charTypedInternal(codepoint: Char, mods: Int): Boolean {
wipeSelection()
if (!multiLine)
cursorLine = 0
var line = this[cursorLine]
if (line == null) {
@ -942,6 +991,7 @@ open class TextInputPanel<out S : Screen>(
set(cursorLine, line)
moveCursors(cursorLine, cursorCharacter, 1)
recordHistory()
triggerChangeCallback()
return true
}
@ -1171,6 +1221,16 @@ open class TextInputPanel<out S : Screen>(
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 {
SPACES {
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.inventory.Slot
import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
import ru.dbotthepony.mc.otm.registry.MMenus
class HoloSignMenu @JvmOverloads constructor(
@ -12,6 +13,11 @@ class HoloSignMenu @JvmOverloads constructor(
) : MatteryMenu(MMenus.HOLO_SIGN, containerId, inventory, tile) {
var text by mSynchronizer.string(name = "text")
val textInput = PlayerInput(BinaryStringCodec) {
if (tile is HoloSignBlockEntity)
tile.text = it
}
override val storageSlots: Collection<Slot>
get() = listOf()

View File

@ -2,7 +2,10 @@ package ru.dbotthepony.mc.otm.menu
import com.google.common.collect.ImmutableList
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 net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
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.enchantment.EnchantmentHelper.hasBindingCurse
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
import ru.dbotthepony.mc.otm.container.ItemFilter
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.network.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
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.function.Supplier
import kotlin.collections.ArrayList
abstract class MatteryMenu @JvmOverloads protected constructor(
menuType: MenuType<*>?,
@ -43,6 +55,45 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
private val _playerExoSuitSlots = 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)
*/

View File

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