diff --git a/gradle.properties b/gradle.properties index b7aa154..e1a57dd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,15 +5,15 @@ org.gradle.jvmargs=-Xmx1G kotlin.code.style=official # Fabric Properties -minecraft_version=1.21 -yarn_mappings=1.21+build.9 -loader_version=0.15.11 +# https://fabricmc.net/develop/ +minecraft_version=1.16.5 +yarn_mappings=1.16.5+build.10 +loader_version=0.16.5 -# Mod Properties - mod_version = 1.0-SNAPSHOT - maven_group = com.yhs0602 - archives_base_name = craftground +# Fabric API +fabric_version=0.42.0+1.16 -# Dependencies - # check this on https://modmuss50.me/fabric.html - fabric_version=0.100.6+1.21 +# Mod Properties +mod_version = 1.0-SNAPSHOT +maven_group = com.yhs0602 +archives_base_name = craftground diff --git a/src/main/cpp/framebuffer_capturer.cpp b/src/main/cpp/framebuffer_capturer.cpp index 9f3590c..858bc45 100644 --- a/src/main/cpp/framebuffer_capturer.cpp +++ b/src/main/cpp/framebuffer_capturer.cpp @@ -222,19 +222,21 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_kyhsgeekcode_minecraft_1env_Frameb // Invert y axis int index = ((targetSizeY - pixelY) * targetSizeX + pixelX) * 3; // 픽셀 인덱스 계산 - // 비트맵 값이 2면 검은색(테두리)로 그립니다. - if (cursor[dy][dx] == 2) { - pixels[index] = 0; // Red - pixels[index + 1] = 0; // Green - pixels[index + 2] = 0; // Blue + if (index >= 0 && index + 2 < textureWidth * textureHeight * 3) { + // 비트맵 값이 2면 검은색(테두리)로 그립니다. + if (cursor[dy][dx] == 2) { + pixels[index] = 0; // Red + pixels[index + 1] = 0; // Green + pixels[index + 2] = 0; // Blue + } + // 비트맵 값이 1이면 흰색(내부)로 그립니다. + else if (cursor[dy][dx] == 1) { + pixels[index] = 255; // Red + pixels[index + 1] = 255; // Green + pixels[index + 2] = 255; // Blue + } + // 비트맵 값이 0이면 투명, 기존 픽셀을 유지합니다. } - // 비트맵 값이 1이면 흰색(내부)로 그립니다. - else if (cursor[dy][dx] == 1) { - pixels[index] = 255; // Red - pixels[index + 1] = 255; // Green - pixels[index + 2] = 255; // Blue - } - // 비트맵 값이 0이면 투명, 기존 픽셀을 유지합니다. } } } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/ActionSpace.kt b/src/main/java/com/kyhsgeekcode/minecraft_env/ActionSpace.kt deleted file mode 100644 index dae40a6..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/ActionSpace.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.kyhsgeekcode.minecraft_env - -data class ActionSpace(val action: IntArray, val command: String) \ No newline at end of file diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/CaptureFramebuffer.kt b/src/main/java/com/kyhsgeekcode/minecraft_env/CaptureFramebuffer.kt deleted file mode 100644 index 3fef5f7..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/CaptureFramebuffer.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.kyhsgeekcode.minecraft_env - -import com.google.protobuf.ByteString -import com.mojang.blaze3d.platform.GlConst -import com.mojang.blaze3d.platform.GlStateManager -import com.mojang.blaze3d.systems.RenderSystem -import net.minecraft.client.gl.Framebuffer -import org.lwjgl.opengl.GL11 -import java.awt.RenderingHints -import java.awt.image.BufferedImage -import java.awt.image.ComponentColorModel -import java.awt.image.DataBufferByte -import java.awt.image.Raster -import java.nio.ByteBuffer - - -fun captureFramebuffer( - framebuffer: Framebuffer, - targetSizeX: Int, - targetSizeY: Int -): ByteString { - val i = framebuffer.textureWidth - val j = framebuffer.textureHeight - RenderSystem.bindTexture(framebuffer.colorAttachment) - RenderSystem.assertOnRenderThread() - GlStateManager._pixelStore(GlConst.GL_PACK_ALIGNMENT, 3) - val byteBuffer = ByteBuffer.allocateDirect(3 * i * j) - GL11.glGetTexImage(GlConst.GL_TEXTURE_2D, 0, GlConst.GL_RGB, GlConst.GL_UNSIGNED_BYTE, byteBuffer) - val dataBuffer = DataBufferByte(byteBuffer.array(), 3 * i * j) - val raster = Raster.createInterleavedRaster(dataBuffer, i, j, 3 * i, 3, intArrayOf(2, 1, 0), null) - val cm = ComponentColorModel( - ComponentColorModel.getRGBdefault().colorSpace, - false, - false, - ComponentColorModel.OPAQUE, - DataBufferByte.TYPE_BYTE - ) - val image = BufferedImage(cm, raster, false, null) - // resize if needed - val resizedImage: BufferedImage - if (i != targetSizeX || j != targetSizeY) { - resizedImage = BufferedImage(targetSizeX, targetSizeY, image.type) - val graphics = resizedImage.createGraphics() - graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR) - // resize and mirror - graphics.drawImage(image, 0, targetSizeY, targetSizeX, -targetSizeY, null) - graphics.dispose() - } else { - // TODO: only mirror - resizedImage = image - } - // convert to protobuf ByteString - // FIXME -// return ByteString.copyFrom(resizedImage.bytes) - return ByteString.copyFrom(byteBuffer) -} \ No newline at end of file diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/EnvironmentInitializer.kt b/src/main/java/com/kyhsgeekcode/minecraft_env/EnvironmentInitializer.kt index df0293b..aee1617 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/EnvironmentInitializer.kt +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/EnvironmentInitializer.kt @@ -1,19 +1,18 @@ package com.kyhsgeekcode.minecraft_env import com.kyhsgeekcode.minecraft_env.mixin.ChatVisibleMessageAccessor +import com.kyhsgeekcode.minecraft_env.mixin.MoreOptionsDialogSeedTextFieldAccessor import com.kyhsgeekcode.minecraft_env.mixin.WindowSizeAccessor +import com.kyhsgeekcode.minecraft_env.mixin.WorldListWidgetLevelSummaryAccessor import com.kyhsgeekcode.minecraft_env.proto.InitialEnvironment import com.kyhsgeekcode.minecraft_env.proto.InitialEnvironment.InitialEnvironmentMessage import net.minecraft.client.MinecraftClient -import net.minecraft.client.gui.Element import net.minecraft.client.gui.hud.ChatHud -import net.minecraft.client.gui.screen.MessageScreen import net.minecraft.client.gui.screen.TitleScreen import net.minecraft.client.gui.screen.world.CreateWorldScreen -import net.minecraft.client.gui.screen.world.CustomizeFlatLevelScreen import net.minecraft.client.gui.screen.world.SelectWorldScreen import net.minecraft.client.gui.screen.world.WorldListWidget -import net.minecraft.client.gui.widget.* +import net.minecraft.client.gui.widget.ButtonWidget import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.option.NarratorMode import net.minecraft.client.tutorial.TutorialStep @@ -44,6 +43,12 @@ class EnvironmentInitializer( private lateinit var player: ClientPlayerEntity var hasMinimizedWindow: Boolean = false + var createButton: ButtonWidget? = null + var createWorldButton: ButtonWidget? = null + var cheatButton: ButtonWidget? = null + var moreWorldOptionButton: ButtonWidget? = null + var worldTypeButton: ButtonWidget? = null + fun onClientTick(client: MinecraftClient) { csvLogger.profileStartPrint("Minecraft_env/onInitialize/ClientTick/EnvironmentInitializer/onClientTick") disableNarrator(client) @@ -54,8 +59,9 @@ class EnvironmentInitializer( } val window = MinecraftClient.getInstance().window val windowSizeGetter = (window as WindowSizeAccessor) - if (windowSizeGetter.windowedWidth != initialEnvironment.imageSizeX || windowSizeGetter.windowedHeight != initialEnvironment.imageSizeY) - window.setWindowedSize(initialEnvironment.imageSizeX, initialEnvironment.imageSizeY) + // Skip in 1.16 +// if (windowSizeGetter.windowedWidth != initialEnvironment.imageSizeX || windowSizeGetter.windowedHeight != initialEnvironment.imageSizeY) +// window.set(initialEnvironment.imageSizeX, initialEnvironment.imageSizeY) if (!hasMinimizedWindow) { GLFW.glfwIconifyWindow(window.handle) hasMinimizedWindow = true @@ -65,9 +71,9 @@ class EnvironmentInitializer( setHudHidden(client, initialEnvironment.hudHidden) setRenderDistance(client, initialEnvironment.renderDistance) setSimulationDistance(client, initialEnvironment.simulationDistance) - disableVSync(client) +// disableVSync(client) disableSound(client) - disableTutorial(client) +// disableTutorial(client) setMaxFPSToUnlimited(client) if (initialEnvironment.noFovEffect) { setFovEffectDisabled(client) @@ -99,19 +105,15 @@ class EnvironmentInitializer( } if (levelList != null) { for (child in levelList.children()) { - if (child is WorldListWidget.LoadingEntry) { + if (child !is WorldListWidget.Entry) { return } - if (child is WorldListWidget.WorldEntry) { - if (!child.isLevelSelectable) { - continue - } - if (child.levelDisplayName == levelDisplayName) { - child.play() - return - } else { - println("Level display name: ${child.levelDisplayName}!= $levelDisplayName") - } + val childDisplayName = (child as WorldListWidgetLevelSummaryAccessor).getLevel()?.displayName + if (childDisplayName == levelDisplayName) { + child.play() + return + } else { + println("Level display name: ${childDisplayName}!= $levelDisplayName") } } } else { @@ -119,15 +121,6 @@ class EnvironmentInitializer( } } - is MessageScreen -> { - for (child in screen.children()) { - println("Message screen child: $child") - if (child is NarratedMultilineTextWidget) { - println("Button widget: ${child.message.string}") - } - } - } - is CreateWorldScreen -> { } @@ -155,7 +148,6 @@ class EnvironmentInitializer( // println("Select world screen1") var widget: WorldListWidget? = null var deleteButton: ButtonWidget? = null - var createButton: ButtonWidget? = null for (child in screen.children()) { // println(child) if (child is WorldListWidget) { @@ -173,208 +165,70 @@ class EnvironmentInitializer( is CreateWorldScreen -> { // println("Create world screen") - var createButton: ButtonWidget? = null val cheatRequested = true - var indexOfWorldSettingTab = -1 - var cheatButton: CyclingButtonWidget<*>? = null - var settingTabWidget: TabNavigationWidget? = null - var worldTypeButton: CyclingButtonWidget<*>? = null for (child in screen.children()) { // search for tab navigation widget, to find index of world settings tab - if (indexOfWorldSettingTab == -1 && child is TabNavigationWidget) { - settingTabWidget = child - for (i in child.children().indices) { - val tabChild: Element = child.children()[i] - if (tabChild is TabButtonWidget) { - if (tabChild.message.string == "World") { - indexOfWorldSettingTab = i - } - } - } - } // search for create button - if (createButton == null && child is ButtonWidget) { + if (createWorldButton == null && child is ButtonWidget) { if (child.message.string == "Create New World") { - createButton = child + createWorldButton = child } } // search for cheat button - if (cheatButton == null && child is CyclingButtonWidget<*>) { - if (child.message.string.startsWith("Allow Commands")) { + if (cheatButton == null && child is ButtonWidget) { + if (child.message.string.startsWith("Allow Cheats")) { cheatButton = child - } else { - println("Cheat button is not found, and the text is ${child.message.string}") + println("Cheat button found") } } - } - // Set allow cheats to requested - if (cheatButton != null) - setupAllowCheats(cheatButton, cheatRequested) - else { - println("Cheat button not found") - throw Exception("Cheat button not found") - } - // Select world settings tab - settingTabWidget!!.selectTab(indexOfWorldSettingTab, false) - // Search for seed input - if (initialEnvironment.seed.isNotEmpty()) { - for (child in screen.children()) { - // println(child) - if (child is TextFieldWidget) { - // println("Found text field") - child.text = initialEnvironment.seed.toString() + if (moreWorldOptionButton == null && child is ButtonWidget) { + if (child.message.string.startsWith("More World Options")) { + moreWorldOptionButton = child } } } - if (initialEnvironment.worldType == InitialEnvironment.WorldType.SUPERFLAT) { - for (child in screen.children()) { - // println(child) - if (worldTypeButton == null && child is CyclingButtonWidget<*>) { - if (child.message.string.startsWith("World Type")) { - worldTypeButton = child - } - } - } - if (worldTypeButton != null) { - while (!worldTypeButton.message.string.endsWith("flat")) { - worldTypeButton.onPress() - } - } + if (createWorldButton == null) { + println("Create button not found") + throw Exception("Create button not found") } - createButton?.onPress() - } - } - } - - private fun createEmptyWorldAndEnterUsingGUI(client: MinecraftClient) { - when (val screen = client.currentScreen) { - is TitleScreen -> { - screen.children().find { - it is ButtonWidget && it.message.string == "Singleplayer" - }?.let { - it as ButtonWidget - it.onPress() - return - } - } - - is SelectWorldScreen -> { - // println("Select world screen1") - var widget: WorldListWidget? = null - var deleteButton: ButtonWidget? = null - var createButton: ButtonWidget? = null - for (child in screen.children()) { - // println(child) - if (child is WorldListWidget) { - widget = child - } else if (child is ButtonWidget) { - if (child.message.string == "Delete Selected World") { - deleteButton = child - } else if (child.message.string == "Create New World") { - createButton = child - } - } - } - createButton?.onPress() - } - - is CreateWorldScreen -> { - // println("Create world screen") - var createButton: ButtonWidget? = null - val cheatRequested = true - var indexOfWorldSettingTab = -1 - var cheatButton: CyclingButtonWidget<*>? = null - var settingTabWidget: TabNavigationWidget? = null - var worldTypeButton: CyclingButtonWidget<*>? = null - var customizeFlatmapButton: ButtonWidget? = null - for (child in screen.children()) { - // search for tab navigation widget, to find index of world settings tab - if (indexOfWorldSettingTab == -1 && child is TabNavigationWidget) { - settingTabWidget = child - for (i in child.children().indices) { - val tabChild: Element = child.children()[i] - if (tabChild is TabButtonWidget) { - if (tabChild.message.string == "World") { - indexOfWorldSettingTab = i - } - } - } - } - // search for create button - if (createButton == null && child is ButtonWidget) { - if (child.message.string == "Create New World") { - createButton = child - } + if (cheatRequested) { + cheatButton?.apply { + setupAllowCheats(this, cheatRequested) + } ?: run { + println("Cheat button not found") + throw Exception("Cheat button not found") } - // search for cheat button - if (cheatButton == null && child is CyclingButtonWidget<*>) { - if (child.message.string.startsWith("Allow Commands")) { - cheatButton = child - } else { - println("Cheat button is not found, and the text is ${child.message.string}") - } - } - } - // Set allow cheats to requested - if (cheatButton != null) - setupAllowCheats(cheatButton, cheatRequested) - else { - println("Cheat button not found") - throw Exception("Cheat button not found") } - // Select world settings tab - settingTabWidget!!.selectTab(indexOfWorldSettingTab, false) // Search for seed input - if (initialEnvironment.seed != null) { - for (child in screen.children()) { - // println(child) - if (child is TextFieldWidget) { - // println("Found text field") - child.text = initialEnvironment.seed.toString() - } - } + if (initialEnvironment.seed.isNotEmpty()) { + (screen.moreOptionsDialog as MoreOptionsDialogSeedTextFieldAccessor).seedTextField?.text = + initialEnvironment.seed.toString() + println("Set seed to ${initialEnvironment.seed}") } if (initialEnvironment.worldType == InitialEnvironment.WorldType.SUPERFLAT) { - for (child in screen.children()) { - // println(child) - if (worldTypeButton == null && child is CyclingButtonWidget<*> - && child.message.string.startsWith("World Type") - ) { - worldTypeButton = child - } - if (customizeFlatmapButton == null && child is ButtonWidget && child.message.string.startsWith("Customize")) { - customizeFlatmapButton = child - } - } - if (worldTypeButton != null) { - while (!worldTypeButton.message.string.endsWith("flat")) { - worldTypeButton.onPress() - } - } - if (customizeFlatmapButton != null) { - customizeFlatmapButton.onPress() + val mapTypeButton = + (screen.moreOptionsDialog as MoreOptionsDialogSeedTextFieldAccessor).mapTypeButton + while (!mapTypeButton.message.string.endsWith("flat")) { + mapTypeButton.onPress() } + println("Set world type to superflat") } - createButton?.onPress() - } - - is CustomizeFlatLevelScreen -> { - + createWorldButton?.onPress() + println("Create world button pressed") } } } + private fun disableSound(client: MinecraftClient) { - client.options?.let { - it.getSoundVolumeOption(SoundCategory.MASTER).value = 0.0 - } + client.options?.setSoundVolume(SoundCategory.MASTER, 0.0f) } private fun disableNarrator(client: MinecraftClient) { val options = client.options if (options != null) { - if (options.narrator.value != NarratorMode.OFF) { - options.narrator.value = NarratorMode.OFF + if (options.narrator != NarratorMode.OFF) { + options.narrator = NarratorMode.OFF options.write() println("Disabled narrator") } @@ -388,8 +242,8 @@ class EnvironmentInitializer( private fun disableVSync(client: MinecraftClient) { val options = client.options if (options != null) { - if (options.enableVsync.value) { - options.enableVsync.value = false + if (options.enableVsync) { + options.enableVsync = false client.options.write() println("Disabled VSync") } @@ -397,21 +251,22 @@ class EnvironmentInitializer( } private fun setSimulationDistance(client: MinecraftClient, simulationDistance: Int) { - val options = client.options - if (options != null) { - if (options.simulationDistance.value != simulationDistance) { - options.simulationDistance.value = simulationDistance - client.options.write() - println("Set simulation distance to $simulationDistance") - } - } + // No such option in 1.16 +// val options = client.options +// if (options != null) { +// if (options.viewDistance != simulationDistance) { +// options.viewDistance.value = simulationDistance +// client.options.write() +// println("Set simulation distance to $simulationDistance") +// } +// } } private fun setRenderDistance(client: MinecraftClient, renderDistance: Int) { val options = client.options if (options != null) { - if (options.viewDistance.value != renderDistance) { - options.viewDistance.value = renderDistance + if (options.viewDistance != renderDistance) { + options.viewDistance = renderDistance client.options.write() println("Set render distance to $renderDistance") } @@ -435,7 +290,7 @@ class EnvironmentInitializer( player = MinecraftClient.getInstance().player ?: return val messages = ArrayList((chatHud as ChatVisibleMessageAccessor).visibleMessages) val hasInitFinishMessage = messages.find { - val text = it.content + val text = it.text val builder = StringBuilder() text.accept { index, style, codePoint -> val ch = codePoint.toChar() @@ -485,11 +340,11 @@ class EnvironmentInitializer( // Set the TPS to virtually unlimited private fun setUnlimitedTPS(commandExecutor: (ClientPlayerEntity, String) -> Unit) { - commandExecutor(player, "/tick rate 10000") +// commandExecutor(player, "/tick rate 10000") } private fun setupAllowCheats( - cheatButton: CyclingButtonWidget<*>, + cheatButton: ButtonWidget, cheatRequested: Boolean ) { val testString = if (cheatRequested) "ON" else "OFF" @@ -499,7 +354,7 @@ class EnvironmentInitializer( } private fun setupGameMode( - gameModeButton: CyclingButtonWidget<*>, + gameModeButton: ButtonWidget, gameModeRequested: GameMode ) { val testString = gameModeRequested.name @@ -529,14 +384,15 @@ class EnvironmentInitializer( } private fun disableOnboardAccessibility(client: MinecraftClient) { - val options = client.options - if (options != null) { - if (options.onboardAccessibility) { - println("Disabled onboardAccessibility") - options.onboardAccessibility = false - client.options.write() - } - } + // No such option in 1.16 +// val options = client.options +// if (options != null) { +// if (options.onboardAccessibility) { +// println("Disabled onboardAccessibility") +// options.onboardAccessibility = false +// client.options.write() +// } +// } } private fun setHudHidden(client: MinecraftClient, hudHidden: Boolean) { @@ -556,8 +412,8 @@ class EnvironmentInitializer( private fun setMaxFPSToUnlimited(client: MinecraftClient) { val options = client.options if (options != null) { - if (options.maxFps.value < 260) { // unlimited - options.maxFps.value = 260 + if (options.maxFps < 260) { // unlimited + options.maxFps = 260 client.options.write() println("Set max fps to 260") } @@ -567,8 +423,8 @@ class EnvironmentInitializer( private fun setFovEffectDisabled(client: MinecraftClient) { val options = client.options if (options != null) { - if (options.fovEffectScale.value != 0.0) { - options.fovEffectScale.value = 0.0 + if (options.fovEffectScale != 0.0f) { + options.fovEffectScale = 0.0f client.options.write() println("Disabled fov effect") } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/KeyboardInfo.kt b/src/main/java/com/kyhsgeekcode/minecraft_env/KeyboardInfo.kt new file mode 100644 index 0000000..8542d14 --- /dev/null +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/KeyboardInfo.kt @@ -0,0 +1,84 @@ +package com.kyhsgeekcode.minecraft_env + +import com.kyhsgeekcode.minecraft_env.proto.ActionSpace +import org.lwjgl.glfw.GLFW.* +import org.lwjgl.glfw.GLFWCharModsCallbackI +import org.lwjgl.glfw.GLFWKeyCallbackI + +object KeyboardInfo { + var charModsCallback: GLFWCharModsCallbackI? = null + var keyCallback: GLFWKeyCallbackI? = null + var handle: Long = 0 + + var currentState: MutableMap = mutableMapOf() + + val keyMappings = mapOf( + "W" to GLFW_KEY_W, + "A" to GLFW_KEY_A, + "S" to GLFW_KEY_S, + "D" to GLFW_KEY_D, + "LShift" to GLFW_KEY_LEFT_SHIFT, + "Ctrl" to GLFW_KEY_LEFT_CONTROL, + "Space" to GLFW_KEY_SPACE, + "E" to GLFW_KEY_E, + "Q" to GLFW_KEY_Q, +// "F" to GLFW_KEY_F, + "Hotbar1" to GLFW_KEY_1, + "Hotbar2" to GLFW_KEY_2, + "Hotbar3" to GLFW_KEY_3, + "Hotbar4" to GLFW_KEY_4, + "Hotbar5" to GLFW_KEY_5, + "Hotbar6" to GLFW_KEY_6, + "Hotbar7" to GLFW_KEY_7, + "Hotbar8" to GLFW_KEY_8, + "Hotbar9" to GLFW_KEY_9, + ) + + fun onAction(actionDict: ActionSpace.ActionSpaceMessageV2) { + val actions = mapOf( + "W" to actionDict.forward, + "A" to actionDict.left, + "S" to actionDict.back, + "D" to actionDict.right, + "LShift" to actionDict.sneak, + "Ctrl" to actionDict.sprint, + "Space" to actionDict.jump, + "E" to actionDict.inventory, + "Q" to actionDict.drop, +// "F" to actionDict.swapHands, + "Hotbar1" to actionDict.hotbar1, + "Hotbar2" to actionDict.hotbar2, + "Hotbar3" to actionDict.hotbar3, + "Hotbar4" to actionDict.hotbar4, + "Hotbar5" to actionDict.hotbar5, + "Hotbar6" to actionDict.hotbar6, + "Hotbar7" to actionDict.hotbar7, + "Hotbar8" to actionDict.hotbar8, + "Hotbar9" to actionDict.hotbar9, + ) + + // 각 키의 상태를 비교하여 변화가 있으면 keyCallback 호출 + for ((key, glfwKey) in keyMappings) { + val previousState = currentState[glfwKey] ?: false + val currentState = actions[key] ?: false + + if (!previousState && currentState) { + // 키가 처음 눌렸을 때 GLFW_PRESS 호출 + keyCallback?.invoke(handle, glfwKey, 0, GLFW_PRESS, 0) + } else if (previousState && currentState) { + // 키가 계속 눌린 상태라면 GLFW_REPEAT 호출 + keyCallback?.invoke(handle, glfwKey, 0, GLFW_REPEAT, 0) + } else if (previousState && !currentState) { + // 키가 떼졌을 때 GLFW_RELEASE 호출 + keyCallback?.invoke(handle, glfwKey, 0, GLFW_RELEASE, 0) + } + + // 현재 상태 갱신 + this.currentState[glfwKey] = currentState + } + } + + fun isKeyPressed(key: Int): Boolean { + return currentState[key] ?: false + } +} \ No newline at end of file diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/MinecraftSoundListener.kt b/src/main/java/com/kyhsgeekcode/minecraft_env/MinecraftSoundListener.kt index 5f282cb..cf1e74d 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/MinecraftSoundListener.kt +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/MinecraftSoundListener.kt @@ -5,7 +5,6 @@ import net.minecraft.client.sound.SoundInstance import net.minecraft.client.sound.SoundInstanceListener import net.minecraft.client.sound.SoundManager import net.minecraft.client.sound.WeightedSoundSet -import net.minecraft.text.TranslatableTextContent data class SoundEntry(val translateKey: String, var age: Long, var x: Double, var y: Double, var z: Double) { fun reset(x: Double, y: Double, z: Double) { @@ -31,16 +30,11 @@ class MinecraftSoundListener(soundManager: SoundManager) : SoundInstanceListener private val _entries: MutableList = mutableListOf() val entries = _entries as List - override fun onSoundPlayed(sound: SoundInstance?, soundSet: WeightedSoundSet?, range: Float) { + override fun onSoundPlayed(sound: SoundInstance?, soundSet: WeightedSoundSet?) { if (sound == null) return if (soundSet == null) return val subtitle = soundSet.subtitle ?: return - val content = subtitle.content - if (content !is TranslatableTextContent) { - println("content is not TranslatableTextContent: $content") - return - } - val translateKey = content.key + val translateKey = subtitle.string if (this._entries.isNotEmpty()) { for (subtitleEntry in this._entries) { if (subtitleEntry.translateKey != translateKey) continue diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/Minecraft_env.kt b/src/main/java/com/kyhsgeekcode/minecraft_env/Minecraft_env.kt index 53896cd..4a1b5ce 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/Minecraft_env.kt +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/Minecraft_env.kt @@ -3,10 +3,10 @@ package com.kyhsgeekcode.minecraft_env import com.google.protobuf.ByteString +import com.kyhsgeekcode.minecraft_env.mixin.ClientRenderTickCounterAccessor import com.kyhsgeekcode.minecraft_env.proto.* import com.kyhsgeekcode.minecraft_env.proto.ActionSpace.ActionSpaceMessageV2 import com.kyhsgeekcode.minecraft_env.proto.ObservationSpace -import com.mojang.blaze3d.platform.GlConst import com.mojang.blaze3d.systems.RenderSystem import net.fabricmc.api.ModInitializer import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents @@ -15,16 +15,11 @@ import net.minecraft.block.BlockState import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient.IS_SYSTEM_MAC import net.minecraft.client.gui.screen.DeathScreen -import net.minecraft.client.gui.screen.Screen import net.minecraft.client.network.ClientPlayerEntity -import net.minecraft.client.option.KeyBinding import net.minecraft.client.render.BackgroundRenderer -import net.minecraft.client.util.InputUtil -import net.minecraft.client.util.ScreenshotRecorder import net.minecraft.client.world.ClientWorld import net.minecraft.entity.EntityType import net.minecraft.network.packet.c2s.play.ClientStatusC2SPacket -import net.minecraft.registry.Registries import net.minecraft.server.MinecraftServer import net.minecraft.stat.Stats import net.minecraft.util.Identifier @@ -33,11 +28,11 @@ import net.minecraft.util.WorldSavePath import net.minecraft.util.function.BooleanBiFunction import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box -import net.minecraft.util.math.MathHelper import net.minecraft.util.math.Vec3d +import net.minecraft.util.registry.Registry import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.World -import org.lwjgl.glfw.GLFW +import org.lwjgl.opengl.GL11 import java.io.IOException import java.net.SocketTimeoutException import java.net.StandardProtocolFamily @@ -57,6 +52,7 @@ enum class ResetPhase { WAIT_PLAYER_DEATH, WAIT_PLAYER_RESPAWN, WAIT_INIT_ENDS, + WAIT_CHUNK_LOADING, END_RESET, } @@ -68,58 +64,6 @@ enum class IOPhase { SENT_OBSERVATION_SHOULD_READ_ACTION, } -fun handleKeyPress( - currentState: Boolean, - wasPressing: Boolean, - keyCode: Int, - mouse: Boolean = false -): Boolean { - val key = if (!mouse) { - InputUtil.fromKeyCode(keyCode, 0) - } else { - InputUtil.Type.MOUSE.createFromCode(keyCode) - } - - // 키가 눌린 상태인지 확인 - if (currentState) { - KeyBinding.setKeyPressed(key, true) - keyMap[keyCode] = true - if (!wasPressing) { - KeyBinding.onKeyPressed(key) - } - } else { - if (mouse) { -// if (wasPressing) { -//// println("Releasing $key") -// } - } - KeyBinding.setKeyPressed(key, false) - keyMap[keyCode] = false - } - - return currentState -} - -fun handleScreenKeyPress( - currentState: Boolean, - wasPressing: Boolean, - keyCode: Int, - scanCode: Int, - modifiers: Int, - screen: Screen -): Boolean { - if (currentState) { - if (!wasPressing) { - return screen.keyPressed(keyCode, scanCode, modifiers) - } - } else if (wasPressing) { - return screen.keyReleased(keyCode, scanCode, modifiers) - } - return false -} - - -val keyMap = java.util.HashMap() class Minecraft_env : ModInitializer, CommandExecutor { private lateinit var initialEnvironment: InitialEnvironment.InitialEnvironmentMessage @@ -136,18 +80,7 @@ class Minecraft_env : ModInitializer, CommandExecutor { private var skipSync = false private var ioPhase = IOPhase.BEGINNING - // Difference matters - private var wasPressingForward = false - private var wasPressingBack = false - private var wasPressingLeft = false - private var wasPressingRight = false - private var wasJumping = false - private var wasSneaking = false - private var wasSprinting = false - private var wasUsing = false - private var wasAttacking = false - private var wasPressingInventory = false - private var wasPressingDrop = false + private var waitLoadingCounter = 0 override fun onInitialize() { val ld_preload = System.getenv("LD_PRELOAD") @@ -301,6 +234,17 @@ class Minecraft_env : ModInitializer, CommandExecutor { csvLogger.log("Waiting for the initialization ends") if (initializer.initWorldFinished) { sendSetScreenNull(client) // clear death screen + waitLoadingCounter = 3 // wait for 3 ticks + resetPhase = ResetPhase.WAIT_CHUNK_LOADING + } + return + } + + ResetPhase.WAIT_CHUNK_LOADING -> { + waitLoadingCounter-- + if (waitLoadingCounter <= 0) { + printWithTime("Chunk loading ends") + csvLogger.log("Chunk loading ends") resetPhase = ResetPhase.END_RESET } return @@ -345,7 +289,7 @@ class Minecraft_env : ModInitializer, CommandExecutor { } private fun sendSetScreenNull(client: MinecraftClient) { - client.setScreen(null) + client.openScreen(null) } @@ -414,107 +358,19 @@ class Minecraft_env : ModInitializer, CommandExecutor { client: MinecraftClient ): Boolean { csvLogger.profileStartPrint("Minecraft_env/onInitialize/ClientWorldTick/ReadAction/ApplyAction") - MouseInfo.handle = client.window.handle - val currentScreen = client.currentScreen - if (currentScreen != null) { - val keys = listOf( - Triple(actionDict.inventory, wasPressingInventory, GLFW.GLFW_KEY_E), - Triple(actionDict.drop, wasPressingDrop, GLFW.GLFW_KEY_Q), - Triple(actionDict.hotbar1, false, GLFW.GLFW_KEY_1), - Triple(actionDict.hotbar2, false, GLFW.GLFW_KEY_2), - Triple(actionDict.hotbar3, false, GLFW.GLFW_KEY_3), - Triple(actionDict.hotbar4, false, GLFW.GLFW_KEY_4), - Triple(actionDict.hotbar5, false, GLFW.GLFW_KEY_5), - Triple(actionDict.hotbar6, false, GLFW.GLFW_KEY_6), - Triple(actionDict.hotbar7, false, GLFW.GLFW_KEY_7), - Triple(actionDict.hotbar8, false, GLFW.GLFW_KEY_8), - Triple(actionDict.hotbar9, false, GLFW.GLFW_KEY_9), - ) - for ((action, wasPressing, keyCode) in keys) { - val handled = handleScreenKeyPress( - action, - wasPressing, - keyCode, - 0, - 0, - currentScreen - ) - wasPressingInventory = actionDict.inventory - wasPressingDrop = actionDict.drop - if (handled) { - return false - } - } + if (actionDict.cameraYaw != 0.0f || actionDict.cameraPitch != 0.0f) { + val dy = actionDict.cameraPitch * 20.0 / 3 + val dx = actionDict.cameraYaw * 20.0 / 3 + MouseInfo.moveMouseBy(dx.toInt(), dy.toInt()) } - - wasPressingForward = handleKeyPress(actionDict.forward, wasPressingForward, GLFW.GLFW_KEY_W) - wasPressingBack = handleKeyPress(actionDict.back, wasPressingBack, GLFW.GLFW_KEY_S) - wasPressingLeft = handleKeyPress(actionDict.left, wasPressingLeft, GLFW.GLFW_KEY_A) - wasPressingRight = handleKeyPress(actionDict.right, wasPressingRight, GLFW.GLFW_KEY_D) - wasJumping = handleKeyPress(actionDict.jump, wasJumping, GLFW.GLFW_KEY_SPACE) - wasSneaking = handleKeyPress(actionDict.sneak, wasSneaking, GLFW.GLFW_KEY_LEFT_SHIFT) - wasSprinting = handleKeyPress(actionDict.sprint, wasSprinting, GLFW.GLFW_KEY_LEFT_CONTROL) - - if (currentScreen != null) { - if (actionDict.use) { - if (!wasUsing) - MouseInfo.clickRightButton() - wasUsing = true - } else { - if (wasUsing) - MouseInfo.releaseRightButton() - wasUsing = false - } - if (actionDict.attack) { - if (!wasAttacking) - MouseInfo.clickLeftButton() - wasAttacking = true - } else { - if (wasAttacking) - MouseInfo.releaseLeftButton() - wasAttacking = false - } - -// wasUsing = false -// wasAttacking = false - } else { - wasUsing = handleKeyPress(actionDict.use, wasUsing, GLFW.GLFW_MOUSE_BUTTON_RIGHT, mouse = true) - wasAttacking = handleKeyPress(actionDict.attack, wasAttacking, GLFW.GLFW_MOUSE_BUTTON_LEFT, mouse = true) + // Handle key press + KeyboardInfo.onAction(actionDict) + val currentScreen = client.currentScreen + if (currentScreen != null && currentScreen is DeathScreen) { + // Disable disconnect button + return false } - - // Should handle screen keys for inventory, drop, hotbars - // TODO: Handle swap - // handleKeyPress(actionDict.swap, false, GLFW.GLFW_KEY_F) - wasPressingDrop = handleKeyPress(actionDict.drop, wasPressingDrop, GLFW.GLFW_KEY_Q) - handleKeyPress(actionDict.inventory, false, GLFW.GLFW_KEY_E) - handleKeyPress(actionDict.hotbar1, false, GLFW.GLFW_KEY_1) - handleKeyPress(actionDict.hotbar2, false, GLFW.GLFW_KEY_2) - handleKeyPress(actionDict.hotbar3, false, GLFW.GLFW_KEY_3) - handleKeyPress(actionDict.hotbar4, false, GLFW.GLFW_KEY_4) - handleKeyPress(actionDict.hotbar5, false, GLFW.GLFW_KEY_5) - handleKeyPress(actionDict.hotbar6, false, GLFW.GLFW_KEY_6) - handleKeyPress(actionDict.hotbar7, false, GLFW.GLFW_KEY_7) - handleKeyPress(actionDict.hotbar8, false, GLFW.GLFW_KEY_8) - handleKeyPress(actionDict.hotbar9, false, GLFW.GLFW_KEY_9) - - // TODO: Translate delta camera to mouse movement - -// if (currentScreen != null) { - // To raise head, pitch should be decreased - // Move mouse up, pitch should be decreased - // Move mouse up = y axis decrease & pitch decrease - val dy = actionDict.cameraPitch * 20.0 / 3 - val dx = actionDict.cameraYaw * 20.0 / 3 - MouseInfo.moveMouseBy(dx, dy) // Invert y axis -// } else { -// // pitch: 0: -90 degree, 24: 90 degree -// val deltaPitchInDeg = actionDict.cameraPitch -// // yaw: 0: -180 degree, 24: 180 degree -// val deltaYawInDeg = actionDict.cameraYaw -// player.pitch += deltaPitchInDeg -// player.yaw += deltaYawInDeg -// player.pitch = MathHelper.clamp(player.pitch, -90.0f, 90.0f) -// } + MouseInfo.onAction(actionDict) csvLogger.profileEndPrint("Minecraft_env/onInitialize/ClientWorldTick/ReadAction/ApplyAction") return false } @@ -578,16 +434,19 @@ class Minecraft_env : ModInitializer, CommandExecutor { printWithTime("New left position: ${left.x}, ${left.y}, ${left.z} ${player.prevX}, ${player.prevY}, ${player.prevZ}") // (client as ClientRenderInvoker).invokeRender(true) render(client) - val image1ByteArray = ScreenshotRecorder.takeScreenshot(buffer).use { screenshot -> - encodeImageToBytes( - screenshot, - initialEnvironment.imageSizeX, - initialEnvironment.imageSizeY, - initialEnvironment.imageSizeX, - initialEnvironment.imageSizeY - ) - } - image_1 = ByteString.copyFrom(image1ByteArray) + image_1 = FramebufferCapturer.captureFramebuffer( + buffer.colorAttachment, + buffer.fbo, + buffer.textureWidth, + buffer.textureHeight, + initialEnvironment.imageSizeX, + initialEnvironment.imageSizeY, + initialEnvironment.screenEncodingMode, + false, + MouseInfo.showCursor, // FramebufferCapturer.isExtensionAvailable + MouseInfo.mouseX.toInt(), + MouseInfo.mouseY.toInt(), + ) player.prevX = right.x player.prevY = right.y player.prevZ = right.z @@ -595,16 +454,19 @@ class Minecraft_env : ModInitializer, CommandExecutor { printWithTime("New right position: ${right.x}, ${right.y}, ${right.z} ${player.prevX}, ${player.prevY}, ${player.prevZ}") // (client as ClientRenderInvoker).invokeRender(true) render(client) - val image2ByteArray = ScreenshotRecorder.takeScreenshot(buffer).use { screenshot -> - encodeImageToBytes( - screenshot, - initialEnvironment.imageSizeX, - initialEnvironment.imageSizeY, - initialEnvironment.imageSizeX, - initialEnvironment.imageSizeY - ) - } - image_2 = ByteString.copyFrom(image2ByteArray) + image_2 = FramebufferCapturer.captureFramebuffer( + buffer.colorAttachment, + buffer.fbo, + buffer.textureWidth, + buffer.textureHeight, + initialEnvironment.imageSizeX, + initialEnvironment.imageSizeY, + initialEnvironment.screenEncodingMode, + false, + MouseInfo.showCursor, // FramebufferCapturer.isExtensionAvailable + MouseInfo.mouseX.toInt(), + MouseInfo.mouseY.toInt(), + ) // return to the original position player.prevX = oldPrevX player.prevY = oldPrevY @@ -688,12 +550,12 @@ class Minecraft_env : ModInitializer, CommandExecutor { killedStatistics[killStatKey] = stat } for (mineStatKey in initialEnvironment.minedStatKeysList) { - val key = Registries.BLOCK.get(Identifier.of("minecraft", mineStatKey)) + val key = Registry.BLOCK.get(Identifier("minecraft", mineStatKey)) val stat = player.statHandler.getStat(Stats.MINED.getOrCreateStat(key)) minedStatistics[mineStatKey] = stat } for (miscStatKey in initialEnvironment.miscStatKeysList) { - val key = Registries.CUSTOM_STAT.get(Identifier.of("minecraft", miscStatKey)) + val key = Registry.CUSTOM_STAT.get(Identifier("minecraft", miscStatKey)) miscStatistics[miscStatKey] = player.statHandler.getStat(Stats.CUSTOM.getOrCreateStat(key)) } entityListener?.run { @@ -724,9 +586,9 @@ class Minecraft_env : ModInitializer, CommandExecutor { if (initialEnvironment.requiresSurroundingBlocks) { val blocks = mutableListOf() - for (i in (player.blockX - 1)..(player.blockX + 1)) { - for (j in player.blockY - 1..player.blockY + 1) { - for (k in player.blockZ - 1..player.blockZ + 1) { + for (i in (player.blockPos.x - 1)..(player.blockPos.x + 1)) { + for (j in player.blockPos.y - 1..player.blockPos.y + 1) { + for (k in player.blockPos.z - 1..player.blockPos.z + 1) { val block = world.getBlockState(BlockPos(i, j, k)) blocks.add( blockInfo { @@ -787,7 +649,8 @@ class Minecraft_env : ModInitializer, CommandExecutor { if (command.startsWith("/")) { command = command.substring(1) } - player.networkHandler.sendChatCommand(command) + command = "/$command" + player.sendChatMessage(command) printWithTime("End send command: $command") csvLogger.log("End send command: $command") } @@ -795,8 +658,16 @@ class Minecraft_env : ModInitializer, CommandExecutor { } fun ClientPlayerEntity.checkIfCameraBlocked(): Boolean { - val f: Float = EntityType.PLAYER.dimensions.width() * 0.8f - val box = Box.of(this.eyePos, f.toDouble(), 1.0E-6, f.toDouble()) + val f: Float = EntityType.PLAYER.dimensions.width * 0.8f + eyeY + val box = Box( + x, + eyeY, + z, + x + f, + eyeY + 1e-6, + z + f + ) return BlockPos.stream(box).anyMatch { pos: BlockPos -> val blockState: BlockState = this.world.getBlockState(pos) !blockState.isAir && VoxelShapes.matchesAnywhere( @@ -811,14 +682,15 @@ fun ClientPlayerEntity.checkIfCameraBlocked(): Boolean { fun render(client: MinecraftClient) { - RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT or GlConst.GL_COLOR_BUFFER_BIT, IS_SYSTEM_MAC) + RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT or GL11.GL_COLOR_BUFFER_BIT, IS_SYSTEM_MAC) client.framebuffer.beginWrite(true) - BackgroundRenderer.clearFog() + BackgroundRenderer.method_23792() RenderSystem.enableCull() val l = Util.getMeasuringTimeNano() client.gameRenderer.render( - client.renderTickCounter,// client.renderTickCounter.tickDelta, - true // tick + (client as ClientRenderTickCounterAccessor).renderTickCounter.tickDelta, + l, + false // tick ) client.framebuffer.endWrite() client.framebuffer.draw(client.window.framebufferWidth, client.window.framebufferHeight) diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/MouseInfo.kt b/src/main/java/com/kyhsgeekcode/minecraft_env/MouseInfo.kt index c64a1d3..4cb604b 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/MouseInfo.kt +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/MouseInfo.kt @@ -1,8 +1,9 @@ package com.kyhsgeekcode.minecraft_env import com.kyhsgeekcode.minecraft_env.mixin.MouseXYAccessor +import com.kyhsgeekcode.minecraft_env.proto.ActionSpace import net.minecraft.client.MinecraftClient -import org.lwjgl.glfw.GLFW +import org.lwjgl.glfw.GLFW.* import org.lwjgl.glfw.GLFWCursorPosCallbackI import org.lwjgl.glfw.GLFWMouseButtonCallbackI @@ -12,10 +13,40 @@ object MouseInfo { var mouseButtonCallback: GLFWMouseButtonCallbackI? = null var mouseX: Double = 0.0 var mouseY: Double = 0.0 - var leftButtonPressed: Boolean = false - var rightButtonPressed: Boolean = false var showCursor: Boolean = false + var currentState: MutableMap = mutableMapOf() + + val buttonMappings = mapOf( + "use" to GLFW_MOUSE_BUTTON_RIGHT, + "attack" to GLFW_MOUSE_BUTTON_LEFT + ) + + fun onAction(actionDict: ActionSpace.ActionSpaceMessageV2) { + val actions = mapOf( + "use" to actionDict.use, + "attack" to actionDict.attack + ) + val shift = KeyboardInfo.isKeyPressed(GLFW_KEY_LEFT_SHIFT) + val mods = if (shift) GLFW_MOD_SHIFT else 0 + // 각 마우스 버튼 상태를 비교하여 변화가 있으면 mouseCallback 호출 + for ((action, glfwButton) in buttonMappings) { + val previousState = currentState[glfwButton] ?: false + val currentState = actions[action] ?: false + + if (!previousState && currentState) { + // 마우스 버튼이 처음 눌렸을 때 GLFW_PRESS 호출 + mouseButtonCallback?.invoke(handle, glfwButton, GLFW_PRESS, mods) + } else if (previousState && !currentState) { + // 마우스 버튼을 뗐을 때 GLFW_RELEASE 호출 + mouseButtonCallback?.invoke(handle, glfwButton, GLFW_RELEASE, mods) + } + + // 현재 상태 갱신 + this.currentState[glfwButton] = currentState + } + } + fun getMousePos(): Pair { return Pair(mouseX, mouseY) } @@ -34,48 +65,32 @@ object MouseInfo { showCursor = show } - fun moveMouseBy(dx: Double, dy: Double) { - // 쪼개는 단위 크기 설정 (예: 1 픽셀씩 움직임) - val stepSize = 1.0 + fun moveMouseBy(dx: Int, dy: Int) { + // dx와 dy의 절대값 계산 (정수로 변환) + val stepsX = Math.abs(dx) + val stepsY = Math.abs(dy) - // dx와 dy의 절대값 계산 - val stepsX = Math.abs(dx / stepSize).toInt() - val stepsY = Math.abs(dy / stepSize).toInt() + // dx와 dy의 이동 방향 계산 + val stepX = if (dx > 0) 1 else -1 + val stepY = if (dy > 0) 1 else -1 - // 가장 큰 움직임에 대한 총 단계 계산 (둘 중 더 큰 값) - val totalSteps = Math.max(stepsX, stepsY) + // 최대 이동 횟수 계산 (더 큰 쪽을 기준으로 반복) + val maxSteps = Math.max(stepsX, stepsY) - // 각 단계에서 움직일 dx, dy 비율 계산 - val stepDx = dx / totalSteps - val stepDy = dy / totalSteps - - // 단계별로 커서를 이동 - for (i in 0 until totalSteps) { - mouseX += stepDx - mouseY += stepDy - cursorPosCallback?.invoke(handle, mouseX, mouseY) + // X와 Y를 번갈아 가며 이동 + var movedX = 0 + var movedY = 0 + for (i in 0 until maxSteps) { + if (movedX < stepsX) { + mouseX += stepX + cursorPosCallback?.invoke(handle, mouseX, mouseY) + movedX++ + } + if (movedY < stepsY) { + mouseY += stepY + cursorPosCallback?.invoke(handle, mouseX, mouseY) + movedY++ + } } } - - - // TODO: Mods (shift click, etc) - fun clickLeftButton() { - mouseButtonCallback?.invoke(handle, GLFW.GLFW_MOUSE_BUTTON_LEFT, GLFW.GLFW_PRESS, 0) - leftButtonPressed = true - } - - fun releaseLeftButton() { - mouseButtonCallback?.invoke(handle, GLFW.GLFW_MOUSE_BUTTON_LEFT, GLFW.GLFW_RELEASE, 0) - leftButtonPressed = false - } - - fun clickRightButton() { - mouseButtonCallback?.invoke(handle, GLFW.GLFW_MOUSE_BUTTON_RIGHT, GLFW.GLFW_PRESS, 0) - rightButtonPressed = true - } - - fun releaseRightButton() { - mouseButtonCallback?.invoke(handle, GLFW.GLFW_MOUSE_BUTTON_RIGHT, GLFW.GLFW_RELEASE, 0) - rightButtonPressed = false - } } \ No newline at end of file diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/Point3D.java b/src/main/java/com/kyhsgeekcode/minecraft_env/Point3D.java deleted file mode 100644 index b8db0b0..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/Point3D.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.kyhsgeekcode.minecraft_env; - -public record Point3D(int x, int y, int z) { - -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/CacheBiomeAccessMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/CacheBiomeAccessMixin.java deleted file mode 100644 index 9b222ae..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/CacheBiomeAccessMixin.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.kyhsgeekcode.minecraft_env.Point3D; -import net.minecraft.registry.entry.RegistryEntry; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.biome.source.BiomeAccess; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(net.minecraft.world.biome.source.BiomeAccess.class) -public class CacheBiomeAccessMixin { - // Thread safe LRU cache - @Unique - private final Cache coordsCache = Caffeine.newBuilder() - .maximumSize(4096) // Max cache size - .build(); - - @Final - @Shadow - private BiomeAccess.Storage storage; - - @Inject( - method = "getBiome", - at = @At(value = "HEAD"), - cancellable = true - ) - private void getBiomeHead(BlockPos pos, CallbackInfoReturnable> cir) { - // BlockPos is mutable - var blockPos = new Point3D(pos.getX(), pos.getY(), pos.getZ()); - var pwx = coordsCache.getIfPresent(blockPos); - if (pwx != null) { - cir.setReturnValue(storage.getBiomeForNoiseGen(pwx.x(), pwx.y(), pwx.z())); - cir.cancel(); - } - } - - @Inject( - method = "getBiome", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/biome/source/BiomeAccess$Storage;getBiomeForNoiseGen(III)Lnet/minecraft/registry/entry/RegistryEntry;"), - locals = LocalCapture.CAPTURE_FAILHARD - ) - private void getBiome( - BlockPos pos, - CallbackInfoReturnable> cir, - int i, - int j, - int k, - int l, - int m, - int n, - double d, - double e, - double f, - int o, - double g, - int p, - int w, - int x - ) { - coordsCache.put(new Point3D(pos.getX(), pos.getY(), pos.getZ()), new Point3D(p, w, x)); - } -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ChatVisibleMessageAccessor.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ChatVisibleMessageAccessor.java index 31f342f..3e0102d 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ChatVisibleMessageAccessor.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ChatVisibleMessageAccessor.java @@ -1,6 +1,7 @@ package com.kyhsgeekcode.minecraft_env.mixin; import net.minecraft.client.gui.hud.ChatHudLine; +import net.minecraft.text.OrderedText; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -9,5 +10,5 @@ @Mixin(net.minecraft.client.gui.hud.ChatHud.class) public interface ChatVisibleMessageAccessor { @Accessor("visibleMessages") - List getVisibleMessages(); + List> getVisibleMessages(); } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientCommonNetworkHandlerClientAccessor.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientCommonNetworkHandlerClientAccessor.java index f0eaf20..a30b180 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientCommonNetworkHandlerClientAccessor.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientCommonNetworkHandlerClientAccessor.java @@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(net.minecraft.client.network.ClientCommonNetworkHandler.class) +@Mixin(net.minecraft.client.network.ClientPlayNetworkHandler.class) public interface ClientCommonNetworkHandlerClientAccessor { @Accessor("client") MinecraftClient getClient(); diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientDoAttackInvoker.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientDoAttackInvoker.java deleted file mode 100644 index efb64a7..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientDoAttackInvoker.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import net.minecraft.client.MinecraftClient; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(MinecraftClient.class) -public interface ClientDoAttackInvoker { - @Invoker("doAttack") - boolean invokeDoAttack(); -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientDoItemUseInvoker.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientDoItemUseInvoker.java deleted file mode 100644 index e0dbf4e..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientDoItemUseInvoker.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import net.minecraft.client.MinecraftClient; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(MinecraftClient.class) -public interface ClientDoItemUseInvoker { - @Invoker("doItemUse") - void invokeDoItemUse(); -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientPlayNetworkHandlerMixin.java index 6f5ee43..5a83c19 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientPlayNetworkHandlerMixin.java @@ -4,7 +4,7 @@ import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; -import net.minecraft.network.packet.s2c.play.DeathMessageS2CPacket; +import net.minecraft.network.packet.s2c.play.CombatEventS2CPacket; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -16,11 +16,11 @@ public class ClientPlayNetworkHandlerMixin implements GetMessagesInterface { @Shadow private ClientWorld world; - @Inject(method = "onDeathMessage", at = @At("HEAD"), cancellable = false) - public void onDeathMessage(DeathMessageS2CPacket packet, CallbackInfo ci) { - Entity entity = this.world.getEntityById(packet.playerId()); + @Inject(method = "onCombatEvent", at = @At("HEAD"), cancellable = false) + public void onCombatEvent(CombatEventS2CPacket packet, CallbackInfo ci) { + Entity entity = this.world.getEntityById(packet.entityId); if (entity == ((ClientCommonNetworkHandlerClientAccessor) this).getClient().player) { - var message = packet.message(); + var message = packet.deathMessage; this.lastDeathMessage.clear(); this.lastDeathMessage.add(message.getString()); } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientRenderTickCounterAccessor.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientRenderTickCounterAccessor.java new file mode 100644 index 0000000..ebbd6c5 --- /dev/null +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ClientRenderTickCounterAccessor.java @@ -0,0 +1,12 @@ +package com.kyhsgeekcode.minecraft_env.mixin; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderTickCounter; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MinecraftClient.class) +public interface ClientRenderTickCounterAccessor { + @Accessor("renderTickCounter") + RenderTickCounter getRenderTickCounter(); +} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/CreateWorldScreenMoreOptionsAccessor.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/CreateWorldScreenMoreOptionsAccessor.java new file mode 100644 index 0000000..c7dafff --- /dev/null +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/CreateWorldScreenMoreOptionsAccessor.java @@ -0,0 +1,10 @@ +package com.kyhsgeekcode.minecraft_env.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(net.minecraft.client.gui.screen.world.CreateWorldScreen.class) +public interface CreateWorldScreenMoreOptionsAccessor { + @Accessor("moreOptionsOpen") + boolean getMoreOptionsOpen(); +} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/DisableRegionalComplianceMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/DisableRegionalComplianceMixin.java deleted file mode 100644 index ff764fb..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/DisableRegionalComplianceMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import net.minecraft.client.resource.PeriodicNotificationManager; -import net.minecraft.resource.ReloadableResourceManagerImpl; -import net.minecraft.resource.ResourceReloader; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(ReloadableResourceManagerImpl.class) -public class DisableRegionalComplianceMixin { - @Inject(method = "registerReloader", at = @At("HEAD"), cancellable = true) - private void registerReloader(ResourceReloader reloader, CallbackInfo ci) { - if (reloader instanceof PeriodicNotificationManager) { - ci.cancel(); // cancel the reload - } - } -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/GammaMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/GammaMixin.java deleted file mode 100644 index 938b48b..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/GammaMixin.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -/* - * This file is part of the Gamma Utils project and is licensed under the GNU Lesser General Public License v3.0. - * - * Copyright (C) 2021 Sjouwer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -import com.mojang.serialization.Codec; -import net.minecraft.client.option.SimpleOption; -import net.minecraft.client.resource.language.I18n; -import net.minecraft.text.Text; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(SimpleOption.class) -public class GammaMixin { - - @Shadow - @Final - Text text; - - @Shadow - T value; - - /** - * Mixin to allow saving "invalid" gamma values into the options file - */ - @Inject(method = "getCodec", at = @At("HEAD"), cancellable = true) - private void returnFakeCodec(CallbackInfoReturnable> info) { - if (text.getString().equals(I18n.translate("options.gamma"))) { - info.setReturnValue(Codec.DOUBLE); - } - } - - /** - * Mixin to allow setting "invalid" gamma values - */ - @Inject(method = "setValue", at = @At("HEAD"), cancellable = true) - private void setRealValue(T value, CallbackInfo info) { - if (text.getString().equals(I18n.translate("options.gamma"))) { - this.value = value; - info.cancel(); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/InputUtilMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/InputUtilMixin.java index 625d1d8..9d668b2 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/InputUtilMixin.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/InputUtilMixin.java @@ -1,23 +1,23 @@ package com.kyhsgeekcode.minecraft_env.mixin; +import com.kyhsgeekcode.minecraft_env.KeyboardInfo; import com.kyhsgeekcode.minecraft_env.MouseInfo; import org.lwjgl.glfw.*; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; -import static com.kyhsgeekcode.minecraft_env.Minecraft_envKt.getKeyMap; - @Mixin(net.minecraft.client.util.InputUtil.class) public class InputUtilMixin { @Overwrite public static boolean isKeyPressed(long handle, int code) { - return getKeyMap().getOrDefault(code, false); + return KeyboardInfo.INSTANCE.isKeyPressed(code); } @Overwrite public static void setMouseCallbacks(long handle, GLFWCursorPosCallbackI cursorPosCallback, GLFWMouseButtonCallbackI mouseButtonCallback, GLFWScrollCallbackI scrollCallback, GLFWDropCallbackI dropCallback) { MouseInfo.INSTANCE.setCursorPosCallback(cursorPosCallback); MouseInfo.INSTANCE.setMouseButtonCallback(mouseButtonCallback); + MouseInfo.INSTANCE.setHandle(handle); } @Overwrite @@ -25,4 +25,11 @@ public static void setCursorParameters(long handler, int inputModeValue, double MouseInfo.INSTANCE.setCursorPos(x, y); MouseInfo.INSTANCE.setCursorShown(inputModeValue == GLFW.GLFW_CURSOR_NORMAL); } + + @Overwrite + public static void setKeyboardCallbacks(long handle, GLFWKeyCallbackI keyCallback, GLFWCharModsCallbackI charModsCallback) { + KeyboardInfo.INSTANCE.setKeyCallback(keyCallback); + KeyboardInfo.INSTANCE.setCharModsCallback(charModsCallback); + KeyboardInfo.INSTANCE.setHandle(handle); + } } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/InventoryScreenDrawBackgroundLoggingMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/InventoryScreenDrawBackgroundLoggingMixin.java deleted file mode 100644 index 18c3ae3..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/InventoryScreenDrawBackgroundLoggingMixin.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import com.kyhsgeekcode.minecraft_env.MouseInfo; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(net.minecraft.client.gui.screen.ingame.InventoryScreen.class) -public class InventoryScreenDrawBackgroundLoggingMixin { - @Shadow - private float mouseX; - @Shadow - private float mouseY; - - @Inject(at = @At("TAIL"), method = "drawBackground", locals = LocalCapture.CAPTURE_FAILSOFT) - private void drawBackground(DrawContext context, float delta, int mouseX, int mouseY, CallbackInfo ci, int i, int j) { -// System.out.println( -// "drawBackground: " + this.mouseX + "," + this.mouseY + ", " + delta + ", " + mouseX + ", " + mouseY + "," + i + ", " + j -// ); -// MinecraftClient client = MinecraftClient.getInstance(); -// double mouseInfomouseX = MouseInfo.INSTANCE.getMouseX(); -// double mouseInfomouseY = MouseInfo.INSTANCE.getMouseY(); -// int convertedX = (int) (mouseInfomouseX * client.getWindow().getScaledWidth() / client.getWindow().getWidth()); -// int convertedY = (int) (mouseInfomouseY * client.getWindow().getScaledHeight() / client.getWindow().getHeight()); -// System.out.println( -// "MouseInfo:" + mouseInfomouseX + "," + mouseInfomouseY + ",converted:" + convertedX + "," + convertedY -// ); - } -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/KeyboardMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/KeyboardMixin.java deleted file mode 100644 index 67ee6ef..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/KeyboardMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import net.minecraft.client.Keyboard; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Keyboard.class) -public class KeyboardMixin { - @Inject(method = "setup", at = @At("HEAD"), cancellable = true) - private void setKeyboardCallbacks(CallbackInfo ci) { - ci.cancel(); - // Call onKey, onChar directly to handle inputs, no external keyboard inputs - } -} - - - - diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MinecraftServer_tickspeedMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MinecraftServer_tickspeedMixin.java index 920f198..da395db 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MinecraftServer_tickspeedMixin.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MinecraftServer_tickspeedMixin.java @@ -3,15 +3,12 @@ import com.kyhsgeekcode.minecraft_env.TickSpeed; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerTask; -import net.minecraft.server.ServerTickManager; import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.TimeHelper; +import net.minecraft.util.TickDurationMonitor; import net.minecraft.util.Util; import net.minecraft.util.profiler.Profiler; -import net.minecraft.util.profiling.jfr.FlightProfiler; import net.minecraft.util.thread.ReentrantThreadExecutor; -import org.apache.commons.lang3.tuple.Pair; -import org.slf4j.Logger; +import org.apache.logging.log4j.Logger; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -23,199 +20,118 @@ import java.util.function.BooleanSupplier; -// https://github.com/gnembon/fabric-carpet/blob/master/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java +// https://github.com/gnembon/fabric-carpet/blob/1.16.5/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java @Mixin(value = MinecraftServer.class, priority = Integer.MAX_VALUE - 10) public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExecutor { - @Shadow - @Final - private static Logger LOGGER; - // just because profilerTimings class is public - Pair profilerTimings = null; @Shadow private volatile boolean running; - // @Shadow -// private long timeReference; - @Shadow - private Profiler profiler; - // @Shadow -// private long nextTickTimestamp; - @Shadow - private volatile boolean loading; - // @Shadow -// private long lastTimeReference; - @Shadow - private boolean waitingForNextTick; + @Shadow - private int ticks; + private long timeReference; + @Shadow - private boolean needsDebugSetup; - @Unique - private float carpetMsptAccum = 0.0f; + @Final + private static Logger LOGGER; @Shadow - private long lastOverloadWarningNanos; + private Profiler profiler; public MinecraftServer_tickspeedMixin(String name) { super(name); } @Shadow - public abstract void tick(BooleanSupplier booleanSupplier_1); + protected abstract void tick(BooleanSupplier booleanSupplier_1); @Shadow protected abstract boolean shouldKeepTicking(); @Shadow - public abstract Iterable getWorlds(); + private long field_19248; @Shadow - protected abstract void runTasksTillTickEnd(); + protected abstract void method_16208(); @Shadow - protected abstract void startTickMetrics(); - - @Shadow - protected abstract void endTickMetrics(); - - @Shadow - public abstract boolean isPaused(); - - @Shadow - public abstract ServerTickManager getTickManager(); - - @Shadow - private long tickStartTimeNanos = Util.getMeasuringTimeNano(); + private volatile boolean loading; @Shadow - private static final long OVERLOAD_THRESHOLD_NANOS = 20L * TimeHelper.SECOND_IN_NANOS / 20L; + protected abstract void startMonitor(TickDurationMonitor monitor); @Shadow - private long tickEndTimeNanos; + private long lastTimeReference; @Shadow - private void startTaskPerformanceLog() { - } + private boolean waitingForNextTick; @Shadow - private void pushPerformanceLogs() { - } + public abstract Iterable getWorlds(); - @Shadow - private void pushFullTickLog() { - } + private float carpetMsptAccum = 0.0f; - @Shadow - private float averageTickTime; + /** + * To ensure compatibility with other mods we should allow milliseconds + */ -// // Cancel the while statement -// @Redirect(method = "runServer", at = @At(value = "FIELD", target = "Lnet/minecraft/server/MinecraftServer;running:Z")) -// private boolean cancelRunLoop(MinecraftServer server) { -// return false; -// } // target run() + // Cancel a while statement + @Redirect(method = "runServer", at = @At(value = "FIELD", target = "Lnet/minecraft/server/MinecraftServer;running:Z")) + private boolean cancelRunLoop(MinecraftServer server) { + return false; + } // target run() // Replaced the above cancelled while statement with this one // could possibly just inject that mspt selection at the beginning of the loop, but then adding all mspt's to // replace 50L will be a hassle -// @Inject(method = "runServer", at = @At(value = "INVOKE", shift = At.Shift.AFTER, -// target = "Lnet/minecraft/server/MinecraftServer;createMetadata()Lnet/minecraft/server/ServerMetadata;")) -// private void modifiedRunLoop(CallbackInfo ci) { -//// while (this.running) { -//// //long long_1 = Util.getMeasuringTimeMs() - this.timeReference; -//// //CM deciding on tick speed -//// long msThisTick = 0L; -//// long long_1 = 0L; -//// -//// msThisTick = (long) carpetMsptAccum; // regular tick -//// carpetMsptAccum += TickSpeed.INSTANCE.getMspt() - msThisTick; -//// -//// long_1 = Util.getMeasuringTimeMs() - this.timeReference; -//// -//// //end tick deciding -//// //smoothed out delay to include mcpt component. With 50L gives defaults. -//// if (long_1 > /*2000L*/1000L + 20 * TickSpeed.INSTANCE.getMspt() && this.timeReference - this.lastTimeReference >= /*15000L*/10000L + 100 * TickSpeed.INSTANCE.getMspt()) { -//// long long_2 = long_1 / TickSpeed.INSTANCE.getMspt();//50L; -//// LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", long_1, long_2); -//// this.timeReference += long_2 * TickSpeed.INSTANCE.getMspt();//50L; -//// this.lastTimeReference = this.timeReference; -//// } -//// -//// if (this.needsDebugSetup) { -//// this.needsDebugSetup = false; -//// this.profilerTimings = Pair.of(Util.getMeasuringTimeNano(), ticks); -//// //this.field_33978 = new MinecraftServer.class_6414(Util.getMeasuringTimeNano(), this.ticks); -//// } -//// this.timeReference += msThisTick;//50L; -//// //TickDurationMonitor tickDurationMonitor = TickDurationMonitor.create("Server"); -//// //this.startMonitor(tickDurationMonitor); -//// this.startTickMetrics(); -//// this.profiler.push("tick"); -//// this.tick(this::shouldKeepTicking); -//// this.profiler.swap("nextTickWait"); -//// while (this.runEveryTask()) { -//// Thread.yield(); -//// } // ? -//// this.waitingForNextTick = true; -//// this.nextTickTimestamp = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference); -//// // run all tasks (this will not do a lot when warping), but that's fine since we already run them -//// this.runTasksTillTickEnd();// this.runTasks(); -////// this.runTasks(); -//// this.profiler.pop(); -//// this.endTickMetrics(); -//// this.loading = true; -// -// boolean bl; -// long l; -// var tickManager = this.getTickManager(); -// if (!this.isPaused() && tickManager.isSprinting() && tickManager.sprint()) { -// l = 0L; -// this.lastOverloadWarningNanos = this.tickStartTimeNanos = Util.getMeasuringTimeNano(); -// } else { -// l = tickManager.getNanosPerTick(); -// long m = Util.getMeasuringTimeNano() - this.tickStartTimeNanos; -// if (m > OVERLOAD_THRESHOLD_NANOS + 20L * l && this.tickStartTimeNanos - this.lastOverloadWarningNanos >= OVERLOAD_WARNING_INTERVAL_NANOS + 100L * l) { -// long n = m / l; -// LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", (Object) (m / TimeHelper.MILLI_IN_NANOS), (Object) n); -// this.tickStartTimeNanos += n * l; -// this.lastOverloadWarningNanos = this.tickStartTimeNanos; -// } -// } -// boolean bl2 = bl = l == 0L; -// if (this.needsDebugSetup) { -// this.needsDebugSetup = false; -// } -// this.tickStartTimeNanos += l; -// this.startTickMetrics(); -// this.profiler.push("tick"); -// this.tick(bl ? () -> false : this::shouldKeepTicking); -// this.profiler.swap("nextTickWait"); -// this.waitingForNextTick = true; -// this.tickEndTimeNanos = Math.max(Util.getMeasuringTimeNano() + l, this.tickStartTimeNanos); -// this.startTaskPerformanceLog(); -// this.runTasksTillTickEnd(); -// this.pushPerformanceLogs(); -// if (bl) { -// tickManager.updateSprintTime(); -// } + @Inject(method = "runServer", at = @At(value = "INVOKE", shift = At.Shift.AFTER, + target = "Lnet/minecraft/server/MinecraftServer;setFavicon(Lnet/minecraft/server/ServerMetadata;)V")) + private void modifiedRunLoop(CallbackInfo ci) { + while (this.running) { + //long long_1 = Util.getMeasuringTimeMs() - this.timeReference; + //CM deciding on tick speed + long msThisTick = 0L; + long long_1 = 0L; + msThisTick = (long) carpetMsptAccum; // regular tick + carpetMsptAccum += TickSpeed.INSTANCE.getMspt() - msThisTick; + long_1 = Util.getMeasuringTimeMs() - this.timeReference; + //end tick deciding + //smoothed out delay to include mcpt component. With 50L gives defaults. + if (long_1 > /*2000L*/1000L + 20 * TickSpeed.INSTANCE.getMspt() && this.timeReference - this.lastTimeReference >= /*15000L*/10000L + 100 * TickSpeed.INSTANCE.getMspt()) { + long long_2 = long_1 / TickSpeed.INSTANCE.getMspt();//50L; + LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", long_1, long_2); + this.timeReference += long_2 * TickSpeed.INSTANCE.getMspt();//50L; + this.lastTimeReference = this.timeReference; + } + + this.timeReference += msThisTick;//50L; + this.tick(this::shouldKeepTicking); + while (this.runEveryTask()) { + Thread.yield(); + } + this.waitingForNextTick = true; + this.field_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference); + // run all tasks (this will not do a lot when warping), but that's fine since we already run them + this.method_16208(); // this.profiler.pop(); -// this.pushFullTickLog(); -// this.endTickMetrics(); -// this.loading = true; -// FlightProfiler.INSTANCE.onTick(this.averageTickTime); -// } -// } - -// private boolean runEveryTask() { -// if (super.runTask()) { -// return true; -// } else { -// if (true) { // unconditionally this time -// for (ServerWorld serverlevel : getWorlds()) { -// if (serverlevel.getChunkManager().executeQueuedTasks()) { -// return true; -// } -// } -// } -// return false; -// } -// } +// this.profiler.endTick(); + this.loading = true; + } + } + + + @Unique + private boolean runEveryTask() { + if (super.runTask()) { + return true; + } else { + if (true) { // unconditionally this time + for (ServerWorld serverlevel : getWorlds()) { + if (serverlevel.getChunkManager().executeQueuedTasks()) { + return true; + } + } + } + + return false; + } + } } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MoreOptionsDialogSeedTextFieldAccessor.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MoreOptionsDialogSeedTextFieldAccessor.java new file mode 100644 index 0000000..8aeb110 --- /dev/null +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MoreOptionsDialogSeedTextFieldAccessor.java @@ -0,0 +1,15 @@ +package com.kyhsgeekcode.minecraft_env.mixin; + +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(net.minecraft.client.gui.screen.world.MoreOptionsDialog.class) +public interface MoreOptionsDialogSeedTextFieldAccessor { + @Accessor("seedTextField") + TextFieldWidget getSeedTextField(); + + @Accessor("mapTypeButton") + ButtonWidget getMapTypeButton(); +} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MouseMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MouseMixin.java deleted file mode 100644 index 5e5cf6b..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/MouseMixin.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import com.kyhsgeekcode.minecraft_env.MouseInfo; -import net.minecraft.client.Mouse; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - - -@Mixin(Mouse.class) -abstract public class MouseMixin { - @Inject(method = "isCursorLocked", at = @At("HEAD"), cancellable = true) - private void isCursorLocked(CallbackInfoReturnable cir) { -// cir.setReturnValue(!MouseInfo.INSTANCE.getShowCursor()); - } -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderMixin.java index b902d1c..dda1298 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderMixin.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderMixin.java @@ -5,6 +5,7 @@ import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.render.Tessellator; import net.minecraft.client.util.Window; +import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @@ -23,10 +24,10 @@ private void frameBufferDraw(Framebuffer instance, int width, int height) { @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;swapBuffers()V")) private void windowSwapBuffers(Window instance) { - RenderSystemPollEventsInvoker.pollEvents(); + GLFW.glfwPollEvents(); RenderSystem.replayQueue(); - Tessellator.getInstance().clear(); + Tessellator.getInstance().getBuffer().clear(); // GLFW.glfwSwapBuffers(window); - RenderSystemPollEventsInvoker.pollEvents(); + GLFW.glfwPollEvents(); } } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderSystemPollEventsInvoker.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderSystemPollEventsInvoker.java deleted file mode 100644 index bedcb5a..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderSystemPollEventsInvoker.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(com.mojang.blaze3d.systems.RenderSystem.class) -public interface RenderSystemPollEventsInvoker { - @Invoker("pollEvents") - static void pollEvents() { - - } -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderTickCounterAccessor.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderTickCounterAccessor.java index 7d3d0ff..0a1a32a 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderTickCounterAccessor.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/RenderTickCounterAccessor.java @@ -4,7 +4,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(RenderTickCounter.Dynamic.class) +@Mixin(RenderTickCounter.class) public interface RenderTickCounterAccessor { @Accessor("prevTimeMillis") void setPrevTimeMillis(long prevTimeMillis); diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/SaveWorldMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/SaveWorldMixin.java deleted file mode 100644 index 17de639..0000000 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/SaveWorldMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.kyhsgeekcode.minecraft_env.mixin; - -import net.minecraft.server.MinecraftServer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(MinecraftServer.class) -public class SaveWorldMixin { - @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;saveAll(ZZZ)Z")) - private boolean saveAll(MinecraftServer server, boolean bl, boolean bl2, boolean bl3) { - return false; // disable saving - } -} diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ServerPlayNetworkHandlerDisableSpamChecker.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ServerPlayNetworkHandlerDisableSpamChecker.java index f7f4b9a..c071d7e 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ServerPlayNetworkHandlerDisableSpamChecker.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/ServerPlayNetworkHandlerDisableSpamChecker.java @@ -8,8 +8,8 @@ @Mixin(ServerPlayNetworkHandler.class) public class ServerPlayNetworkHandlerDisableSpamChecker { - @Inject(method = "checkForSpam", at = @At("HEAD"), cancellable = true) - private void checkForSpam(CallbackInfo ci) { + @Inject(method = "disconnect", at = @At("HEAD"), cancellable = true) + private void disconnect(CallbackInfo ci) { ci.cancel(); } } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/TickSpeedMixin.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/TickSpeedMixin.java index 02bdfcb..a108ed4 100644 --- a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/TickSpeedMixin.java +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/TickSpeedMixin.java @@ -1,25 +1,26 @@ package com.kyhsgeekcode.minecraft_env.mixin; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.RenderTickCounter; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -@Mixin(RenderTickCounter.Dynamic.class) +@Mixin(MinecraftClient.class) public class TickSpeedMixin { @Redirect( - method = "beginRenderTick(JZ)I", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderTickCounter$Dynamic;beginRenderTick(J)I") + method = "render(Z)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderTickCounter;beginRenderTick(J)I") ) - private int beginRenderTick(RenderTickCounter.Dynamic renderTickCounter, long timeMillis) { + private int beginRenderTick(RenderTickCounter renderTickCounter, long timeMillis) { ((RenderTickCounterAccessor) renderTickCounter).setLastFrameDuration(1);// (float)(timeMillis - this.prevTimeMillis) / this.tickTime; ((RenderTickCounterAccessor) renderTickCounter).setPrevTimeMillis(timeMillis); ((RenderTickCounterAccessor) renderTickCounter).setTickDelta( - renderTickCounter.getTickDelta(true) + renderTickCounter.getLastFrameDuration() + (renderTickCounter).tickDelta + renderTickCounter.lastFrameDuration ); - int i = (int) renderTickCounter.getTickDelta(true); - ((RenderTickCounterAccessor) renderTickCounter).setTickDelta((renderTickCounter.getTickDelta(true) - (float) i)); + int i = (int) renderTickCounter.tickDelta; + ((RenderTickCounterAccessor) renderTickCounter).setTickDelta((renderTickCounter.tickDelta - (float) i)); return i; } } diff --git a/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/WorldListWidgetLevelSummaryAccessor.java b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/WorldListWidgetLevelSummaryAccessor.java new file mode 100644 index 0000000..8492da6 --- /dev/null +++ b/src/main/java/com/kyhsgeekcode/minecraft_env/mixin/WorldListWidgetLevelSummaryAccessor.java @@ -0,0 +1,11 @@ +package com.kyhsgeekcode.minecraft_env.mixin; + +import net.minecraft.world.level.storage.LevelSummary; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(net.minecraft.client.gui.screen.world.WorldListWidget.Entry.class) +public interface WorldListWidgetLevelSummaryAccessor { + @Accessor("level") + LevelSummary getLevel(); +} diff --git a/src/main/resources/com.kyhsgeekcode.minecraft_env.mixin.json b/src/main/resources/com.kyhsgeekcode.minecraft_env.mixin.json index 717cdce..6ae4897 100644 --- a/src/main/resources/com.kyhsgeekcode.minecraft_env.mixin.json +++ b/src/main/resources/com.kyhsgeekcode.minecraft_env.mixin.json @@ -4,33 +4,28 @@ "package": "com.kyhsgeekcode.minecraft_env.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ - "CacheBiomeAccessMixin", - "DisableRegionalComplianceMixin", + "MinecraftServer_tickspeedMixin", "RenderMixin", - "SaveWorldMixin", "ServerPlayNetworkHandlerDisableSpamChecker" ], "client": [ "ChatVisibleMessageAccessor", "ClientCommonNetworkHandlerClientAccessor", - "ClientDoAttackInvoker", - "ClientDoItemUseInvoker", "ClientEntityRenderDispatcherAccessor", "ClientIsWindowFocusedMixin", "ClientPlayNetworkHandlerMixin", "ClientRenderInvoker", + "ClientRenderTickCounterAccessor", "ClientWorldMixin", - "GammaMixin", + "CreateWorldScreenMoreOptionsAccessor", "InputUtilMixin", - "InventoryScreenDrawBackgroundLoggingMixin", - "KeyboardMixin", - "MouseMixin", + "MoreOptionsDialogSeedTextFieldAccessor", "MouseXYAccessor", - "RenderSystemPollEventsInvoker", "RenderTickCounterAccessor", "TickSpeedMixin", "WindowOffScreenMixin", "WindowSizeAccessor", + "WorldListWidgetLevelSummaryAccessor", "WorldRendererCallEntityRenderMixin" ], "server": [],