rendering: wip: chat field

This commit is contained in:
Bixilon 2021-04-10 18:30:23 +02:00
parent c1fa6d5829
commit 4866528802
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
15 changed files with 351 additions and 23 deletions

View File

@ -52,11 +52,16 @@ object KeyBindingsNames {
val WHEN_IN_GAME = ResourceLocation("minosoft:in_game")
val WHEN_PLAYER_IS_FLYING = ResourceLocation("minosoft:is_flying")
val WHEN_IN_CHAT = ResourceLocation("minosoft:in_chat")
val TAKE_SCREENSHOT = ResourceLocation("minosoft:take_screenshot")
val TOGGLE_HUD = ResourceLocation("minosoft:toggle_hud")
val OPEN_CHAT = ResourceLocation("minosoft:open_chat")
val CLOSE_CHAT = ResourceLocation("minosoft:close_chat")
val SELECT_HOTBAR_SLOTS = arrayOf(ResourceLocation("minosoft:select_hotbar_slot_1"),
ResourceLocation("minosoft:select_hotbar_slot_2"),
@ -217,5 +222,17 @@ object KeyBindingsNames {
),
mutableSetOf(mutableSetOf(WHEN_IN_GAME))
),
OPEN_CHAT to KeyBinding(
mutableMapOf(
KeyAction.PRESS to mutableSetOf(KeyCodes.KEY_T)
),
mutableSetOf(mutableSetOf(WHEN_IN_GAME))
),
CLOSE_CHAT to KeyBinding(
mutableMapOf(
KeyAction.PRESS to mutableSetOf(KeyCodes.KEY_ESCAPE)
),
mutableSetOf(mutableSetOf(WHEN_IN_CHAT))
),
)
}

View File

@ -25,4 +25,5 @@ object ElementsNames {
val CROSSHAIR_RESOURCE_LOCATION = ResourceLocation("minosoft:crosshair")
val WORLD_DEBUG_SCREEN_RESOURCE_LOCATION = ResourceLocation("minosoft:world_debug_screen")
val SYSTEM_DEBUG_SCREEN_RESOURCE_LOCATION = ResourceLocation("minosoft:system_debug_screen")
val CHAT_RESOURCE_LOCATION = ResourceLocation("minosoft:chat")
}

View File

