Holo sign lock, more streamlined player input API

This commit is contained in:
DBotThePony 2023-01-29 20:02:40 +07:00
parent e505bce76d
commit 19d8eca2e5
Signed by: DBot
GPG Key ID: DCC23B5715498507
11 changed files with 106 additions and 57 deletions

View File

@ -20,6 +20,8 @@ class HoloSignBlockEntity(blockPos: BlockPos, blockState: BlockState) : Synchron
access.write(value)
})
var locked = false
override fun createMenu(p_39954_: Int, p_39955_: Inventory, p_39956_: Player): AbstractContainerMenu {
return HoloSignMenu(p_39954_, p_39955_, this)
}
@ -35,12 +37,35 @@ class HoloSignBlockEntity(blockPos: BlockPos, blockState: BlockState) : Synchron
override fun load(p_155245_: CompoundTag) {
super.load(p_155245_)
text = p_155245_.getString(TEXT_KEY)
locked = p_155245_.getBoolean(LOCKED_KEY)
if (locked) {
text = p_155245_.getString(TEXT_KEY)
} else {
text = truncate(p_155245_.getString(TEXT_KEY))
}
}
companion object {
const val TEXT_KEY = "SignText"
const val LOCKED_KEY = "Locked"
const val DEFAULT_MAX_NEWLINES = 8
const val DEFAULT_MAX_LINE_LENGTH = 15
private val NEWLINES = Regex("\r?\n")
fun truncate(input: String): String {
val lines = input.split(NEWLINES)
val result = ArrayList<String>(lines.size.coerceAtMost(DEFAULT_MAX_NEWLINES))
for (i in 0 until lines.size.coerceAtMost(DEFAULT_MAX_NEWLINES)) {
if (lines[i].length > DEFAULT_MAX_LINE_LENGTH) {
result.add(lines[i].substring(0, DEFAULT_MAX_LINE_LENGTH))
} else {
result.add(lines[i])
}
}
return result.joinToString("\n")
}
}
}

View File

@ -5,7 +5,9 @@ import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
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.button.CheckBoxLabelInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.input.NetworkedStringInputPanel
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu
class HoloSignScreen(menu: HoloSignMenu, inventory: Inventory, title: Component) : MatteryScreen<HoloSignMenu>(menu, title) {
@ -16,6 +18,9 @@ class HoloSignScreen(menu: HoloSignMenu, inventory: Inventory, title: Component)
input.dock = Dock.FILL
input.multiLine = true
val lock = CheckBoxLabelInputPanel(this, frame, menu.locked, TranslatableComponent("otm.gui.lock_holo_screen"))
lock.dock = Dock.BOTTOM
return frame
}
}

View File

@ -3,12 +3,13 @@ package ru.dbotthepony.mc.otm.client.screen.panels.button
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.core.value
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
open class CheckBoxInputPanel<out S : Screen> @JvmOverloads constructor(
screen: S,
parent: EditablePanel<*>?,
val widget: BooleanInputWithFeedback,
val widget: IPlayerInputWithFeedback<Boolean>,
x: Float = 0f,
y: Float = 0f,
width: Float = REGULAR_DIMENSIONS + 120f,
@ -19,10 +20,10 @@ open class CheckBoxInputPanel<out S : Screen> @JvmOverloads constructor(
set(value) {}
override var isDisabled: Boolean
get() = widget.input.allowSpectators || minecraft.player?.isSpectator == true
get() = !widget.test(minecraft.player)
set(value) {}
override fun onClick() {
widget.input(!checked)
widget.accept(!checked)
}
}

View File

@ -5,11 +5,12 @@ import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.Label
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
open class CheckBoxLabelInputPanel<out S : Screen> @JvmOverloads constructor(
screen: S,
parent: EditablePanel<*>?,
widget: BooleanInputWithFeedback,
widget: IPlayerInputWithFeedback<Boolean>,
text: Component,
x: Float = 0f,
y: Float = 0f,
@ -17,14 +18,6 @@ open class CheckBoxLabelInputPanel<out S : Screen> @JvmOverloads constructor(
height: Float = CheckBoxPanel.REGULAR_DIMENSIONS
) : EditablePanel<S>(screen, parent, x, y, width, height) {
val widget get() = checkbox.widget
val checkbox = CheckBoxInputPanel(
screen,
this,
widget,
0f,
0f,
CheckBoxPanel.REGULAR_DIMENSIONS,
CheckBoxPanel.REGULAR_DIMENSIONS
)
val checkbox = CheckBoxInputPanel(screen = screen, parent = this, widget = widget, x = 0f, y = 0f, width = CheckBoxPanel.REGULAR_DIMENSIONS, height = CheckBoxPanel.REGULAR_DIMENSIONS)
val label = Label(screen, this, CheckBoxPanel.REGULAR_DIMENSIONS + 4f, 4f, text = text)
}

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.client.screen.panels.input
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.menu.input.AbstractPlayerInputWithFeedback
import ru.dbotthepony.mc.otm.milliTime
@ -15,13 +16,13 @@ open class NetworkedStringInputPanel<out S : Screen>(
height: Float = 11f,
) : TextInputPanel<S>(screen, parent, x, y, width, height) {
override var isActive: Boolean
get() = backend.check()
get() = backend.test(minecraft.player)
set(value) {}
override fun onFocusChanged(new: Boolean, old: Boolean) {
super.onFocusChanged(new, old)
if (new && !backend.check()) {
if (new && !backend.test(minecraft.player)) {
killFocus()
}
}
@ -30,7 +31,7 @@ open class NetworkedStringInputPanel<out S : Screen>(
override fun onTextChanged(old: String, new: String) {
lastChanges = milliTime + 1000L
backend.input(new)
backend.accept(new)
}
override fun tick() {

View File

@ -19,6 +19,14 @@ interface GetterSetter<V> : Supplier<V>, Consumer<V>, ReadWriteProperty<Any?, V>
return get()
}
operator fun invoke(): V {
return get()
}
operator fun invoke(value: V) {
accept(value)
}
companion object {
fun <V> of(getter: Supplier<V>, setter: Consumer<V>): GetterSetter<V> {
return object : GetterSetter<V> {

View File

@ -39,6 +39,7 @@ import java.io.DataInputStream
import java.io.DataOutputStream
import java.math.BigDecimal
import java.util.*
import java.util.function.Predicate
import java.util.function.Supplier
import kotlin.collections.ArrayList
@ -82,7 +83,7 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
val menu = context.sender?.containerMenu as? MatteryMenu ?: return@enqueueWork
if (menu.containerId != containerId) return@enqueueWork
val input = menu.playerInputs.getOrNull(inputId) ?: return@enqueueWork
if (!input.allowSpectators && context.sender!!.isSpectator) return@enqueueWork
if (!input.test(context.sender)) return@enqueueWork
input.invoke(input.codec.read(DataInputStream(FastByteArrayInputStream(payload))))
}
}
@ -91,7 +92,7 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
/**
* Client->Server handler
*/
inner class PlayerInput<V>(val codec: IStreamCodec<V>, allowSpectators: Boolean = false, val handler: (V) -> Unit) {
inner class PlayerInput<V>(val codec: IStreamCodec<V>, allowSpectators: Boolean = false, val handler: (V) -> Unit) : Predicate<Player?> {
val id = playerInputs.size
var allowSpectators by mSynchronizer.bool(allowSpectators)
@ -99,6 +100,22 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
playerInputs.add(this)
}
private val filters = ArrayList<Predicate<Player>>()
init {
filters.add { allowSpectators || !it.isSpectator }
}
fun filter(predicate: Predicate<Player>): PlayerInput<V> {
filters.add(predicate)
return this
}
override fun test(player: Player?): Boolean {
if (player == null) return false
return filters.all { it.test(player) }
}
fun input(value: V) {
val stream = FastByteArrayOutputStream()
codec.write(DataOutputStream(stream), value)

View File

@ -4,6 +4,7 @@ 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.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.StringInputWithFeedback
import ru.dbotthepony.mc.otm.registry.MMenus
@ -12,28 +13,18 @@ class HoloSignMenu @JvmOverloads constructor(
inventory: Inventory,
tile: HoloSignBlockEntity? = null
) : MatteryMenu(MMenus.HOLO_SIGN, containerId, inventory, tile) {
val text = if (tile != null)
StringInputWithFeedback(this).withConsumer {
val lines = it.split(NEWLINES)
val result = ArrayList<String>(lines.size.coerceAtMost(HoloSignBlockEntity.DEFAULT_MAX_NEWLINES))
val text = StringInputWithFeedback(this)
val locked = BooleanInputWithFeedback(this)
for (i in 0 until lines.size.coerceAtMost(HoloSignBlockEntity.DEFAULT_MAX_NEWLINES)) {
if (lines[i].length > HoloSignBlockEntity.DEFAULT_MAX_LINE_LENGTH) {
result.add(lines[i].substring(0, HoloSignBlockEntity.DEFAULT_MAX_LINE_LENGTH))
} else {
result.add(lines[i])
}
}
init {
locked.filter { it.isCreative }
tile.text = result.joinToString("\n")
}.withSupplier(tile::text)
else
StringInputWithFeedback(this)
if (tile != null) {
text.withConsumer { if (tile.locked) tile.text = it else tile.text = HoloSignBlockEntity.truncate(it) }.withSupplier(tile::text)
locked.with(tile::locked)
}
}
override val storageSlots: Collection<Slot>
get() = listOf()
companion object {
private val NEWLINES = Regex("\r?\n")
}
}

View File

@ -4,12 +4,17 @@ import net.minecraft.world.entity.player.Player
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import java.util.function.Consumer
import java.util.function.Predicate
import java.util.function.Supplier
import kotlin.reflect.KMutableProperty0
interface IPlayerInputWithFeedback<V> : GetterSetter<V>, Predicate<Player?>
/**
* Getting and setting values should ONLY be done clientside
*/
abstract class AbstractPlayerInputWithFeedback<V> : GetterSetter<V> {
abstract class AbstractPlayerInputWithFeedback<V> : IPlayerInputWithFeedback<V> {
abstract val input: MatteryMenu.PlayerInput<V>
abstract val value: V
@ -21,6 +26,9 @@ abstract class AbstractPlayerInputWithFeedback<V> : GetterSetter<V> {
input.checkedInput(t, minecraft.player as Player?)
}
override fun test(player: Player?) = input.test(player)
fun filter(filter: Predicate<Player>) = input.filter(filter)
var supplier: (() -> V)? = null
var consumer: ((V) -> Unit)? = null
@ -29,11 +37,21 @@ abstract class AbstractPlayerInputWithFeedback<V> : GetterSetter<V> {
return this
}
fun withSupplier(func: Supplier<V>): AbstractPlayerInputWithFeedback<V> {
supplier = func::get
return this
}
fun withConsumer(func: (V) -> Unit): AbstractPlayerInputWithFeedback<V> {
consumer = func
return this
}
fun withConsumer(func: Consumer<V>): AbstractPlayerInputWithFeedback<V> {
consumer = func::accept
return this
}
fun with(state: KMutableProperty0<V>): AbstractPlayerInputWithFeedback<V> {
withConsumer { state.set(it) }
withSupplier { state.get() }
@ -41,8 +59,8 @@ abstract class AbstractPlayerInputWithFeedback<V> : GetterSetter<V> {
}
fun with(state: GetterSetter<V>): AbstractPlayerInputWithFeedback<V> {
withConsumer(state::accept)
withSupplier(state::get)
withConsumer(state)
withSupplier(state)
return this
}
@ -51,18 +69,4 @@ abstract class AbstractPlayerInputWithFeedback<V> : GetterSetter<V> {
consumer = null
return this
}
/**
* shortcut to checked input of [input]
*/
fun input(newValue: V) {
input.checkedInput(newValue, minecraft.player as Player?)
}
/**
* checks if local (client) player can modify this input
*/
fun check(): Boolean {
return input.allowSpectators || (minecraft.player as Player?)?.isSpectator == false
}
}

View File

@ -17,6 +17,6 @@ class BooleanInputWithFeedback(menu: MatteryMenu) : AbstractPlayerInputWithFeedb
}
fun switchValue() {
input(!value)
accept(!value)
}
}

View File

@ -99,6 +99,10 @@ sealed interface IMutableField<V> : IField<V>, GetterSetter<V> {
override fun accept(t: V) {
value = t
}
override fun invoke(): V {
return this.value
}
}
data class MapChangeset<out K, out V>(