@ -216,8 +216,7 @@ open class TextComponent : ChatComponent {
for (char in charArray) {
if (char == '\n') {
offset.x = 0
offset.y = 0
val yOffset = offset.y + Font.CHAR_HEIGHT + RenderConstants.TEXT_LINE_PADDING
val yOffset = Font.CHAR_HEIGHT + RenderConstants.TEXT_LINE_PADDING
offset.y += yOffset
retMaxSize.y += yOffset
continue

View File

@ -125,9 +125,9 @@ class World : BiomeAccessor {
fun getBlocks(start: Vec3i, end: Vec3i): Map<Vec3i, BlockState> {
val blocks: MutableMap<Vec3i, BlockState> = mutableMapOf()
for (z in start.z until end.z) {
for (y in start.y until end.y) {
for (x in start.x until end.x) {
for (z in start.z..end.z) {
for (y in start.y..end.y) {
for (x in start.x..end.x) {
val blockPosition = Vec3i(x, y, z)
getBlockState(blockPosition)?.let {
blocks[blockPosition] = it

View File

@ -95,6 +95,9 @@ class Camera(
var yOffset = yPos - this.lastMouseY
lastMouseX = xPos
lastMouseY = yPos
if (!renderWindow.currentElement.contains(KeyBindingsNames.WHEN_IN_GAME)) {
return
}
xOffset *= mouseSensitivity
yOffset *= mouseSensitivity
var yaw = xOffset.toFloat() + playerEntity.rotation.headYaw
@ -145,7 +148,7 @@ class Camera(
fun handleInput(deltaTime: Double) {
var cameraSpeed = movementSpeed * deltaTime
val movementFront = Vec3(cameraFront)
if (! Minosoft.getConfig().config.game.camera.noCipMovement) {
if (!Minosoft.getConfig().config.game.camera.noCipMovement) {
movementFront.y = 0.0f
movementFront.normalizeAssign() // when moving forwards, do not move down
}

View File

@ -24,6 +24,7 @@ import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.chunk.WorldRenderer
import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.hud.elements.input.KeyConsumer
import de.bixilon.minosoft.gui.rendering.textures.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
import de.bixilon.minosoft.gui.rendering.util.ScreenshotTaker
@ -36,10 +37,9 @@ import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.logging.Log
import glm_.vec2.Vec2
import glm_.vec2.Vec2i
import org.lwjgl.*
import org.lwjgl.glfw.*
import org.lwjgl.glfw.GLFW.*
import org.lwjgl.opengl.*
import org.lwjgl.opengl.GL
import org.lwjgl.opengl.GL11.*
import org.lwjgl.system.MemoryStack
import org.lwjgl.system.MemoryUtil
@ -81,6 +81,30 @@ class RenderWindow(
val renderQueue = ConcurrentLinkedQueue<Runnable>()
private var _currentInputConsumer: KeyConsumer? = null
val currentElement: MutableList<ResourceLocation> = mutableListOf(KeyBindingsNames.WHEN_IN_GAME, KeyBindingsNames.WHEN_PLAYER_IS_FLYING)
private var skipNextChatPress = false
var currentInputConsumer: KeyConsumer?
get() = _currentInputConsumer
set(value) {
_currentInputConsumer = value
for ((_, binding) in keyBindingCallbacks) {
if (!keyBindingDown.contains(binding.first)) {
continue
}
if (!binding.first.action.containsKey(KeyAction.TOGGLE) && !binding.first.action.containsKey(KeyAction.CHANGE)) {
continue
}
for (keyCallback in binding.second) {
keyCallback.invoke(KeyCodes.KEY_UNKNOWN, KeyAction.RELEASE)
}
}
keyBindingDown.clear()
}
init {
connection.registerEvent(EventInvokerCallback<ConnectionStateChangeEvent> {
if (it.connection.isDisconnected) {
@ -104,7 +128,6 @@ class RenderWindow(
})
}
fun init(latch: CountUpAndDownLatch) {
// Setup an error callback. The default implementation
// will print the error message in System.err.
@ -146,12 +169,37 @@ class RenderWindow(
keysDown.remove(keyCode)
}
if (keyAction == KeyAction.PRESS) {
// ToDo: Repeatable keys, long holding, etc
currentInputConsumer?.keyInput(keyCode)
}
for ((_, keyCallbackPair) in keyBindingCallbacks) {
run {
val keyBinding = keyCallbackPair.first
val keyCallbacks = keyCallbackPair.second
var anyCheckRun = false
var andWhenValid = false
for (or in keyBinding.`when`) {
var andValid = true
for (and in or) {
if (!currentElement.contains(and)) {
andValid = false
break
}
}
if (andValid) {
andWhenValid = true
break
}
}
if (!andWhenValid) {
return@run
}
keyBinding.action[KeyAction.MODIFIER]?.let {
val previousKeysDown = if (keyAction == KeyAction.RELEASE) {
val previousKeysDown = keysDown.toMutableList()
@ -196,11 +244,19 @@ class RenderWindow(
}
for (keyCallback in keyCallbacks) {
keyCallback.invoke(keyCode, keyAction)
skipNextChatPress = true
}
}
}
}
glfwSetCharCallback(windowId) { _: Long, char: Int ->
if (skipNextChatPress) {
skipNextChatPress = false
return@glfwSetCharCallback
}
currentInputConsumer?.charInput(char.toChar())
}
if (mouseCatch) {
glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED)

View File

@ -19,8 +19,8 @@ import glm_.vec2.Vec2
data class HUDElementProperties(
val position: Vec2,
@Json(name = "x_binding") val xBinding: PositionBindings,
@Json(name = "y_binding") val yBinding: PositionBindings,
@Json(name = "x_binding") val xBinding: PositionBindings = PositionBindings.FURTHEST_POINT_AWAY,
@Json(name = "y_binding") val yBinding: PositionBindings = PositionBindings.FURTHEST_POINT_AWAY,
@Json(name = "toggle_key_binding") var toggleKeyBinding: ResourceLocation? = null,
val scale: Float = 1.0f,
var enabled: Boolean = true,

View File

@ -22,6 +22,7 @@ import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.Renderer
import de.bixilon.minosoft.gui.rendering.hud.atlas.HUDAtlasElement
import de.bixilon.minosoft.gui.rendering.hud.elements.HUDElement
import de.bixilon.minosoft.gui.rendering.hud.elements.chat.ChatBoxHUDElement
import de.bixilon.minosoft.gui.rendering.hud.elements.debug.HUDSystemDebugElement
import de.bixilon.minosoft.gui.rendering.hud.elements.debug.HUDWorldDebugElement
import de.bixilon.minosoft.gui.rendering.hud.elements.other.CrosshairHUDElement
@ -75,7 +76,6 @@ class HUDRenderer(val connection: PlayConnection, val renderWindow: RenderWindow
addElement(ElementsNames.HOTBAR_RESOURCE_LOCATION, HotbarHUDElement(this), HUDElementProperties(
position = Vec2(0.0f, -1.0f),
xBinding = HUDElementProperties.PositionBindings.CENTER,
yBinding = HUDElementProperties.PositionBindings.FURTHEST_POINT_AWAY,
))
addElement(ElementsNames.CROSSHAIR_RESOURCE_LOCATION, CrosshairHUDElement(this), HUDElementProperties(
@ -85,18 +85,17 @@ class HUDRenderer(val connection: PlayConnection, val renderWindow: RenderWindow
))
addElement(ElementsNames.WORLD_DEBUG_SCREEN_RESOURCE_LOCATION, HUDWorldDebugElement(this), HUDElementProperties(
position = Vec2(-1.0f, 1.0f),
xBinding = HUDElementProperties.PositionBindings.FURTHEST_POINT_AWAY,
yBinding = HUDElementProperties.PositionBindings.FURTHEST_POINT_AWAY,
toggleKeyBinding = KeyBindingsNames.TOGGLE_DEBUG_SCREEN,
enabled = false,
))
addElement(ElementsNames.SYSTEM_DEBUG_SCREEN_RESOURCE_LOCATION, HUDSystemDebugElement(this), HUDElementProperties(
position = Vec2(1.0f, 1.0f),
xBinding = HUDElementProperties.PositionBindings.FURTHEST_POINT_AWAY,
yBinding = HUDElementProperties.PositionBindings.FURTHEST_POINT_AWAY,
toggleKeyBinding = KeyBindingsNames.TOGGLE_DEBUG_SCREEN,
enabled = false,
))
addElement(ElementsNames.CHAT_RESOURCE_LOCATION, ChatBoxHUDElement(this), HUDElementProperties(
position = Vec2(0f, -1.0f),
))
}
fun addElement(resourceLocation: ResourceLocation, hudElement: HUDElement, defaultProperties: HUDElementProperties) {

View File

@ -0,0 +1,58 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.hud.elements.chat
import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.hud.elements.HUDElement
import de.bixilon.minosoft.gui.rendering.hud.elements.input.SubmittableTextField
class ChatBoxHUDElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer) {
private lateinit var inputField: SubmittableTextField
override fun init() {
inputField = SubmittableTextField(font = hudRenderer.renderWindow.font, z = 100, maxLength = 256, onSubmit = {
try {
hudRenderer.renderWindow.connection.sender.sendChatMessage(it)
closeChat()
return@SubmittableTextField true
} catch (exception: Exception) {
closeChat()
return@SubmittableTextField false
}
})
layout.addChild(inputField)
hudRenderer.renderWindow.registerKeyCallback(KeyBindingsNames.OPEN_CHAT) { _, _ ->
openChat()
}
hudRenderer.renderWindow.registerKeyCallback(KeyBindingsNames.CLOSE_CHAT) { _, _ ->
closeChat()
}
}
fun openChat() {
hudRenderer.renderWindow.currentInputConsumer = inputField
hudRenderer.renderWindow.currentElement.remove(KeyBindingsNames.WHEN_IN_GAME)
hudRenderer.renderWindow.currentElement.add(KeyBindingsNames.WHEN_IN_CHAT)
}
fun closeChat() {
inputField.clearText()
hudRenderer.renderWindow.currentInputConsumer = null
hudRenderer.renderWindow.currentElement.remove(KeyBindingsNames.WHEN_IN_CHAT)
hudRenderer.renderWindow.currentElement.add(KeyBindingsNames.WHEN_IN_GAME)
}
}

View File

@ -0,0 +1,26 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.hud.elements.input
import de.bixilon.minosoft.config.key.KeyCodes
interface KeyConsumer {
val focused: Boolean
fun charInput(char: Char) {}
fun keyInput(keyCodes: KeyCodes) {
}
}

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.hud.elements.input
import glm_.vec2.Vec2i
interface MouseConsumer {
val focused: Boolean
fun mouseMove(position: Vec2i) {}
}

View File

@ -0,0 +1,46 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.hud.elements.input
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.font.Font
import glm_.vec2.Vec2i
class SubmittableTextField(
start: Vec2i = Vec2i(0, 0),
z: Int = 0,
font: Font,
defaultText: String = "",
maxLength: Int = 256,
private val onSubmit: (text: String) -> Boolean,
) : TextField(start, z, font, defaultText, maxLength) {
fun submit() {
if (!onSubmit.invoke(text)) {
// failed
return
}
text = ""
}
override fun keyInput(keyCodes: KeyCodes) {
if (keyCodes == KeyCodes.KEY_ENTER) {
// submit
submit()
return
}
super.keyInput(keyCodes)
}
}

View File

@ -0,0 +1,101 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.hud.elements.input
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.Layout
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.TextElement
import de.bixilon.minosoft.util.MMath
import glm_.vec2.Vec2i
open class TextField(
start: Vec2i = Vec2i(0, 0),
z: Int = 0,
font: Font,
defaultText: String = "",
val maxLength: Int = 256,
) : Layout(start, z), KeyConsumer, MouseConsumer {
override var focused: Boolean = true
private var textBuilder: StringBuilder = StringBuilder(defaultText)
private val textElement = TextElement(ChatComponent.valueOf(text), font)
private var position = text.length
var text: String
get() = textBuilder.toString()
set(value) {
position = value.length
textBuilder = StringBuilder(value)
update()
}
init {
addChild(textElement)
}
fun clearText() {
textBuilder.clear()
update()
}
private fun update() {
textElement.text = ChatComponent.valueOf(text)
}
override fun keyInput(keyCodes: KeyCodes) {
when (keyCodes) {
KeyCodes.KEY_BACKSPACE -> {
if (textBuilder.isEmpty()) {
return
}
textBuilder.deleteCharAt(--position)
}
KeyCodes.KEY_DELETE -> {
if (textBuilder.isEmpty() || position == textBuilder.length) {
return
}
textBuilder.deleteCharAt(position)
}
KeyCodes.KEY_ENTER -> {
if (position > maxLength) {
return
}
textBuilder.insert(position++, '\n')
}
KeyCodes.KEY_LEFT -> {
position = MMath.clamp(position - 1, 0, text.length)
return
}
KeyCodes.KEY_RIGHT -> {
position = MMath.clamp(position + 1, 0, text.length)
return
}
// ToDo: Up and down for line breaks, shift and ctrl modifier, ...
else -> {
return
}
}
update()
super.keyInput(keyCodes)
}
override fun charInput(char: Char) {
if (position >= maxLength) {
return
}
textBuilder.insert(position++, char.toString())
update()
}
}

View File

@ -34,7 +34,6 @@ import glm_.vec2.Vec2i
class HotbarHUDElement(
hudRender: HUDRenderer,
) : HUDElement(hudRender) {
private lateinit var hotbarBase: HotbarBaseElement
private lateinit var experienceBar: ProgressBar
@ -55,7 +54,6 @@ class HotbarHUDElement(
z = 1,
)
levelText = TextElement(
font = hudRenderer.renderWindow.font,
background = false,
@ -110,7 +108,6 @@ class HotbarHUDElement(
healthBar.prepare()
prepare()
})
}
private fun prepare() {
@ -166,7 +163,7 @@ class HotbarHUDElement(
private val frame = ImageElement(textureLike = frameHUDAtlasElement, z = 2)
init {
fakeX = base.size.x.toInt()
fakeX = base.size.x
addChild(base)
addChild(frame)
}

View File

@ -33,7 +33,7 @@ import org.checkerframework.common.value.qual.IntRange;
import java.util.UUID;
public class PacketSender {
public static final String[] ILLEGAL_CHAT_CHARS = {"§"};
public static final char[] ILLEGAL_CHAT_CHARS = {'§', '\n', '\r'};
private final PlayConnection connection;
public PacketSender(PlayConnection connection) {
@ -45,8 +45,11 @@ public class PacketSender {
}
public void sendChatMessage(String message) {
for (String illegalChar : ILLEGAL_CHAT_CHARS) {
if (message.contains(illegalChar)) {
if (message.isBlank()) {
throw new IllegalArgumentException(("Chat message is blank!"));
}
for (char illegalChar : ILLEGAL_CHAT_CHARS) {
if (message.indexOf(illegalChar) != -1) {
throw new IllegalArgumentException(String.format("%s is not allowed in chat", illegalChar));
}
}