aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/gui')
-rw-r--r--src/main/kotlin/gui/BarComponent.kt49
-rw-r--r--src/main/kotlin/gui/CheckboxComponent.kt15
-rw-r--r--src/main/kotlin/gui/FirmButtonComponent.kt134
-rw-r--r--src/main/kotlin/gui/FirmHoverComponent.kt92
-rw-r--r--src/main/kotlin/gui/ImageComponent.kt44
-rw-r--r--src/main/kotlin/gui/config/AllConfigsGui.kt39
-rw-r--r--src/main/kotlin/gui/config/BooleanHandler.kt3
-rw-r--r--src/main/kotlin/gui/config/BuiltInConfigScreenProvider.kt4
-rw-r--r--src/main/kotlin/gui/config/ChoiceHandler.kt7
-rw-r--r--src/main/kotlin/gui/config/ClickHandler.kt1
-rw-r--r--src/main/kotlin/gui/config/ColourHandler.kt83
-rw-r--r--src/main/kotlin/gui/config/DurationHandler.kt7
-rw-r--r--src/main/kotlin/gui/config/EnumRenderer.kt8
-rw-r--r--src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt4
-rw-r--r--src/main/kotlin/gui/config/GuiAppender.kt6
-rw-r--r--src/main/kotlin/gui/config/HudMetaHandler.kt21
-rw-r--r--src/main/kotlin/gui/config/IntegerHandler.kt6
-rw-r--r--src/main/kotlin/gui/config/JAnyHud.kt67
-rw-r--r--src/main/kotlin/gui/config/KeyBindingHandler.kt38
-rw-r--r--src/main/kotlin/gui/config/KeyBindingStateManager.kt117
-rw-r--r--src/main/kotlin/gui/config/ManagedConfig.kt252
-rw-r--r--src/main/kotlin/gui/config/ManagedConfigElement.kt8
-rw-r--r--src/main/kotlin/gui/config/ManagedOption.kt15
-rw-r--r--src/main/kotlin/gui/config/StringHandler.kt7
-rw-r--r--src/main/kotlin/gui/config/storage/ConfigLoadContext.kt98
-rw-r--r--src/main/kotlin/gui/config/storage/ConfigStorageClass.kt8
-rw-r--r--src/main/kotlin/gui/config/storage/FirmamentConfigLoader.kt252
-rw-r--r--src/main/kotlin/gui/config/storage/FirstLevelSplitJsonFolder.kt109
-rw-r--r--src/main/kotlin/gui/config/storage/LegacyImporter.kt68
-rw-r--r--src/main/kotlin/gui/config/storage/README.md68
-rw-r--r--src/main/kotlin/gui/entity/EntityModifier.kt2
-rw-r--r--src/main/kotlin/gui/entity/EntityRenderer.kt168
-rw-r--r--src/main/kotlin/gui/entity/FakeWorld.kt343
-rw-r--r--src/main/kotlin/gui/entity/GuiPlayer.kt60
-rw-r--r--src/main/kotlin/gui/entity/ModifyAge.kt16
-rw-r--r--src/main/kotlin/gui/entity/ModifyCharged.kt8
-rw-r--r--src/main/kotlin/gui/entity/ModifyEquipment.kt24
-rw-r--r--src/main/kotlin/gui/entity/ModifyHorse.kt94
-rw-r--r--src/main/kotlin/gui/entity/ModifyInvisible.kt2
-rw-r--r--src/main/kotlin/gui/entity/ModifyName.kt6
-rw-r--r--src/main/kotlin/gui/entity/ModifyPlayerSkin.kt84
-rw-r--r--src/main/kotlin/gui/entity/ModifyRiding.kt4
-rw-r--r--src/main/kotlin/gui/entity/ModifyWither.kt8
-rw-r--r--src/main/kotlin/gui/hud/MoulConfigHud.kt96
44 files changed, 1352 insertions, 1193 deletions
diff --git a/src/main/kotlin/gui/BarComponent.kt b/src/main/kotlin/gui/BarComponent.kt
index b82c666..4c0a52d 100644
--- a/src/main/kotlin/gui/BarComponent.kt
+++ b/src/main/kotlin/gui/BarComponent.kt
@@ -1,16 +1,14 @@
package moe.nea.firmament.gui
-import com.mojang.blaze3d.systems.RenderSystem
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
-import io.github.notenoughupdates.moulconfig.common.RenderContext
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import io.github.notenoughupdates.moulconfig.observer.GetSetter
-import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
+import io.github.notenoughupdates.moulconfig.platform.MoulConfigRenderContext
import me.shedaniel.math.Color
-import net.minecraft.client.gui.DrawContext
-import net.minecraft.client.render.RenderLayer
-import net.minecraft.util.Identifier
+import net.minecraft.client.renderer.RenderPipelines
+import net.minecraft.client.gui.GuiGraphics
+import net.minecraft.resources.ResourceLocation
import moe.nea.firmament.Firmament
class BarComponent(
@@ -27,13 +25,13 @@ class BarComponent(
}
data class Texture(
- val identifier: Identifier,
- val u1: Float, val v1: Float,
- val u2: Float, val v2: Float,
+ val identifier: ResourceLocation,
+ val u1: Float, val v1: Float,
+ val u2: Float, val v2: Float,
) {
- fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) {
- context.drawTexturedQuad(
- RenderLayer::getGuiTextured,
+ fun draw(context: GuiGraphics, x: Int, y: Int, width: Int, height: Int, color: Color) {
+ context.innerBlit(
+ RenderPipelines.GUI_TEXTURED,
identifier,
x, y, x + width, x + height,
u1, u2, v1, v2,
@@ -51,13 +49,13 @@ class BarComponent(
}
private fun drawSection(
- context: DrawContext,
- texture: Texture,
- x: Int,
- y: Int,
- width: Int,
- sectionStart: Double,
- sectionEnd: Double
+ context: GuiGraphics,
+ texture: Texture,
+ x: Int,
+ y: Int,
+ width: Int,
+ sectionStart: Double,
+ sectionEnd: Double
) {
if (sectionEnd < progress.get() && width == 4) {
texture.draw(context, x, y, 4, 8, fillColor)
@@ -81,7 +79,7 @@ class BarComponent(
}
override fun render(context: GuiImmediateContext) {
- val renderContext = (context.renderContext as ModernRenderContext).drawContext
+ val renderContext = (context.renderContext as MoulConfigRenderContext).drawContext
var i = 0
val x = 0
val y = 0
@@ -104,20 +102,11 @@ class BarComponent(
(context.width - 4) * total.get() / context.width,
total.get()
)
- RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
}
}
-fun Identifier.toMoulConfig(): MyResourceLocation {
+fun ResourceLocation.toMoulConfig(): MyResourceLocation {
return MyResourceLocation(this.namespace, this.path)
}
-
-fun RenderContext.color(color: Color) {
- color(color.red, color.green, color.blue, color.alpha)
-}
-
-fun RenderContext.color(red: Int, green: Int, blue: Int, alpha: Int) {
- color(red / 255f, green / 255f, blue / 255f, alpha / 255f)
-}
diff --git a/src/main/kotlin/gui/CheckboxComponent.kt b/src/main/kotlin/gui/CheckboxComponent.kt
index 761c086..da3e5c8 100644
--- a/src/main/kotlin/gui/CheckboxComponent.kt
+++ b/src/main/kotlin/gui/CheckboxComponent.kt
@@ -4,8 +4,8 @@ import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import io.github.notenoughupdates.moulconfig.gui.MouseEvent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
-import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
-import net.minecraft.client.render.RenderLayer
+import io.github.notenoughupdates.moulconfig.platform.MoulConfigRenderContext
+import net.minecraft.client.renderer.RenderPipelines
import moe.nea.firmament.Firmament
class CheckboxComponent<T>(
@@ -25,11 +25,11 @@ class CheckboxComponent<T>(
}
override fun render(context: GuiImmediateContext) {
- val ctx = (context.renderContext as ModernRenderContext).drawContext
- ctx.drawGuiTexture(
- RenderLayer::getGuiTextured,
- if (isEnabled()) Firmament.identifier("firmament:widget/checkbox_checked")
- else Firmament.identifier("firmament:widget/checkbox_unchecked"),
+ val ctx = (context.renderContext as MoulConfigRenderContext).drawContext
+ ctx.blitSprite(
+ RenderPipelines.GUI_TEXTURED,
+ if (isEnabled()) Firmament.identifier("widget/checkbox_checked")
+ else Firmament.identifier("widget/checkbox_unchecked"),
0, 0,
16, 16
)
@@ -43,6 +43,7 @@ class CheckboxComponent<T>(
isClicking = false
if (context.isHovered)
state.set(value)
+ blur()
return true
}
if (mouseEvent.mouseState && mouseEvent.mouseButton == 0 && context.isHovered) {
diff --git a/src/main/kotlin/gui/FirmButtonComponent.kt b/src/main/kotlin/gui/FirmButtonComponent.kt
index 82e5b05..1469c09 100644
--- a/src/main/kotlin/gui/FirmButtonComponent.kt
+++ b/src/main/kotlin/gui/FirmButtonComponent.kt
@@ -1,4 +1,3 @@
-
package moe.nea.firmament.gui
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
@@ -11,71 +10,78 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter
open class FirmButtonComponent(
- child: GuiComponent,
- val isEnabled: GetSetter<Boolean> = GetSetter.constant(true),
- val noBackground: Boolean = false,
- val action: Runnable,
+ child: GuiComponent,
+ val isEnabled: GetSetter<Boolean> = GetSetter.constant(true),
+ val noBackground: Boolean = false,
+ val action: (mouseButton: Int) -> Unit,
) : PanelComponent(child, if (noBackground) 0 else 2, DefaultBackgroundRenderer.TRANSPARENT) {
- /* TODO: make use of vanillas built in nine slicer */
- val hoveredBg =
- NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_highlighted.png"))
- .cornerSize(5)
- .cornerUv(5 / 200F, 5 / 20F)
- .mode(NinePatch.Mode.STRETCHING)
- .build()
- val unhoveredBg = NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button.png"))
- .cornerSize(5)
- .cornerUv(5 / 200F, 5 / 20F)
- .mode(NinePatch.Mode.STRETCHING)
- .build()
- val disabledBg =
- NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_disabled.png"))
- .cornerSize(5)
- .cornerUv(5 / 200F, 5 / 20F)
- .mode(NinePatch.Mode.STRETCHING)
- .build()
- val activeBg = NinePatch.builder(MyResourceLocation("firmament", "textures/gui/sprites/widget/button_active.png"))
- .cornerSize(5)
- .cornerUv(5 / 200F, 5 / 20F)
- .mode(NinePatch.Mode.STRETCHING)
- .build()
- var isClicking = false
- override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
- if (!isEnabled.get()) return false
- if (isClicking) {
- if (mouseEvent is MouseEvent.Click && !mouseEvent.mouseState && mouseEvent.mouseButton == 0) {
- isClicking = false
- if (context.isHovered) {
- action.run()
- }
- return true
- }
- }
- if (!context.isHovered) return false
- if (mouseEvent !is MouseEvent.Click) return false
- if (mouseEvent.mouseState && mouseEvent.mouseButton == 0) {
- requestFocus()
- isClicking = true
- return true
- }
- return false
- }
+ constructor(
+ child: GuiComponent,
+ isEnabled: GetSetter<Boolean> = GetSetter.constant(true),
+ noBackground: Boolean = false,
+ action: Runnable,
+ ) : this(child, isEnabled, noBackground, { action.run() })
+
+ /* TODO: make use of vanillas built in nine slicer */
+ val hoveredBg =
+ NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_highlighted.png"))
+ .cornerSize(5)
+ .cornerUv(5 / 200F, 5 / 20F)
+ .mode(NinePatch.Mode.STRETCHING)
+ .build()
+ val unhoveredBg = NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button.png"))
+ .cornerSize(5)
+ .cornerUv(5 / 200F, 5 / 20F)
+ .mode(NinePatch.Mode.STRETCHING)
+ .build()
+ val disabledBg =
+ NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_disabled.png"))
+ .cornerSize(5)
+ .cornerUv(5 / 200F, 5 / 20F)
+ .mode(NinePatch.Mode.STRETCHING)
+ .build()
+ val activeBg = NinePatch.builder(MyResourceLocation("firmament", "textures/gui/sprites/widget/button_active.png"))
+ .cornerSize(5)
+ .cornerUv(5 / 200F, 5 / 20F)
+ .mode(NinePatch.Mode.STRETCHING)
+ .build()
+ var isClicking = false
+ override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
+ if (!isEnabled.get()) return false
+ if (isClicking) {
+ if (mouseEvent is MouseEvent.Click && !mouseEvent.mouseState) {
+ isClicking = false
+ if (context.isHovered) {
+ action.invoke(mouseEvent.mouseButton)
+ }
+ return true
+ }
+ }
+ if (!context.isHovered) return false
+ if (mouseEvent !is MouseEvent.Click) return false
+ if (mouseEvent.mouseState) {
+ requestFocus()
+ isClicking = true
+ return true
+ }
+ return false
+ }
- open fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> =
- if (!isEnabled.get()) disabledBg
- else if (context.isHovered || isClicking) hoveredBg
- else unhoveredBg
+ open fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> =
+ if (!isEnabled.get()) disabledBg
+ else if (context.isHovered || isClicking) hoveredBg
+ else unhoveredBg
- override fun render(context: GuiImmediateContext) {
- context.renderContext.pushMatrix()
- if (!noBackground)
- context.renderContext.drawNinePatch(
- getBackground(context),
- 0f, 0f, context.width, context.height
- )
- context.renderContext.translate(insets.toFloat(), insets.toFloat(), 0f)
- element.render(getChildContext(context))
- context.renderContext.popMatrix()
- }
+ override fun render(context: GuiImmediateContext) {
+ context.renderContext.pushMatrix()
+ if (!noBackground)
+ context.renderContext.drawNinePatch(
+ getBackground(context),
+ 0f, 0f, context.width, context.height
+ )
+ context.renderContext.translate(insets.toFloat(), insets.toFloat())
+ element.render(getChildContext(context))
+ context.renderContext.popMatrix()
+ }
}
diff --git a/src/main/kotlin/gui/FirmHoverComponent.kt b/src/main/kotlin/gui/FirmHoverComponent.kt
index b1792ce..eed795a 100644
--- a/src/main/kotlin/gui/FirmHoverComponent.kt
+++ b/src/main/kotlin/gui/FirmHoverComponent.kt
@@ -1,5 +1,6 @@
package moe.nea.firmament.gui
+import io.github.notenoughupdates.moulconfig.common.text.StructuredText
import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
@@ -10,50 +11,51 @@ import kotlin.time.Duration
import moe.nea.firmament.util.TimeMark
class FirmHoverComponent(
- val child: GuiComponent,
- val hoverLines: Supplier<List<String>>,
- val hoverDelay: Duration,
+ val child: GuiComponent,
+ val hoverLines: Supplier<List<String>>,
+ val hoverDelay: Duration,
) : GuiComponent() {
- override fun getWidth(): Int {
- return child.width
- }
-
- override fun getHeight(): Int {
- return child.height
- }
-
- override fun <T : Any?> foldChildren(
- initial: T,
- visitor: BiFunction<GuiComponent, T, T>
- ): T {
- return visitor.apply(child, initial)
- }
-
- override fun render(context: GuiImmediateContext) {
- if (context.isHovered && (permaHover || lastMouseMove.passedTime() > hoverDelay)) {
- context.renderContext.scheduleDrawTooltip(hoverLines.get())
- permaHover = true
- } else {
- permaHover = false
- }
- if (!context.isHovered) {
- lastMouseMove = TimeMark.now()
- }
- child.render(context)
-
- }
-
- var permaHover = false
- var lastMouseMove = TimeMark.farPast()
-
- override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
- if (mouseEvent is MouseEvent.Move) {
- lastMouseMove = TimeMark.now()
- }
- return child.mouseEvent(mouseEvent, context)
- }
-
- override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
- return child.keyboardEvent(event, context)
- }
+ override fun getWidth(): Int {
+ return child.width
+ }
+
+ override fun getHeight(): Int {
+ return child.height
+ }
+
+ override fun <T : Any?> foldChildren(
+ initial: T,
+ visitor: BiFunction<GuiComponent, T, T>
+ ): T {
+ return visitor.apply(child, initial)
+ }
+
+ override fun render(context: GuiImmediateContext) {
+ if (context.isHovered && (permaHover || lastMouseMove.passedTime() > hoverDelay)) {
+ context.renderContext.scheduleDrawTooltip(context.mouseX, context.mouseY, hoverLines.get()
+ .map { it -> StructuredText.of(it) })
+ permaHover = true
+ } else {
+ permaHover = false
+ }
+ if (!context.isHovered) {
+ lastMouseMove = TimeMark.now()
+ }
+ child.render(context)
+
+ }
+
+ var permaHover = false
+ var lastMouseMove = TimeMark.farPast()
+
+ override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean {
+ if (mouseEvent is MouseEvent.Move) {
+ lastMouseMove = TimeMark.now()
+ }
+ return child.mouseEvent(mouseEvent, context)
+ }
+
+ override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
+ return child.keyboardEvent(event, context)
+ }
}
diff --git a/src/main/kotlin/gui/ImageComponent.kt b/src/main/kotlin/gui/ImageComponent.kt
index bba7dee..695c0ed 100644
--- a/src/main/kotlin/gui/ImageComponent.kt
+++ b/src/main/kotlin/gui/ImageComponent.kt
@@ -6,28 +6,30 @@ import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import java.util.function.Supplier
class ImageComponent(
- private val width: Int,
- private val height: Int,
- val resourceLocation: Supplier<MyResourceLocation>,
- val u1: Float,
- val u2: Float,
- val v1: Float,
- val v2: Float,
+ private val width: Int,
+ private val height: Int,
+ val resourceLocation: Supplier<MyResourceLocation>,
+ val u1: Float,
+ val u2: Float,
+ val v1: Float,
+ val v2: Float,
) : GuiComponent() {
- override fun getWidth(): Int {
- return width
- }
+ override fun getWidth(): Int {
+ return width
+ }
- override fun getHeight(): Int {
- return height
- }
+ override fun getHeight(): Int {
+ return height
+ }
- override fun render(context: GuiImmediateContext) {
- context.renderContext.bindTexture(resourceLocation.get())
- context.renderContext.drawTexturedRect(
- 0f, 0f,
- context.width.toFloat(), context.height.toFloat(),
- u1, v1, u2, v2
- )
- }
+ override fun render(context: GuiImmediateContext) {
+ context.renderContext.drawComplexTexture(
+ resourceLocation.get(),
+ 0f, 0f,
+ context.width.toFloat(), context.height.toFloat(),
+ {
+ it.uv(u1, v1, u2, v2)
+ }
+ )
+ }
}
diff --git a/src/main/kotlin/gui/config/AllConfigsGui.kt b/src/main/kotlin/gui/config/AllConfigsGui.kt
index 73ff444..60711ca 100644
--- a/src/main/kotlin/gui/config/AllConfigsGui.kt
+++ b/src/main/kotlin/gui/config/AllConfigsGui.kt
@@ -2,11 +2,19 @@ package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.observer.ObservableList
import io.github.notenoughupdates.moulconfig.xml.Bind
-import net.minecraft.client.gui.screen.Screen
-import net.minecraft.text.Text
+import net.minecraft.client.gui.screens.Screen
+import net.minecraft.network.chat.Component
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.commands.RestArgumentType
+import moe.nea.firmament.commands.get
+import moe.nea.firmament.commands.thenArgument
+import moe.nea.firmament.commands.thenExecute
+import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.MoulConfigUtils
import moe.nea.firmament.util.ScreenUtil.setScreenLater
+import moe.nea.firmament.util.data.Config
+import moe.nea.firmament.util.data.ManagedConfig
object AllConfigsGui {
//
@@ -15,9 +23,11 @@ object AllConfigsGui {
// RepoManager.Config
// ) + FeatureManager.allFeatures.mapNotNull { it.config }
+ @Config
object ConfigConfig : ManagedConfig("configconfig", Category.META) {
val enableYacl by toggle("enable-yacl") { false }
val enableMoulConfig by toggle("enable-moulconfig") { true }
+ val enableWideMC by toggle("wide-moulconfig") { false }
}
fun <T> List<T>.toObservableList(): ObservableList<T> = ObservableList(this)
@@ -27,16 +37,16 @@ object AllConfigsGui {
val configs = category.configs.map { EntryMapping(it) }.toObservableList()
@Bind
- fun name() = category.labelText.string
+ fun name() = category.labelText
@Bind
fun close() {
- MC.screen?.close()
+ MC.screen?.onClose()
}
class EntryMapping(val config: ManagedConfig) {
@Bind
- fun name() = Text.translatable("firmament.config.${config.name}").string
+ fun name() = Component.translatable("firmament.config.${config.name}")
@Bind
fun openEditor() {
@@ -53,7 +63,7 @@ object AllConfigsGui {
class CategoryEntry(val category: ManagedConfig.Category) {
@Bind
- fun name() = category.labelText.string
+ fun name() = category.labelText
@Bind
fun open() {
@@ -66,7 +76,7 @@ object AllConfigsGui {
return MoulConfigUtils.loadScreen("config/main", CategoryView(), parent)
}
- fun makeScreen(parent: Screen? = null): Screen {
+ fun makeScreen(search: String? = null, parent: Screen? = null): Screen {
val wantedKey = when {
ConfigConfig.enableMoulConfig -> "moulconfig"
ConfigConfig.enableYacl -> "yacl"
@@ -74,10 +84,23 @@ object AllConfigsGui {
}
val provider = FirmamentConfigScreenProvider.providers.find { it.key == wantedKey }
?: FirmamentConfigScreenProvider.providers.first()
- return provider.open(parent)
+ return provider.open(search, parent)
}
fun showAllGuis() {
setScreenLater(makeScreen())
}
+
+ @Subscribe
+ fun registerCommands(event: CommandEvent.SubCommand) {
+ event.subcommand("search") {
+ thenArgument("search", RestArgumentType) { search ->
+ thenExecute {
+ val search = this[search]
+ setScreenLater(makeScreen(search = search))
+ }
+ }
+ }
+ }
+
}
diff --git a/src/main/kotlin/gui/config/BooleanHandler.kt b/src/main/kotlin/gui/config/BooleanHandler.kt
index 8592777..b954401 100644
--- a/src/main/kotlin/gui/config/BooleanHandler.kt
+++ b/src/main/kotlin/gui/config/BooleanHandler.kt
@@ -9,6 +9,7 @@ import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.jsonPrimitive
+import moe.nea.firmament.util.data.ManagedConfig
class BooleanHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<Boolean> {
override fun toJson(element: Boolean): JsonElement? {
@@ -29,7 +30,7 @@ class BooleanHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<Bo
override fun set(newValue: Boolean) {
opt.set(newValue)
- config.save()
+ config.markDirty()
}
}, 200)
))
diff --git a/src/main/kotlin/gui/config/BuiltInConfigScreenProvider.kt b/src/main/kotlin/gui/config/BuiltInConfigScreenProvider.kt
index 19e7383..6495e68 100644
--- a/src/main/kotlin/gui/config/BuiltInConfigScreenProvider.kt
+++ b/src/main/kotlin/gui/config/BuiltInConfigScreenProvider.kt
@@ -1,14 +1,14 @@
package moe.nea.firmament.gui.config
import com.google.auto.service.AutoService
-import net.minecraft.client.gui.screen.Screen
+import net.minecraft.client.gui.screens.Screen
@AutoService(FirmamentConfigScreenProvider::class)
class BuiltInConfigScreenProvider : FirmamentConfigScreenProvider {
override val key: String
get() = "builtin"
- override fun open(parent: Screen?): Screen {
+ override fun open(search: String?, parent: Screen?): Screen {
return AllConfigsGui.makeBuiltInScreen(parent)
}
}
diff --git a/src/main/kotlin/gui/config/ChoiceHandler.kt b/src/main/kotlin/gui/config/ChoiceHandler.kt
index 2ea3efc..494d08a 100644
--- a/src/main/kotlin/gui/config/ChoiceHandler.kt
+++ b/src/main/kotlin/gui/config/ChoiceHandler.kt
@@ -7,16 +7,17 @@ import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import kotlinx.serialization.json.JsonElement
import kotlin.jvm.optionals.getOrNull
-import net.minecraft.util.StringIdentifiable
+import net.minecraft.util.StringRepresentable
import moe.nea.firmament.gui.CheckboxComponent
import moe.nea.firmament.util.ErrorUtil
+import moe.nea.firmament.util.data.ManagedConfig
import moe.nea.firmament.util.json.KJsonOps
class ChoiceHandler<E>(
val enumClass: Class<E>,
val universe: List<E>,
-) : ManagedConfig.OptionHandler<E> where E : Enum<E>, E : StringIdentifiable {
- val codec = StringIdentifiable.createCodec {
+) : ManagedConfig.OptionHandler<E> where E : Enum<E>, E : StringRepresentable {
+ val codec = StringRepresentable.fromEnum {
@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
(universe as java.util.List<*>).toArray(arrayOfNulls<Enum<E>>(0)) as Array<E>
}
diff --git a/src/main/kotlin/gui/config/ClickHandler.kt b/src/main/kotlin/gui/config/ClickHandler.kt
index fa1c621..9ea83aa 100644
--- a/src/main/kotlin/gui/config/ClickHandler.kt
+++ b/src/main/kotlin/gui/config/ClickHandler.kt
@@ -5,6 +5,7 @@ package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import kotlinx.serialization.json.JsonElement
import moe.nea.firmament.gui.FirmButtonComponent
+import moe.nea.firmament.util.data.ManagedConfig
class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : ManagedConfig.OptionHandler<Unit> {
override fun toJson(element: Unit): JsonElement? {
diff --git a/src/main/kotlin/gui/config/ColourHandler.kt b/src/main/kotlin/gui/config/ColourHandler.kt
new file mode 100644
index 0000000..33daa6d
--- /dev/null
+++ b/src/main/kotlin/gui/config/ColourHandler.kt
@@ -0,0 +1,83 @@
+package moe.nea.firmament.gui.config
+
+import io.github.notenoughupdates.moulconfig.ChromaColour
+import io.github.notenoughupdates.moulconfig.gui.component.ColorSelectComponent
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonElement
+import moe.nea.firmament.util.data.ManagedConfig
+
+class ColourHandler(val config: ManagedConfig) :
+ ManagedConfig.OptionHandler<ChromaColour> {
+ @Serializable
+ data class ChromaDelegate(
+ @SerialName("h")
+ val hue: Float,
+ @SerialName("s")
+ val saturation: Float,
+ @SerialName("b")
+ val brightness: Float,
+ @SerialName("a")
+ val alpha: Int,
+ @SerialName("c")
+ val timeForFullRotationInMillis: Int,
+ ) {
+ constructor(delegate: ChromaColour) : this(
+ delegate.hue,
+ delegate.saturation,
+ delegate.brightness,
+ delegate.alpha,
+ delegate.timeForFullRotationInMillis
+ )
+
+ fun into(): ChromaColour = ChromaColour(hue, saturation, brightness, timeForFullRotationInMillis, alpha)
+ }
+
+ object ChromaSerializer : KSerializer<ChromaColour> {
+ override val descriptor: SerialDescriptor
+ get() = SerialDescriptor("FirmChromaColour", ChromaDelegate.serializer().descriptor)
+
+ override fun serialize(
+ encoder: Encoder,
+ value: ChromaColour
+ ) {
+ encoder.encodeSerializableValue(ChromaDelegate.serializer(), ChromaDelegate(value))
+ }
+
+ override fun deserialize(decoder: Decoder): ChromaColour {
+ return decoder.decodeSerializableValue(ChromaDelegate.serializer()).into()
+ }
+ }
+
+ override fun toJson(element: ChromaColour): JsonElement? {
+ return Json.encodeToJsonElement(ChromaSerializer, element)
+ }
+
+ override fun fromJson(element: JsonElement): ChromaColour {
+ return Json.decodeFromJsonElement(ChromaSerializer, element)
+ }
+
+ override fun emitGuiElements(
+ opt: ManagedOption<ChromaColour>,
+ guiAppender: GuiAppender
+ ) {
+ guiAppender.appendLabeledRow(
+ opt.labelText,
+ ColorSelectComponent(
+ 0,
+ 0,
+ opt.value.toLegacyString(),
+ {
+ opt.value = ChromaColour.forLegacyString(it)
+ config.markDirty()
+ },
+ { }
+ )
+ )
+ }
+}
diff --git a/src/main/kotlin/gui/config/DurationHandler.kt b/src/main/kotlin/gui/config/DurationHandler.kt
index 8d485b1..0fc945f 100644
--- a/src/main/kotlin/gui/config/DurationHandler.kt
+++ b/src/main/kotlin/gui/config/DurationHandler.kt
@@ -3,6 +3,7 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.common.IMinecraft
+import io.github.notenoughupdates.moulconfig.common.text.StructuredText
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
@@ -14,8 +15,8 @@ import kotlinx.serialization.json.long
import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.toDuration
-import net.minecraft.text.Text
import moe.nea.firmament.util.FirmFormatters
+import moe.nea.firmament.util.data.ManagedConfig
class DurationHandler(val config: ManagedConfig, val min: Duration, val max: Duration) :
ManagedConfig.OptionHandler<Duration> {
@@ -31,8 +32,8 @@ class DurationHandler(val config: ManagedConfig, val min: Duration, val max: Dur
guiAppender.appendLabeledRow(
opt.labelText,
RowComponent(
- TextComponent(IMinecraft.instance.defaultFontRenderer,
- { FirmFormatters.formatTimespan(opt.value) },
+ TextComponent(IMinecraft.INSTANCE.defaultFontRenderer,
+ { StructuredText.of(FirmFormatters.formatTimespan(opt.value)) },
40,
TextComponent.TextAlignment.CENTER,
true,
diff --git a/src/main/kotlin/gui/config/EnumRenderer.kt b/src/main/kotlin/gui/config/EnumRenderer.kt
index 3b80b7e..a2dee69 100644
--- a/src/main/kotlin/gui/config/EnumRenderer.kt
+++ b/src/main/kotlin/gui/config/EnumRenderer.kt
@@ -1,14 +1,14 @@
package moe.nea.firmament.gui.config
-import net.minecraft.text.Text
+import net.minecraft.network.chat.Component
interface EnumRenderer<E : Any> {
- fun getName(option: ManagedOption<E>, value: E): Text
+ fun getName(option: ManagedOption<E>, value: E): Component
companion object {
fun <E : Enum<E>> default() = object : EnumRenderer<E> {
- override fun getName(option: ManagedOption<E>, value: E): Text {
- return Text.translatable(option.rawLabelText + ".choice." + value.name.lowercase())
+ override fun getName(option: ManagedOption<E>, value: E): Component {
+ return Component.translatable(option.rawLabelText + ".choice." + value.name.lowercase())
}
}
}
diff --git a/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt b/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt
index faad1cc..d2a8ab6 100644
--- a/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt
+++ b/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt
@@ -1,13 +1,13 @@
package moe.nea.firmament.gui.config
-import net.minecraft.client.gui.screen.Screen
+import net.minecraft.client.gui.screens.Screen
import moe.nea.firmament.util.compatloader.CompatLoader
interface FirmamentConfigScreenProvider {
val key: String
val isEnabled: Boolean get() = true
- fun open(parent: Screen?): Screen
+ fun open(search: String?, parent: Screen?): Screen
companion object : CompatLoader<FirmamentConfigScreenProvider>(FirmamentConfigScreenProvider::class) {
val providers by lazy {
diff --git a/src/main/kotlin/gui/config/GuiAppender.kt b/src/main/kotlin/gui/config/GuiAppender.kt
index 329319d..ba28400 100644
--- a/src/main/kotlin/gui/config/GuiAppender.kt
+++ b/src/main/kotlin/gui/config/GuiAppender.kt
@@ -6,8 +6,8 @@ import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
-import net.minecraft.client.gui.screen.Screen
-import net.minecraft.text.Text
+import net.minecraft.client.gui.screens.Screen
+import net.minecraft.network.chat.Component
import moe.nea.firmament.gui.FixedComponent
class GuiAppender(val width: Int, val screenAccessor: () -> Screen) {
@@ -18,7 +18,7 @@ class GuiAppender(val width: Int, val screenAccessor: () -> Screen) {
reloadables.add(reloadable)
}
- fun appendLabeledRow(label: Text, right: GuiComponent) {
+ fun appendLabeledRow(label: Component, right: GuiComponent) {
appendSplitRow(
TextComponent(label.string),
right
diff --git a/src/main/kotlin/gui/config/HudMetaHandler.kt b/src/main/kotlin/gui/config/HudMetaHandler.kt
index a9659ee..915dcf3 100644
--- a/src/main/kotlin/gui/config/HudMetaHandler.kt
+++ b/src/main/kotlin/gui/config/HudMetaHandler.kt
@@ -5,21 +5,29 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
-import net.minecraft.client.gui.screen.Screen
-import net.minecraft.text.MutableText
-import net.minecraft.text.Text
+import net.minecraft.client.gui.screens.Screen
+import net.minecraft.network.chat.MutableComponent
+import net.minecraft.network.chat.Component
+import moe.nea.firmament.Firmament
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.jarvis.JarvisIntegration
import moe.nea.firmament.util.MC
+import moe.nea.firmament.util.data.ManagedConfig
-class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val width: Int, val height: Int) :
+class HudMetaHandler(
+ val config: ManagedConfig,
+ val propertyName: String,
+ val label: MutableComponent,
+ val width: Int,
+ val height: Int
+) :
ManagedConfig.OptionHandler<HudMeta> {
override fun toJson(element: HudMeta): JsonElement? {
return Json.encodeToJsonElement(element.position)
}
override fun fromJson(element: JsonElement): HudMeta {
- return HudMeta(Json.decodeFromJsonElement(element), label, width, height)
+ return HudMeta(Json.decodeFromJsonElement(element), Firmament.identifier(propertyName), label, width, height)
}
fun openEditor(option: ManagedOption<HudMeta>, oldScreen: Screen) {
@@ -34,7 +42,8 @@ class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val widt
opt.labelText,
FirmButtonComponent(
TextComponent(
- Text.stringifiedTranslatable("firmament.hud.edit", label).string),
+ Component.translatableEscape("firmament.hud.edit", label).string
+ ),
) {
openEditor(opt, guiAppender.screenAccessor())
})
diff --git a/src/main/kotlin/gui/config/IntegerHandler.kt b/src/main/kotlin/gui/config/IntegerHandler.kt
index 31ce90f..ab0237a 100644
--- a/src/main/kotlin/gui/config/IntegerHandler.kt
+++ b/src/main/kotlin/gui/config/IntegerHandler.kt
@@ -3,6 +3,7 @@
package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.common.IMinecraft
+import io.github.notenoughupdates.moulconfig.common.text.StructuredText
import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
@@ -12,6 +13,7 @@ import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonPrimitive
import moe.nea.firmament.util.FirmFormatters
+import moe.nea.firmament.util.data.ManagedConfig
class IntegerHandler(val config: ManagedConfig, val min: Int, val max: Int) : ManagedConfig.OptionHandler<Int> {
override fun toJson(element: Int): JsonElement? {
@@ -26,8 +28,8 @@ class IntegerHandler(val config: ManagedConfig, val min: Int, val max: Int) : Ma
guiAppender.appendLabeledRow(
opt.labelText,
RowComponent(
- TextComponent(IMinecraft.instance.defaultFontRenderer,
- { FirmFormatters.formatCommas(opt.value, 0) },
+ TextComponent(IMinecraft.INSTANCE.defaultFontRenderer,
+ { StructuredText.of(FirmFormatters.formatCommas(opt.value, 0)) },
40,
TextComponent.TextAlignment.CENTER,
true,
diff --git a/src/main/kotlin/gui/config/JAnyHud.kt b/src/main/kotlin/gui/config/JAnyHud.kt
index 35c4eb2..63975c6 100644
--- a/src/main/kotlin/gui/config/JAnyHud.kt
+++ b/src/main/kotlin/gui/config/JAnyHud.kt
@@ -1,48 +1,67 @@
-
-
package moe.nea.firmament.gui.config
import moe.nea.jarvis.api.JarvisHud
-import moe.nea.jarvis.api.JarvisScalable
+import org.joml.Matrix3x2f
+import org.joml.Vector2i
+import org.joml.Vector2ic
import kotlinx.serialization.Serializable
-import net.minecraft.text.Text
+import net.minecraft.network.chat.Component
+import net.minecraft.resources.ResourceLocation
+import moe.nea.firmament.jarvis.JarvisIntegration
@Serializable
data class HudPosition(
- var x: Double,
- var y: Double,
- var scale: Float,
+ var x: Int,
+ var y: Int,
+ var scale: Float,
)
data class HudMeta(
val position: HudPosition,
- private val label: Text,
+ private val id: ResourceLocation,
+ private val label: Component,
private val width: Int,
private val height: Int,
-) : JarvisScalable, JarvisHud {
- override fun getX(): Double = position.x
+) : JarvisHud, JarvisHud.Scalable {
+ override fun getLabel(): Component = label
+ override fun getUnscaledWidth(): Int {
+ return width
+ }
+
+ override fun getUnscaledHeight(): Int {
+ return height
+ }
- override fun setX(newX: Double) {
- position.x = newX
- }
+ override fun getHudId(): ResourceLocation {
+ return id
+ }
- override fun getY(): Double = position.y
+ override fun getPosition(): Vector2ic {
+ return Vector2i(position.x, position.y)
+ }
- override fun setY(newY: Double) {
- position.y = newY
- }
+ override fun setPosition(p0: Vector2ic) {
+ position.x = p0.x()
+ position.y = p0.y()
+ }
- override fun getLabel(): Text = label
+ override fun isEnabled(): Boolean {
+ return true // TODO: this should be actually truthful, if possible
+ }
- override fun getWidth(): Int = width
+ override fun isVisible(): Boolean {
+ return true // TODO: this should be actually truthful, if possible
+ }
- override fun getHeight(): Int = height
+ override fun getScale(): Float = position.scale
- override fun getScale(): Float = position.scale
+ override fun setScale(newScale: Float) {
+ position.scale = newScale
+ }
- override fun setScale(newScale: Float) {
- position.scale = newScale
- }
+ fun applyTransformations(matrix4f: Matrix3x2f) {
+ applyTransformations(JarvisIntegration.jarvis, matrix4f)
+ }
}
diff --git a/src/main/kotlin/gui/config/KeyBindingHandler.kt b/src/main/kotlin/gui/config/KeyBindingHandler.kt
index d7d0b47..3c08da2 100644
--- a/src/main/kotlin/gui/config/KeyBindingHandler.kt
+++ b/src/main/kotlin/gui/config/KeyBindingHandler.kt
@@ -1,11 +1,5 @@
package moe.nea.firmament.gui.config
-import io.github.notenoughupdates.moulconfig.common.IMinecraft
-import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
-import io.github.notenoughupdates.moulconfig.deps.libninepatch.NinePatch
-import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
-import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
-import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement
@@ -13,6 +7,7 @@ import kotlinx.serialization.json.encodeToJsonElement
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.keybindings.FirmamentKeyBindings
import moe.nea.firmament.keybindings.SavedKeyBinding
+import moe.nea.firmament.util.data.ManagedConfig
class KeyBindingHandler(val name: String, val managedConfig: ManagedConfig) :
ManagedConfig.OptionHandler<SavedKeyBinding> {
@@ -35,39 +30,12 @@ class KeyBindingHandler(val name: String, val managedConfig: ManagedConfig) :
{ opt.value },
{
opt.value = it
- opt.element.save()
+ opt.element.markDirty()
},
{ button.blur() },
{ button.requestFocus() }
)
- button = object : FirmButtonComponent(
- TextComponent(
- IMinecraft.instance.defaultFontRenderer,
- { sm.label.string },
- 130,
- TextComponent.TextAlignment.LEFT,
- false,
- false
- ), action = {
- sm.onClick()
- }) {
- override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
- if (event is KeyboardEvent.KeyPressed) {
- return sm.keyboardEvent(event.keycode, event.pressed)
- }
- return super.keyboardEvent(event, context)
- }
-
- override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> {
- if (sm.editing) return activeBg
- return super.getBackground(context)
- }
-
-
- override fun onLostFocus() {
- sm.onLostFocus()
- }
- }
+ button = sm.createButton()
sm.updateLabel()
return button
}
diff --git a/src/main/kotlin/gui/config/KeyBindingStateManager.kt b/src/main/kotlin/gui/config/KeyBindingStateManager.kt
index cc8178d..9cf2771 100644
--- a/src/main/kotlin/gui/config/KeyBindingStateManager.kt
+++ b/src/main/kotlin/gui/config/KeyBindingStateManager.kt
@@ -1,8 +1,18 @@
package moe.nea.firmament.gui.config
+import io.github.notenoughupdates.moulconfig.common.IMinecraft
+import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
+import io.github.notenoughupdates.moulconfig.deps.libninepatch.NinePatch
+import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
+import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
+import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
+import io.github.notenoughupdates.moulconfig.platform.MoulConfigPlatform
import org.lwjgl.glfw.GLFW
-import net.minecraft.text.Text
-import net.minecraft.util.Formatting
+import net.minecraft.network.chat.Component
+import net.minecraft.ChatFormatting
+import moe.nea.firmament.gui.FirmButtonComponent
+import moe.nea.firmament.keybindings.GenericInputButton
+import moe.nea.firmament.keybindings.InputModifiers
import moe.nea.firmament.keybindings.SavedKeyBinding
class KeyBindingStateManager(
@@ -12,73 +22,65 @@ class KeyBindingStateManager(
val requestFocus: () -> Unit,
) {
var editing = false
- var lastPressed = 0
- var lastPressedNonModifier = 0
- var label: Text = Text.literal("")
+ var lastPressed: GenericInputButton? = null
+ var label: Component = Component.literal("")
- fun onClick() {
+ fun onClick(mouseButton: Int) {
if (editing) {
- editing = false
- blur()
- } else {
+ keyboardEvent(GenericInputButton.mouse(mouseButton), true)
+ } else if (mouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) {
editing = true
requestFocus()
}
updateLabel()
}
- fun keyboardEvent(keyCode: Int, pressed: Boolean): Boolean {
- return if (pressed) onKeyPressed(keyCode, SavedKeyBinding.getModInt())
- else onKeyReleased(keyCode, SavedKeyBinding.getModInt())
+ fun keyboardEvent(keyCode: GenericInputButton, pressed: Boolean): Boolean {
+ return if (pressed) onKeyPressed(keyCode, InputModifiers.current())
+ else onKeyReleased(keyCode, InputModifiers.current())
}
- fun onKeyPressed(ch: Int, modifiers: Int): Boolean {
+ fun onKeyPressed(
+ ch: GenericInputButton,
+ modifiers: InputModifiers
+ ): Boolean { // TODO !!!!!: genericify this method to allow for other inputs
if (!editing) {
return false
}
- if (ch == GLFW.GLFW_KEY_ESCAPE) {
- lastPressedNonModifier = 0
+ if (ch == GenericInputButton.escape()) {
editing = false
- lastPressed = 0
- setValue(SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN))
+ lastPressed = null
+ setValue(SavedKeyBinding.unbound())
updateLabel()
blur()
return true
}
- if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT
- || ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT
- || ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL
- ) {
+ if (ch.isModifier()) {
lastPressed = ch
} else {
- setValue(SavedKeyBinding(
- ch, modifiers
- ))
+ setValue(SavedKeyBinding(ch, modifiers))
editing = false
blur()
- lastPressed = 0
- lastPressedNonModifier = 0
+ lastPressed = null
}
updateLabel()
return true
}
fun onLostFocus() {
- lastPressedNonModifier = 0
editing = false
- lastPressed = 0
+ lastPressed = null
updateLabel()
}
- fun onKeyReleased(ch: Int, modifiers: Int): Boolean {
+ fun onKeyReleased(ch: GenericInputButton, modifiers: InputModifiers): Boolean {
if (!editing)
return false
- if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) {
+ if (ch == lastPressed) { // TODO: check modifiers dont duplicate (CTRL+CTRL)
setValue(SavedKeyBinding(ch, modifiers))
editing = false
blur()
- lastPressed = 0
- lastPressedNonModifier = 0
+ lastPressed = null
}
updateLabel()
return true
@@ -87,22 +89,51 @@ class KeyBindingStateManager(
fun updateLabel() {
var stroke = value().format()
if (editing) {
- stroke = Text.literal("")
- val (shift, ctrl, alt) = SavedKeyBinding.getMods(SavedKeyBinding.getModInt())
- if (shift) {
- stroke.append("SHIFT + ")
- }
- if (alt) {
- stroke.append("ALT + ")
- }
- if (ctrl) {
- stroke.append("CTRL + ")
+ stroke = Component.empty()
+ val modifiers = InputModifiers.current()
+ if (!modifiers.isEmpty()) {
+ stroke.append(modifiers.format())
+ stroke.append(" + ")
}
stroke.append("???")
- stroke.styled { it.withColor(Formatting.YELLOW) }
+ stroke.withStyle { it.withColor(ChatFormatting.YELLOW) }
}
label = stroke
}
+ fun createButton(): FirmButtonComponent {
+ return object : FirmButtonComponent(
+ TextComponent(
+ IMinecraft.INSTANCE.defaultFontRenderer,
+ { MoulConfigPlatform.wrap(this@KeyBindingStateManager.label) },
+ 130,
+ TextComponent.TextAlignment.LEFT,
+ false,
+ false
+ ), action = {
+ this@KeyBindingStateManager.onClick(it)
+ }) {
+ override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
+ if (event is KeyboardEvent.KeyPressed) {
+ return this@KeyBindingStateManager.keyboardEvent(
+ GenericInputButton.ofKeyAndScan(
+ event.keycode,
+ event.scancode
+ ), event.pressed
+ )
+ }
+ return super.keyboardEvent(event, context)
+ }
+ override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> {
+ if (this@KeyBindingStateManager.editing) return activeBg
+ return super.getBackground(context)
+ }
+
+
+ override fun onLostFocus() {
+ this@KeyBindingStateManager.onLostFocus()
+ }
+ }
+ }
}
diff --git a/src/main/kotlin/gui/config/ManagedConfig.kt b/src/main/kotlin/gui/config/ManagedConfig.kt
deleted file mode 100644
index 7ddda9e..0000000
--- a/src/main/kotlin/gui/config/ManagedConfig.kt
+++ /dev/null
@@ -1,252 +0,0 @@
-package moe.nea.firmament.gui.config
-
-import com.mojang.serialization.Codec
-import io.github.notenoughupdates.moulconfig.gui.CloseEventListener
-import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
-import io.github.notenoughupdates.moulconfig.gui.GuiContext
-import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent
-import io.github.notenoughupdates.moulconfig.gui.component.ColumnComponent
-import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
-import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
-import io.github.notenoughupdates.moulconfig.gui.component.ScrollPanelComponent
-import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
-import moe.nea.jarvis.api.Point
-import org.lwjgl.glfw.GLFW
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.JsonObject
-import kotlin.io.path.createDirectories
-import kotlin.io.path.readText
-import kotlin.io.path.writeText
-import kotlin.time.Duration
-import net.minecraft.client.gui.screen.Screen
-import net.minecraft.text.Text
-import net.minecraft.util.StringIdentifiable
-import moe.nea.firmament.Firmament
-import moe.nea.firmament.gui.FirmButtonComponent
-import moe.nea.firmament.keybindings.SavedKeyBinding
-import moe.nea.firmament.util.ScreenUtil.setScreenLater
-import moe.nea.firmament.util.collections.InstanceList
-
-abstract class ManagedConfig(
- override val name: String,
- val category: Category,
- // TODO: allow vararg secondaryCategories: Category,
-) : ManagedConfigElement() {
- enum class Category {
- // Böse Kategorie, nicht benutzten lol
- MISC,
- CHAT,
- INVENTORY,
- MINING,
- EVENTS,
- INTEGRATIONS,
- META,
- DEV,
- ;
-
- val labelText: Text = Text.translatable("firmament.config.category.${name.lowercase()}")
- val description: Text = Text.translatable("firmament.config.category.${name.lowercase()}.description")
- val configs: MutableList<ManagedConfig> = mutableListOf()
- }
-
- companion object {
- val allManagedConfigs = InstanceList<ManagedConfig>("ManagedConfig")
- }
-
- interface OptionHandler<T : Any> {
- fun initOption(opt: ManagedOption<T>) {}
- fun toJson(element: T): JsonElement?
- fun fromJson(element: JsonElement): T
- fun emitGuiElements(opt: ManagedOption<T>, guiAppender: GuiAppender)
- }
-
- init {
- allManagedConfigs.getAll().forEach {
- require(it.name != name) { "Duplicate name '$name' used for config" }
- }
- allManagedConfigs.add(this)
- category.configs.add(this)
- }
-
- val file = Firmament.CONFIG_DIR.resolve("$name.json")
- val data: JsonObject by lazy {
- try {
- Firmament.json.decodeFromString(
- file.readText()
- )
- } catch (e: Exception) {
- Firmament.logger.info("Could not read config $name. Loading empty config.")
- JsonObject(mutableMapOf())
- }
- }
-
- fun save() {
- val data = JsonObject(allOptions.mapNotNull { (key, value) ->
- value.toJson()?.let {
- key to it
- }
- }.toMap())
- file.parent.createDirectories()
- file.writeText(Firmament.json.encodeToString(data))
- }
-
-
- val allOptions = mutableMapOf<String, ManagedOption<*>>()
- val sortedOptions = mutableListOf<ManagedOption<*>>()
-
- private var latestGuiAppender: GuiAppender? = null
-
- protected fun <T : Any> option(
- propertyName: String,
- default: () -> T,
- handler: OptionHandler<T>
- ): ManagedOption<T> {
- if (propertyName in allOptions) error("Cannot register the same name twice")
- return ManagedOption(this, propertyName, default, handler).also {
- it.handler.initOption(it)
- it.load(data)
- allOptions[propertyName] = it
- sortedOptions.add(it)
- }
- }
-
- protected fun toggle(propertyName: String, default: () -> Boolean): ManagedOption<Boolean> {
- return option(propertyName, default, BooleanHandler(this))
- }
-
- protected fun <E> choice(
- propertyName: String,
- enumClass: Class<E>,
- default: () -> E
- ): ManagedOption<E> where E : Enum<E>, E : StringIdentifiable {
- return option(propertyName, default, ChoiceHandler(enumClass, enumClass.enumConstants.toList()))
- }
-
- protected inline fun <reified E> choice(
- propertyName: String,
- noinline default: () -> E
- ): ManagedOption<E> where E : Enum<E>, E : StringIdentifiable {
- return choice(propertyName, E::class.java, default)
- }
-
- private fun <E> createStringIdentifiable(x: () -> Array<out E>): Codec<E> where E : Enum<E>, E : StringIdentifiable {
- return StringIdentifiable.createCodec { x() }
- }
-
- // TODO: wait on https://youtrack.jetbrains.com/issue/KT-73434
-// protected inline fun <reified E> choice(
-// propertyName: String,
-// noinline default: () -> E
-// ): ManagedOption<E> where E : Enum<E>, E : StringIdentifiable {
-// return choice(
-// propertyName,
-// enumEntries<E>().toList(),
-// StringIdentifiable.createCodec { enumValues<E>() },
-// EnumRenderer.default(),
-// default
-// )
-// }
- open fun onChange(option: ManagedOption<*>) {
- }
-
- protected fun duration(
- propertyName: String,
- min: Duration,
- max: Duration,
- default: () -> Duration,
- ): ManagedOption<Duration> {
- return option(propertyName, default, DurationHandler(this, min, max))
- }
-
-
- protected fun position(
- propertyName: String,
- width: Int,
- height: Int,
- default: () -> Point,
- ): ManagedOption<HudMeta> {
- val label = Text.translatable("firmament.config.${name}.${propertyName}")
- return option(propertyName, {
- val p = default()
- HudMeta(HudPosition(p.x, p.y, 1F), label, width, height)
- }, HudMetaHandler(this, label, width, height))
- }
-
- protected fun keyBinding(
- propertyName: String,
- default: () -> Int,
- ): ManagedOption<SavedKeyBinding> = keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding(default()) }
-
- protected fun keyBindingWithOutDefaultModifiers(
- propertyName: String,
- default: () -> SavedKeyBinding,
- ): ManagedOption<SavedKeyBinding> {
- return option(propertyName, default, KeyBindingHandler("firmament.config.${name}.${propertyName}", this))
- }
-
- protected fun keyBindingWithDefaultUnbound(
- propertyName: String,
- ): ManagedOption<SavedKeyBinding> {
- return keyBindingWithOutDefaultModifiers(propertyName) { SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN) }
- }
-
- protected fun integer(
- propertyName: String,
- min: Int,
- max: Int,
- default: () -> Int,
- ): ManagedOption<Int> {
- return option(propertyName, default, IntegerHandler(this, min, max))
- }
-
- protected fun button(propertyName: String, runnable: () -> Unit): ManagedOption<Unit> {
- return option(propertyName, { }, ClickHandler(this, runnable))
- }
-
- protected fun string(propertyName: String, default: () -> String): ManagedOption<String> {
- return option(propertyName, default, StringHandler(this))
- }
-
-
- fun reloadGui() {
- latestGuiAppender?.reloadables?.forEach { it() }
- }
-
- val translationKey get() = "firmament.config.${name}"
- val labelText: Text = Text.translatable(translationKey)
-
- fun getConfigEditor(parent: Screen? = null): Screen {
- var screen: Screen? = null
- val guiapp = GuiAppender(400) { requireNotNull(screen) { "Screen Accessor called too early" } }
- latestGuiAppender = guiapp
- guiapp.appendFullRow(RowComponent(
- FirmButtonComponent(TextComponent("←")) {
- if (parent != null) {
- save()
- setScreenLater(parent)
- } else {
- AllConfigsGui.showAllGuis()
- }
- }
- ))
- sortedOptions.forEach { it.appendToGui(guiapp) }
- guiapp.reloadables.forEach { it() }
- val component = CenterComponent(PanelComponent(ScrollPanelComponent(400, 300, ColumnComponent(guiapp.panel)),
- 10,
- PanelComponent.DefaultBackgroundRenderer.VANILLA))
- screen = object : GuiComponentWrapper(GuiContext(component)) {
- override fun close() {
- if (context.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) {
- client!!.setScreen(parent)
- }
- }
- }
- return screen
- }
-
- fun showConfigEditor(parent: Screen? = null) {
- setScreenLater(getConfigEditor(parent))
- }
-
-}
diff --git a/src/main/kotlin/gui/config/ManagedConfigElement.kt b/src/main/kotlin/gui/config/ManagedConfigElement.kt
deleted file mode 100644
index 28cd6b8..0000000
--- a/src/main/kotlin/gui/config/ManagedConfigElement.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-package moe.nea.firmament.gui.config
-
-abstract class ManagedConfigElement {
- abstract val name: String
-
-}
diff --git a/src/main/kotlin/gui/config/ManagedOption.kt b/src/main/kotlin/gui/config/ManagedOption.kt
index 383f392..4c228de 100644
--- a/src/main/kotlin/gui/config/ManagedOption.kt
+++ b/src/main/kotlin/gui/config/ManagedOption.kt
@@ -5,8 +5,9 @@ import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
-import net.minecraft.text.Text
+import net.minecraft.network.chat.Component
import moe.nea.firmament.util.ErrorUtil
+import moe.nea.firmament.util.data.ManagedConfig
class ManagedOption<T : Any>(
val element: ManagedConfig,
@@ -23,15 +24,15 @@ class ManagedOption<T : Any>(
}
val rawLabelText = "firmament.config.${element.name}.${propertyName}"
- val labelText: Text = Text.translatable(rawLabelText)
+ val labelText: Component = Component.translatable(rawLabelText)
val descriptionTranslationKey = "firmament.config.${element.name}.${propertyName}.description"
- val labelDescription: Text = Text.translatable(descriptionTranslationKey)
+ val labelDescription: Component = Component.translatable(descriptionTranslationKey)
- private var actualValue: T? = null
+ var _actualValue: T? = null
var value: T
- get() = actualValue ?: error("Lateinit variable not initialized")
+ get() = _actualValue ?: error("Lateinit variable not initialized")
set(value) {
- actualValue = value
+ _actualValue = value
element.onChange(this)
}
@@ -49,7 +50,7 @@ class ManagedOption<T : Any>(
value = handler.fromJson(root[propertyName]!!)
return
} catch (e: Exception) {
- ErrorUtil.softError(
+ ErrorUtil.logError(
"Exception during loading of config file ${element.name}. This will reset this config.",
e
)
diff --git a/src/main/kotlin/gui/config/StringHandler.kt b/src/main/kotlin/gui/config/StringHandler.kt
index a326abb..17bb981 100644
--- a/src/main/kotlin/gui/config/StringHandler.kt
+++ b/src/main/kotlin/gui/config/StringHandler.kt
@@ -7,7 +7,8 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonPrimitive
-import net.minecraft.text.Text
+import net.minecraft.network.chat.Component
+import moe.nea.firmament.util.data.ManagedConfig
class StringHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<String> {
override fun toJson(element: String): JsonElement? {
@@ -25,11 +26,11 @@ class StringHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<Str
object : GetSetter<String> by opt {
override fun set(newValue: String) {
opt.set(newValue)
- config.save()
+ config.markDirty()
}
},
130,
- suggestion = Text.translatableWithFallback(opt.rawLabelText + ".hint", "").string
+ suggestion = Component.translatableWithFallback(opt.rawLabelText + ".hint", "").string
),
)
}
diff --git a/src/main/kotlin/gui/config/storage/ConfigLoadContext.kt b/src/main/kotlin/gui/config/storage/ConfigLoadContext.kt
new file mode 100644
index 0000000..4a06ec6
--- /dev/null
+++ b/src/main/kotlin/gui/config/storage/ConfigLoadContext.kt
@@ -0,0 +1,98 @@
+package moe.nea.firmament.gui.config.storage
+
+import java.io.PrintWriter
+import java.nio.file.Path
+import org.apache.commons.io.output.StringBuilderWriter
+import kotlin.io.path.ExperimentalPathApi
+import kotlin.io.path.OnErrorResult
+import kotlin.io.path.Path
+import kotlin.io.path.copyToRecursively
+import kotlin.io.path.createParentDirectories
+import kotlin.io.path.writeText
+import moe.nea.firmament.Firmament
+
+data class ConfigLoadContext(
+ val loadId: String,
+) : AutoCloseable {
+ val backupPath = Path("backups").resolve(Firmament.MOD_ID)
+ .resolve("config-$loadId")
+ .toAbsolutePath()
+ val logFile = Path("logs")
+ .resolve(Firmament.MOD_ID)
+ .resolve("config-$loadId.log")
+ .toAbsolutePath()
+ val logBuffer = StringBuilder()
+
+ var shouldSaveLogBuffer = false
+ fun markShouldSaveLogBuffer() {
+ shouldSaveLogBuffer = true
+ }
+
+ fun logDebug(message: String) {
+ logBuffer.append("[DEBUG] ").append(message).appendLine()
+ }
+
+ fun logInfo(message: String) {
+ if (Firmament.DEBUG)
+ Firmament.logger.info("[ConfigUpgrade] $message")
+ logBuffer.append("[INFO] ").append(message).appendLine()
+ }
+
+ fun logError(message: String, exception: Throwable) {
+ markShouldSaveLogBuffer()
+ if (Firmament.DEBUG)
+ Firmament.logger.error("[ConfigUpgrade] $message", exception)
+ logBuffer.append("[ERROR] ").append(message).appendLine()
+ PrintWriter(StringBuilderWriter(logBuffer)).use {
+ exception.printStackTrace(it)
+ }
+ logBuffer.appendLine()
+ }
+
+ fun logError(message: String) {
+ markShouldSaveLogBuffer()
+ Firmament.logger.error("[ConfigUpgrade] $message")
+ logBuffer.append("[ERROR] ").append(message).appendLine()
+ }
+
+ fun ensureWritable(path: Path) {
+ path.createParentDirectories()
+ }
+
+ fun use(block: (ConfigLoadContext) -> Unit) {
+ try {
+ block(this)
+ } catch (ex: Exception) {
+ logError("Caught exception on CLC", ex)
+ } finally {
+ close()
+ }
+ }
+
+ override fun close() {
+ logInfo("Closing out config load.")
+ if (shouldSaveLogBuffer) {
+ try {
+ ensureWritable(logFile)
+ logFile.writeText(logBuffer.toString())
+ } catch (ex: Exception) {
+ logError("Could not save config load log", ex)
+ }
+ }
+ }
+
+ @OptIn(ExperimentalPathApi::class)
+ fun createBackup(folder: Path, string: String) {
+ val backupDestination = backupPath.resolve("$string-${System.currentTimeMillis()}")
+ logError("Creating backup of $folder in $backupDestination")
+ folder.copyToRecursively(
+ backupDestination.createParentDirectories(),
+ onError = { source: Path, target: Path, exception: Exception ->
+ logError("Failed to copy subtree $source to $target", exception)
+ OnErrorResult.SKIP_SUBTREE
+ },
+ followLinks = false,
+ overwrite = false
+ )
+ }
+}
diff --git a/src/main/kotlin/gui/config/storage/ConfigStorageClass.kt b/src/main/kotlin/gui/config/storage/ConfigStorageClass.kt
new file mode 100644
index 0000000..8258fe7
--- /dev/null
+++ b/src/main/kotlin/gui/config/storage/ConfigStorageClass.kt
@@ -0,0 +1,8 @@
+package moe.nea.firmament.gui.config.storage
+
+enum class ConfigStorageClass { // TODO: make this encode type info somehow
+ PROFILE,
+ STORAGE,
+ CONFIG,
+}
+
diff --git a/src/main/kotlin/gui/config/storage/FirmamentConfigLoader.kt b/src/main/kotlin/gui/config/storage/FirmamentConfigLoader.kt
new file mode 100644
index 0000000..0292721
--- /dev/null
+++ b/src/main/kotlin/gui/config/storage/FirmamentConfigLoader.kt
@@ -0,0 +1,252 @@
+package moe.nea.firmament.gui.config.storage
+
+import java.util.UUID
+import java.util.concurrent.CompletableFuture
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.buildJsonObject
+import kotlinx.serialization.json.jsonObject
+import kotlin.io.path.Path
+import kotlin.io.path.exists
+import kotlin.io.path.forEachDirectoryEntry
+import kotlin.io.path.isDirectory
+import kotlin.io.path.listDirectoryEntries
+import kotlin.io.path.name
+import kotlin.io.path.readText
+import kotlin.io.path.writeText
+import kotlin.time.Duration.Companion.seconds
+import moe.nea.firmament.annotations.Subscribe
+import moe.nea.firmament.events.TickEvent
+import moe.nea.firmament.features.debug.DebugLogger
+import moe.nea.firmament.util.SBData.NULL_UUID
+import moe.nea.firmament.util.TimeMark
+import moe.nea.firmament.util.data.IConfigProvider
+import moe.nea.firmament.util.data.IDataHolder
+import moe.nea.firmament.util.data.ProfileKeyedConfig
+import moe.nea.firmament.util.json.intoGson
+import moe.nea.firmament.util.json.intoKotlinJson
+
+object FirmamentConfigLoader {
+ val currentConfigVersion = 1000
+ val configFolder = Path("config/firmament")
+ .toAbsolutePath()
+ val storageFolder = configFolder.resolve("storage")
+ val profilePath = configFolder.resolve("profiles")
+ val tagLines = listOf(
+ "<- your config version here",
+ "I'm a teapot",
+ "mail.example.com ESMTP",
+ "Apples"
+ )
+ val configVersionFile = configFolder.resolve("config.version")
+
+ fun loadConfig() {
+ if (configFolder.exists()) {
+ if (!configVersionFile.exists()) {
+ LegacyImporter.importFromLegacy()
+ }
+ updateConfigs()
+ }
+
+ ConfigLoadContext("load-${System.currentTimeMillis()}").use { loadContext ->
+ val configData = FirstLevelSplitJsonFolder(loadContext, configFolder).load()
+ loadConfigFromData(configData, Unit, ConfigStorageClass.CONFIG)
+ val storageData = FirstLevelSplitJsonFolder(loadContext, storageFolder).load()
+ loadConfigFromData(storageData, Unit, ConfigStorageClass.STORAGE)
+ var profileData =
+ profilePath.takeIf { it.exists() }
+ ?.listDirectoryEntries()
+ ?.filter { it.isDirectory() }
+ ?.mapNotNull {
+ val uuid= runCatching { UUID.fromString(it.name) }.getOrNull() ?: return@mapNotNull null
+ uuid to FirstLevelSplitJsonFolder(loadContext, it).load()
+ }
+ ?.toMap()
+ if (profileData.isNullOrEmpty())
+ profileData = mapOf(NULL_UUID to JsonObject(mapOf()))
+ profileData.forEach { (key, value) ->
+ loadConfigFromData(value, key, ConfigStorageClass.PROFILE)
+ }
+ }
+ }
+
+ fun <T> loadConfigFromData(
+ configData: JsonObject,
+ key: T?,
+ storageClass: ConfigStorageClass
+ ) {
+ for (holder in allConfigs) {
+ if (holder.storageClass == storageClass) {
+ val h = (holder as IDataHolder<T>)
+ if (key == null) {
+ h.explicitDefaultLoad()
+ } else {
+ h.loadFrom(key, configData)
+ }
+ }
+ }
+ }
+
+ fun <T> collectConfigFromData(
+ key: T,
+ storageClass: ConfigStorageClass,
+ ): JsonObject {
+ var json = JsonObject(mapOf())
+ for (holder in allConfigs) {
+ if (holder.storageClass == storageClass) {
+ json = mergeJson(json, (holder as IDataHolder<T>).saveTo(key))
+ }
+ }
+ return json
+ }
+
+ fun <T> saveStorage(
+ storageClass: ConfigStorageClass,
+ key: T,
+ firstLevelSplitJsonFolder: FirstLevelSplitJsonFolder,
+ ) {
+ firstLevelSplitJsonFolder.save(
+ collectConfigFromData(key, storageClass)
+ )
+ }
+
+ fun collectAllProfileIds(): Set<UUID> {
+ return allConfigs
+ .filter { it.storageClass == ConfigStorageClass.PROFILE }
+ .flatMapTo(mutableSetOf()) {
+ (it as ProfileKeyedConfig<*>).keys()
+ }
+ }
+
+ fun saveAll() {
+ ConfigLoadContext("save-${System.currentTimeMillis()}").use { context ->
+ saveStorage(
+ ConfigStorageClass.CONFIG,
+ Unit,
+ FirstLevelSplitJsonFolder(context, configFolder)
+ )
+ saveStorage(
+ ConfigStorageClass.STORAGE,
+ Unit,
+ FirstLevelSplitJsonFolder(context, storageFolder)
+ )
+ collectAllProfileIds().forEach { profileId ->
+ saveStorage(
+ ConfigStorageClass.PROFILE,
+ profileId,
+ FirstLevelSplitJsonFolder(context, profilePath.resolve(profileId.toString()))
+ )
+ }
+ writeConfigVersion()
+ }
+ }
+
+ fun mergeJson(a: JsonObject, b: JsonObject): JsonObject {
+ fun mergeInner(a: JsonElement?, b: JsonElement?): JsonElement {
+ if (a == null)
+ return b!!
+ if (b == null)
+ return a
+ a as JsonObject
+ b as JsonObject
+ return buildJsonObject {
+ (a.keys + b.keys)
+ .forEach {
+ put(it, mergeInner(a[it], b[it]))
+ }
+ }
+ }
+ return mergeInner(a, b) as JsonObject
+ }
+
+ val allConfigs: List<IDataHolder<*>> = IConfigProvider.providers.allValidInstances.flatMap { it.configs }
+
+ fun updateConfigs() {
+ val startVersion = configVersionFile.readText()
+ .substringBefore(' ')
+ .trim()
+ .toInt()
+ ConfigLoadContext("update-from-$startVersion-to-$currentConfigVersion-${System.currentTimeMillis()}")
+ .use { loadContext ->
+ updateOneConfig(
+ loadContext,
+ startVersion,
+ ConfigStorageClass.CONFIG,
+ FirstLevelSplitJsonFolder(loadContext, configFolder)
+ )
+ updateOneConfig(
+ loadContext,
+ startVersion,
+ ConfigStorageClass.STORAGE,
+ FirstLevelSplitJsonFolder(loadContext, storageFolder)
+ )
+ profilePath.forEachDirectoryEntry {
+ updateOneConfig(
+ loadContext,
+ startVersion,
+ ConfigStorageClass.PROFILE,
+ FirstLevelSplitJsonFolder(loadContext, it)
+ )
+ }
+ writeConfigVersion()
+ }
+ }
+
+ fun writeConfigVersion() {
+ configVersionFile.writeText("$currentConfigVersion ${tagLines.random()}")
+ }
+
+ private fun updateOneConfig(
+ loadContext: ConfigLoadContext,
+ startVersion: Int,
+ storageClass: ConfigStorageClass,
+ firstLevelSplitJsonFolder: FirstLevelSplitJsonFolder
+ ) {
+ if (startVersion == currentConfigVersion) {
+ loadContext.logDebug("Skipping upgrade to ")
+ return
+ }
+ loadContext.logInfo("Starting upgrade from at ${firstLevelSplitJsonFolder.folder} ($storageClass) to $startVersion")
+ var data = firstLevelSplitJsonFolder.load()
+ for (nextVersion in (startVersion + 1)..currentConfigVersion) {
+ data = updateOneConfigOnce(nextVersion, storageClass, data)
+ }
+ firstLevelSplitJsonFolder.save(data)
+ }
+
+ private fun updateOneConfigOnce(
+ nextVersion: Int,
+ storageClass: ConfigStorageClass,
+ data: JsonObject
+ ): JsonObject {
+ return ConfigFixEvent.publish(ConfigFixEvent(storageClass, nextVersion, data.intoGson().asJsonObject))
+ .data.intoKotlinJson().jsonObject
+ }
+
+ @Subscribe
+ fun onTick(event: TickEvent) {
+ val config = configPromise ?: return
+ val passedTime = saveDebounceStart.passedTime()
+ if (passedTime < 1.seconds)
+ return
+ if (!config.isDone && passedTime < 3.seconds)
+ return
+ debugLogger.log("Performing config save")
+ configPromise = null
+ saveAll()
+ }
+
+ val debugLogger = DebugLogger("config")
+
+ var configPromise: CompletableFuture<Void?>? = null
+ var saveDebounceStart: TimeMark = TimeMark.farPast()
+ fun markDirty(
+ holder: IDataHolder<*>,
+ timeoutPromise: CompletableFuture<Void?>? = null
+ ) {
+ debugLogger.log("Config marked dirty")
+ this.saveDebounceStart = TimeMark.now()
+ this.configPromise = timeoutPromise ?: CompletableFuture.completedFuture(null)
+ }
+
+}
diff --git a/src/main/kotlin/gui/config/storage/FirstLevelSplitJsonFolder.kt b/src/main/kotlin/gui/config/storage/FirstLevelSplitJsonFolder.kt
new file mode 100644
index 0000000..b92488a
--- /dev/null
+++ b/src/main/kotlin/gui/config/storage/FirstLevelSplitJsonFolder.kt
@@ -0,0 +1,109 @@
+@file:OptIn(ExperimentalSerializationApi::class)
+
+package moe.nea.firmament.gui.config.storage
+
+import java.nio.file.Path
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.decodeFromStream
+import kotlinx.serialization.json.encodeToStream
+import kotlin.io.path.createDirectories
+import kotlin.io.path.deleteExisting
+import kotlin.io.path.exists
+import kotlin.io.path.inputStream
+import kotlin.io.path.listDirectoryEntries
+import kotlin.io.path.nameWithoutExtension
+import kotlin.io.path.outputStream
+import moe.nea.firmament.Firmament
+
+// TODO: make this class write / read async
+class FirstLevelSplitJsonFolder(
+ val context: ConfigLoadContext,
+ val folder: Path
+) {
+
+ var hasCreatedBackup = false
+
+ fun backup(cause: String) {
+ if (hasCreatedBackup) return
+ hasCreatedBackup = true
+ context.createBackup(folder, cause)
+ }
+
+ fun load(): JsonObject {
+ context.logInfo("Loading FLSJF from $folder")
+ if (!folder.exists())
+ return JsonObject(mapOf())
+ return try {
+ folder.listDirectoryEntries("*.json")
+ .mapNotNull(::loadIndividualFile)
+ .toMap()
+ .let(::JsonObject)
+ .also { context.logInfo("FLSJF from $folder - Voller Erfolg!") }
+ } catch (ex: Exception) {
+ context.logError("Could not load files from $folder", ex)
+ backup("failed-load")
+ JsonObject(mapOf())
+ }
+ }
+
+ fun loadIndividualFile(path: Path): Pair<String, JsonElement>? {
+ context.logDebug("Loading partial file from $path")
+ return try {
+ path.inputStream().use {
+ path.nameWithoutExtension to Firmament.json.decodeFromStream(JsonElement.serializer(), it)
+ }
+ } catch (ex: Exception) {
+ context.logError("Could not load file from $path", ex)
+ backup("failed-load")
+ null
+ }
+ }
+
+ fun save(value: JsonObject) {
+ context.logInfo("Saving FLSJF to $folder")
+ context.logDebug("Current value:\n$value")
+ if (!folder.exists()) {
+ context.logInfo("Creating folder $folder")
+ folder.createDirectories()
+ }
+ val entries = folder.listDirectoryEntries("*.json")
+ .toMutableList()
+ for ((name, element) in value) {
+ val path = saveIndividualFile(name, element)
+ if (path != null) {
+ entries.remove(path)
+ }
+ }
+ if (entries.isNotEmpty()) {
+ context.logInfo("Deleting additional files.")
+ for (path in entries) {
+ context.logInfo("Deleting $path")
+ backup("save-deletion")
+ try {
+ path.deleteExisting()
+ } catch (ex: Exception) {
+ context.logError("Could not delete $path", ex)
+ }
+ }
+ }
+ context.logInfo("FLSJF to $folder - Voller Erfolg!")
+ }
+
+ fun saveIndividualFile(name: String, element: JsonElement): Path? {
+ try {
+ context.logDebug("Saving partial file with name $name")
+ val path = folder.resolve("$name.json")
+ context.ensureWritable(path)
+ path.outputStream().use {
+ Firmament.json.encodeToStream(JsonElement.serializer(), element, it)
+ }
+ return path
+ } catch (ex: Exception) {
+ context.logError("Could not save $name with value $element", ex)
+ backup("failed-save")
+ return null
+ }
+ }
+}
diff --git a/src/main/kotlin/gui/config/storage/LegacyImporter.kt b/src/main/kotlin/gui/config/storage/LegacyImporter.kt
new file mode 100644
index 0000000..c1f8b90
--- /dev/null
+++ b/src/main/kotlin/gui/config/storage/LegacyImporter.kt
@@ -0,0 +1,68 @@
+package moe.nea.firmament.gui.config.storage
+
+import java.nio.file.Path
+import kotlin.io.path.copyTo
+import kotlin.io.path.createDirectories
+import kotlin.io.path.createParentDirectories
+import kotlin.io.path.exists
+import kotlin.io.path.forEachDirectoryEntry
+import kotlin.io.path.listDirectoryEntries
+import kotlin.io.path.moveTo
+import kotlin.io.path.name
+import kotlin.io.path.nameWithoutExtension
+import kotlin.io.path.writeText
+import moe.nea.firmament.gui.config.storage.FirmamentConfigLoader.configFolder
+import moe.nea.firmament.gui.config.storage.FirmamentConfigLoader.configVersionFile
+import moe.nea.firmament.gui.config.storage.FirmamentConfigLoader.storageFolder
+
+object LegacyImporter {
+ val legacyConfigVersion = 995
+ val backupPath = configFolder.resolveSibling("firmament-legacy-config-${System.currentTimeMillis()}")
+
+ fun copyIf(from: Path, to: Path) {
+ if (from.exists()) {
+ to.createParentDirectories()
+ from.copyTo(to)
+ }
+ }
+
+ val legacyStorage = listOf(
+ "inventory-buttons",
+ "macros",
+ )
+
+ fun importFromLegacy() {
+ if (!configFolder.exists()) return
+ configFolder.moveTo(backupPath)
+ configFolder.createDirectories()
+
+ legacyStorage.forEach {
+ copyIf(
+ backupPath.resolve("$it.json"),
+ storageFolder.resolve("$it.json")
+ )
+ }
+
+ backupPath.listDirectoryEntries("*.json")
+ .filter { it.nameWithoutExtension !in legacyStorage }
+ .forEach { path ->
+ val name = path.name
+ path.copyTo(configFolder.resolve(name))
+ }
+
+ backupPath.resolve("profiles")
+ .takeIf { it.exists() }
+ ?.forEachDirectoryEntry { category ->
+ category.forEachDirectoryEntry { profile ->
+ copyIf(
+ profile,
+ FirmamentConfigLoader.profilePath
+ .resolve(profile.nameWithoutExtension)
+ .resolve(category.name + ".json")
+ )
+ }
+ }
+
+ configVersionFile.writeText("$legacyConfigVersion LEGACY")
+ }
+}
diff --git a/src/main/kotlin/gui/config/storage/README.md b/src/main/kotlin/gui/config/storage/README.md
new file mode 100644
index 0000000..aad4afe
--- /dev/null
+++ b/src/main/kotlin/gui/config/storage/README.md
@@ -0,0 +1,68 @@
+<!--
+SPDX-FileCopyrightText: 2025 Linnea Gräf <nea@nea.moe>
+
+SPDX-License-Identifier: CC0-1.0
+-->
+
+# Plan for the 2026 Config Renewal of Firmament
+
+The current config system in Firmament is not growing at a reasonable pace. Here is a list of my grievances with it:
+
+- the config files are split, resulting in making migrations between different config files (which might load in
+ different order) difficult
+- it is difficult to detect extraneous properties / files, because not all files are loaded and consumed at once
+- profile specific data should be in a different hierarchy. the current hierarchy of `profiles/topic/<uuid>.json` orders
+ data from different profiles to be closer than data from the same profile. this also contributes to the two former
+ problems.
+
+## Goals
+
+- i want to retain having multiple different files for different topics, as well as a folder structure that makes sense
+ for profiles.
+- i want to split up "storage" type data, with "config" type data
+- i want to support partial loads with some broken files (resetting the files that are broken)
+- i want to support backups on any detected error (or simply at will)
+ - notably i do not care about the structure of the backups much. even just a all json files merged backup is fine
+ for me, for now.
+
+## Implementation
+
+### FirstLevelSplitJsonFolder
+
+One of the basic components of this new config folder is a `FirstLevelSplitJsonFolder`. A `FLSJF` takes in a folder
+containing multiple JSON-files and loads all of them unconditionally. Each file is then inserted side by side into a
+json object, to be processed further by other mechanisms.
+
+In essence the `FLSJF` takes a folder structure like this:
+
+```
+file-1.json
+file-2.json
+file-3.json
+```
+
+and turns it into a single merged json object:
+
+```json
+{
+ "file-1": "the json content of file-1.json",
+ "file-2": "the json content of file-2.json",
+ "file-3": "the json content of file-3.json"
+}
+```
+
+As with any stage of the implementation, any unparsable files shall be copied over to a backup spot and discarded.
+
+Nota bene: Folders are wholesale ignored.
+
+### Config folders
+
+Firmament stores all configs and data in the root config folder `./config/firmament`.
+
+- Any config data is stored as an [`FLSJF`](#firstlevelsplitjsonfolder) in the root config folder
+- Any generic storage data is stored as an [`FLSJF`](#firstlevelsplitjsonfolder) in `${rootConfigFolder}/storage/`.
+- Any profile specific storage data is stored as an [`FLSJF`](#firstlevelsplitjsonfolder) for each profile in `${rootConfigFolder}/profileStorage/${profileUuid}/`.
+- Any backup data is stored in `${rootConfigFolder}/backups/${launchId}/${loadId}/${fileName}`.
+ - Where `launchId` is `${currentLaunchTimestamp}-${random()}` to avoid collisions.
+ - Where `loadId` depends on which stage of the config load we are doing (`merge`/`upgrade`/etc.) and what type of config we are loading (`profileSpecific`/`config`/etc.).
+ - And where `fileName` may be a relative filename of where this data was originally found or some internal descriptor for the merged data stage we are on.
diff --git a/src/main/kotlin/gui/entity/EntityModifier.kt b/src/main/kotlin/gui/entity/EntityModifier.kt
index 9623070..4915ebb 100644
--- a/src/main/kotlin/gui/entity/EntityModifier.kt
+++ b/src/main/kotlin/gui/entity/EntityModifier.kt
@@ -2,7 +2,7 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.entity.LivingEntity
+import net.minecraft.world.entity.LivingEntity
fun interface EntityModifier {
fun apply(entity: LivingEntity, info: JsonObject): LivingEntity
diff --git a/src/main/kotlin/gui/entity/EntityRenderer.kt b/src/main/kotlin/gui/entity/EntityRenderer.kt
index fd7a0c4..4972709 100644
--- a/src/main/kotlin/gui/entity/EntityRenderer.kt
+++ b/src/main/kotlin/gui/entity/EntityRenderer.kt
@@ -3,17 +3,18 @@ package moe.nea.firmament.gui.entity
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
+import me.shedaniel.math.Dimension
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.atan
-import net.minecraft.client.gui.DrawContext
-import net.minecraft.client.gui.screen.ingame.InventoryScreen
-import net.minecraft.entity.Entity
-import net.minecraft.entity.EntityType
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.SpawnReason
-import net.minecraft.util.Identifier
-import net.minecraft.world.World
+import net.minecraft.client.gui.GuiGraphics
+import net.minecraft.client.gui.screens.inventory.InventoryScreen
+import net.minecraft.world.entity.Entity
+import net.minecraft.world.entity.EntityType
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.entity.EntitySpawnReason
+import net.minecraft.resources.ResourceLocation
+import net.minecraft.world.level.Level
import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.iterate
@@ -21,47 +22,87 @@ import moe.nea.firmament.util.openFirmamentResource
import moe.nea.firmament.util.render.enableScissorWithTranslation
object EntityRenderer {
- val fakeWorld: World get() = MC.lastWorld!!
+ val fakeWorld: Level get() = MC.lastWorld!!
private fun <T : Entity> t(entityType: EntityType<T>): () -> T {
- return { entityType.create(fakeWorld, SpawnReason.LOAD)!! }
+ return { entityType.create(fakeWorld, EntitySpawnReason.LOAD)!! }
}
val entityIds: Map<String, () -> LivingEntity> = mapOf(
- "Zombie" to t(EntityType.ZOMBIE),
+ "Armadillo" to t(EntityType.ARMADILLO),
+ "ArmorStand" to t(EntityType.ARMOR_STAND),
+ "Axolotl" to t(EntityType.AXOLOTL),
+ "Bat" to t(EntityType.BAT),
+ "Bee" to t(EntityType.BEE),
+ "Blaze" to t(EntityType.BLAZE),
+ "Bogged" to t(EntityType.BOGGED),
+ "Breeze" to t(EntityType.BREEZE),
+ "CaveSpider" to t(EntityType.CAVE_SPIDER),
"Chicken" to t(EntityType.CHICKEN),
- "Slime" to t(EntityType.SLIME),
- "Wolf" to t(EntityType.WOLF),
- "Skeleton" to t(EntityType.SKELETON),
+ "Cod" to t(EntityType.COD),
+ "Cow" to t(EntityType.COW),
+ "Creaking" to t(EntityType.CREAKING),
"Creeper" to t(EntityType.CREEPER),
+ "Dolphin" to t(EntityType.DOLPHIN),
+ "Donkey" to t(EntityType.DONKEY),
+ "Dragon" to t(EntityType.ENDER_DRAGON),
+ "Drowned" to t(EntityType.DROWNED),
+ "Eisengolem" to t(EntityType.IRON_GOLEM),
+ "Enderman" to t(EntityType.ENDERMAN),
+ "Endermite" to t(EntityType.ENDERMITE),
+ "Evoker" to t(EntityType.EVOKER),
+ "Fox" to t(EntityType.FOX),
+ "Frog" to t(EntityType.FROG),
+ "Ghast" to t(EntityType.GHAST),
+ "Giant" to t(EntityType.GIANT),
+ "GlowSquid" to t(EntityType.GLOW_SQUID),
+ "Goat" to t(EntityType.GOAT),
+ "Guardian" to t(EntityType.GUARDIAN),
+ "Horse" to t(EntityType.HORSE),
+ "Husk" to t(EntityType.HUSK),
+ "Illusioner" to t(EntityType.ILLUSIONER),
+ "LLama" to t(EntityType.LLAMA),
+ "MagmaCube" to t(EntityType.MAGMA_CUBE),
+ "Mooshroom" to t(EntityType.MOOSHROOM),
+ "Mule" to t(EntityType.MULE),
"Ocelot" to t(EntityType.OCELOT),
- "Blaze" to t(EntityType.BLAZE),
+ "Panda" to t(EntityType.PANDA),
+ "Phantom" to t(EntityType.PHANTOM),
+ "Pig" to t(EntityType.PIG),
+ "Piglin" to t(EntityType.PIGLIN),
+ "PiglinBrute" to t(EntityType.PIGLIN_BRUTE),
+ "Pigman" to t(EntityType.ZOMBIFIED_PIGLIN),
+ "Pillager" to t(EntityType.PILLAGER),
+ "Player" to { makeGuiPlayer(fakeWorld) },
+ "PolarBear" to t(EntityType.POLAR_BEAR),
+ "Pufferfish" to t(EntityType.PUFFERFISH),
"Rabbit" to t(EntityType.RABBIT),
+ "Salmom" to t(EntityType.SALMON),
+ "Salmon" to t(EntityType.SALMON),
"Sheep" to t(EntityType.SHEEP),
- "Horse" to t(EntityType.HORSE),
- "Eisengolem" to t(EntityType.IRON_GOLEM),
+ "Shulker" to t(EntityType.SHULKER),
"Silverfish" to t(EntityType.SILVERFISH),
- "Witch" to t(EntityType.WITCH),
- "Endermite" to t(EntityType.ENDERMITE),
+ "Skeleton" to t(EntityType.SKELETON),
+ "Slime" to t(EntityType.SLIME),
+ "Sniffer" to t(EntityType.SNIFFER),
"Snowman" to t(EntityType.SNOW_GOLEM),
- "Villager" to t(EntityType.VILLAGER),
- "Guardian" to t(EntityType.GUARDIAN),
- "ArmorStand" to t(EntityType.ARMOR_STAND),
- "Squid" to t(EntityType.SQUID),
- "Bat" to t(EntityType.BAT),
"Spider" to t(EntityType.SPIDER),
- "CaveSpider" to t(EntityType.CAVE_SPIDER),
- "Pigman" to t(EntityType.ZOMBIFIED_PIGLIN),
- "Ghast" to t(EntityType.GHAST),
- "MagmaCube" to t(EntityType.MAGMA_CUBE),
+ "Squid" to t(EntityType.SQUID),
+ "Stray" to t(EntityType.STRAY),
+ "Strider" to t(EntityType.STRIDER),
+ "Tadpole" to t(EntityType.TADPOLE),
+ "TropicalFish" to t(EntityType.TROPICAL_FISH),
+ "Turtle" to t(EntityType.TURTLE),
+ "Vex" to t(EntityType.VEX),
+ "Villager" to t(EntityType.VILLAGER),
+ "Vindicator" to t(EntityType.VINDICATOR),
+ "Warden" to t(EntityType.WARDEN),
+ "Witch" to t(EntityType.WITCH),
"Wither" to t(EntityType.WITHER),
- "Enderman" to t(EntityType.ENDERMAN),
- "Mooshroom" to t(EntityType.MOOSHROOM),
"WitherSkeleton" to t(EntityType.WITHER_SKELETON),
- "Cow" to t(EntityType.COW),
- "Dragon" to t(EntityType.ENDER_DRAGON),
- "Player" to { makeGuiPlayer(fakeWorld) },
- "Pig" to t(EntityType.PIG),
- "Giant" to t(EntityType.GIANT),
+ "Wolf" to t(EntityType.WOLF),
+ "Zoglin" to t(EntityType.ZOGLIN),
+ "Zombie" to t(EntityType.ZOMBIE),
+ "ZombieVillager" to t(EntityType.ZOMBIE_VILLAGER)
)
val entityModifiers: Map<String, EntityModifier> = mapOf(
"playerdata" to ModifyPlayerSkin,
@@ -83,7 +124,8 @@ object EntityRenderer {
for (modifierJson in modifiers) {
val modifier = ErrorUtil.notNullOr(
modifierJson["type"]?.asString?.let(entityModifiers::get),
- "Could not create entity with id $entityId. Failed to apply modifier $modifierJson") { return null }
+ "Could not create entity with id $entityId. Failed to apply modifier $modifierJson"
+ ) { return null }
entity = modifier.apply(entity, modifierJson)
}
return entity
@@ -98,7 +140,7 @@ object EntityRenderer {
}
private val gson = Gson()
- fun constructEntity(location: Identifier): LivingEntity? {
+ fun constructEntity(location: ResourceLocation): LivingEntity? {
return constructEntity(
gson.fromJson(
location.openFirmamentResource().bufferedReader(), JsonObject::class.java
@@ -108,7 +150,7 @@ object EntityRenderer {
fun renderEntity(
entity: LivingEntity,
- renderContext: DrawContext,
+ renderContext: GuiGraphics,
posX: Int,
posY: Int,
// TODO: Add width, height properties here
@@ -121,10 +163,10 @@ object EntityRenderer {
var bottomOffset = 0.0
var currentEntity = entity
val maxSize = entity.iterate { it.firstPassenger as? LivingEntity }
- .map { it.height }
+ .map { it.bbHeight }
.sum()
while (true) {
- currentEntity.age = MC.player?.age ?: 0
+ currentEntity.tickCount = MC.player?.tickCount ?: 0
drawEntity(
renderContext,
posX,
@@ -138,14 +180,14 @@ object EntityRenderer {
currentEntity
)
val next = currentEntity.firstPassenger as? LivingEntity ?: break
- bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F
+ bottomOffset += currentEntity.getPassengerRidingPosition(next).y.toFloat() * 0.75F
currentEntity = next
}
}
fun drawEntity(
- context: DrawContext,
+ context: GuiGraphics,
x1: Int,
y1: Int,
x2: Int,
@@ -162,38 +204,38 @@ object EntityRenderer {
val hw = (x2 - x1) / 2
val hh = (y2 - y1) / 2
val targetYaw = atan(((centerX - mouseX) / hw)).toFloat()
- val targetPitch = atan(((centerY - mouseY) / hh)).toFloat()
+ val targetPitch = atan(((centerY - mouseY) / hh - entity.eyeHeight * hh / 40)).toFloat()
val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat())
val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180))
rotateToFaceTheFront.mul(rotateToFaceTheCamera)
- val oldBodyYaw = entity.bodyYaw
- val oldYaw = entity.yaw
- val oldPitch = entity.pitch
- val oldPrevHeadYaw = entity.prevHeadYaw
- val oldHeadYaw = entity.headYaw
- entity.bodyYaw = 180.0f + targetYaw * 20.0f
- entity.yaw = 180.0f + targetYaw * 40.0f
- entity.pitch = -targetPitch * 20.0f
- entity.headYaw = entity.yaw
- entity.prevHeadYaw = entity.yaw
- val vector3f = Vector3f(0.0f, (entity.height / 2.0f + bottomOffset).toFloat(), 0.0f)
- InventoryScreen.drawEntity(
+ val oldBodyYaw = entity.yBodyRot
+ val oldYaw = entity.yRot
+ val oldPitch = entity.xRot
+ val oldPrevHeadYaw = entity.yHeadRotO
+ val oldHeadYaw = entity.yHeadRot
+ entity.yBodyRot = 180.0f + targetYaw * 20.0f
+ entity.yRot = 180.0f + targetYaw * 40.0f
+ entity.xRot = -targetPitch * 20.0f
+ entity.yHeadRot = entity.yRot
+ entity.yHeadRotO = entity.yRot
+ val vector3f = Vector3f(0.0f, (entity.bbHeight / 2.0f + bottomOffset).toFloat(), 0.0f)
+ InventoryScreen.renderEntityInInventory( // TODO: fix multiple entities rendering the same entity
context,
- centerX,
- centerY,
+ x1, y1,
+ x2, y2,
size.toFloat(),
vector3f,
rotateToFaceTheFront,
rotateToFaceTheCamera,
entity
)
- entity.bodyYaw = oldBodyYaw
- entity.yaw = oldYaw
- entity.pitch = oldPitch
- entity.prevHeadYaw = oldPrevHeadYaw
- entity.headYaw = oldHeadYaw
+ entity.yBodyRot = oldBodyYaw
+ entity.yRot = oldYaw
+ entity.xRot = oldPitch
+ entity.yHeadRotO = oldPrevHeadYaw
+ entity.yHeadRot = oldHeadYaw
context.disableScissor()
}
-
+ val defaultSize = Dimension(50, 80)
}
diff --git a/src/main/kotlin/gui/entity/FakeWorld.kt b/src/main/kotlin/gui/entity/FakeWorld.kt
deleted file mode 100644
index ccf6b60..0000000
--- a/src/main/kotlin/gui/entity/FakeWorld.kt
+++ /dev/null
@@ -1,343 +0,0 @@
-package moe.nea.firmament.gui.entity
-
-import java.util.UUID
-import java.util.function.BooleanSupplier
-import java.util.function.Consumer
-import net.minecraft.block.Block
-import net.minecraft.block.BlockState
-import net.minecraft.client.gui.screen.world.SelectWorldScreen
-import net.minecraft.component.type.MapIdComponent
-import net.minecraft.entity.Entity
-import net.minecraft.entity.boss.dragon.EnderDragonPart
-import net.minecraft.entity.damage.DamageSource
-import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.fluid.Fluid
-import net.minecraft.item.FuelRegistry
-import net.minecraft.item.map.MapState
-import net.minecraft.particle.ParticleEffect
-import net.minecraft.recipe.BrewingRecipeRegistry
-import net.minecraft.recipe.RecipeManager
-import net.minecraft.recipe.RecipePropertySet
-import net.minecraft.recipe.StonecuttingRecipe
-import net.minecraft.recipe.display.CuttingRecipeDisplay
-import net.minecraft.registry.DynamicRegistryManager
-import net.minecraft.registry.Registries
-import net.minecraft.registry.RegistryKey
-import net.minecraft.registry.RegistryKeys
-import net.minecraft.registry.ServerDynamicRegistryType
-import net.minecraft.registry.entry.RegistryEntry
-import net.minecraft.resource.DataConfiguration
-import net.minecraft.resource.ResourcePackManager
-import net.minecraft.resource.featuretoggle.FeatureFlags
-import net.minecraft.resource.featuretoggle.FeatureSet
-import net.minecraft.scoreboard.Scoreboard
-import net.minecraft.server.SaveLoading
-import net.minecraft.server.command.CommandManager
-import net.minecraft.sound.SoundCategory
-import net.minecraft.sound.SoundEvent
-import net.minecraft.util.Identifier
-import net.minecraft.util.TypeFilter
-import net.minecraft.util.function.LazyIterationConsumer
-import net.minecraft.util.math.BlockPos
-import net.minecraft.util.math.Box
-import net.minecraft.util.math.ChunkPos
-import net.minecraft.util.math.Direction
-import net.minecraft.util.math.Vec3d
-import net.minecraft.world.BlockView
-import net.minecraft.world.Difficulty
-import net.minecraft.world.MutableWorldProperties
-import net.minecraft.world.World
-import net.minecraft.world.biome.Biome
-import net.minecraft.world.biome.BiomeKeys
-import net.minecraft.world.chunk.Chunk
-import net.minecraft.world.chunk.ChunkManager
-import net.minecraft.world.chunk.ChunkStatus
-import net.minecraft.world.chunk.EmptyChunk
-import net.minecraft.world.chunk.light.LightingProvider
-import net.minecraft.world.entity.EntityLookup
-import net.minecraft.world.event.GameEvent
-import net.minecraft.world.explosion.ExplosionBehavior
-import net.minecraft.world.tick.OrderedTick
-import net.minecraft.world.tick.QueryableTickScheduler
-import net.minecraft.world.tick.TickManager
-import moe.nea.firmament.util.MC
-
-fun createDynamicRegistry(): DynamicRegistryManager.Immutable {
- // TODO: use SaveLoading.load() to properly load a full registry
- return DynamicRegistryManager.of(Registries.REGISTRIES)
-}
-
-class FakeWorld(
- registries: DynamicRegistryManager.Immutable = createDynamicRegistry(),
-) : World(
- Properties,
- RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")),
- registries,
- MC.defaultRegistries.getOrThrow(RegistryKeys.DIMENSION_TYPE)
- .getOrThrow(RegistryKey.of(RegistryKeys.DIMENSION_TYPE, Identifier.of("minecraft", "overworld"))),
- true,
- false,
- 0L,
- 0
-) {
- object Properties : MutableWorldProperties {
- override fun getSpawnPos(): BlockPos {
- return BlockPos.ORIGIN
- }
-
- override fun getSpawnAngle(): Float {
- return 0F
- }
-
- override fun getTime(): Long {
- return 0
- }
-
- override fun getTimeOfDay(): Long {
- return 0
- }
-
- override fun isThundering(): Boolean {
- return false
- }
-
- override fun isRaining(): Boolean {
- return false
- }
-
- override fun setRaining(raining: Boolean) {
- }
-
- override fun isHardcore(): Boolean {
- return false
- }
-
- override fun getDifficulty(): Difficulty {
- return Difficulty.HARD
- }
-
- override fun isDifficultyLocked(): Boolean {
- return false
- }
-
- override fun setSpawnPos(pos: BlockPos?, angle: Float) {}
- }
-
- override fun getPlayers(): List<PlayerEntity> {
- return emptyList()
- }
-
- override fun getBrightness(direction: Direction?, shaded: Boolean): Float {
- return 1f
- }
-
- override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> {
- return registryManager.getOptionalEntry(BiomeKeys.PLAINS).get()
- }
-
- override fun getSeaLevel(): Int {
- return 0
- }
-
- override fun getEnabledFeatures(): FeatureSet {
- return FeatureFlags.VANILLA_FEATURES
- }
-
- class FakeTickScheduler<T> : QueryableTickScheduler<T> {
- override fun scheduleTick(orderedTick: OrderedTick<T>?) {
- }
-
- override fun isQueued(pos: BlockPos?, type: T): Boolean {
- return true
- }
-
- override fun getTickCount(): Int {
- return 0
- }
-
- override fun isTicking(pos: BlockPos?, type: T): Boolean {
- return true
- }
-
- }
-
- override fun getBlockTickScheduler(): QueryableTickScheduler<Block> {
- return FakeTickScheduler()
- }
-
- override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> {
- return FakeTickScheduler()
- }
-
-
- class FakeChunkManager(val world: FakeWorld) : ChunkManager() {
- override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk {
- return EmptyChunk(
- world,
- ChunkPos(x, z),
- world.registryManager.getOptionalEntry(BiomeKeys.PLAINS).get()
- )
- }
-
- override fun getWorld(): BlockView {
- return world
- }
-
- override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) {
- }
-
- override fun getDebugString(): String {
- return "FakeChunkManager"
- }
-
- override fun getLoadedChunkCount(): Int {
- return 0
- }
-
- override fun getLightingProvider(): LightingProvider {
- return FakeLightingProvider(this)
- }
- }
-
- class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false)
-
- override fun getChunkManager(): ChunkManager {
- return FakeChunkManager(this)
- }
-
- override fun playSound(
- source: PlayerEntity?,
- x: Double,
- y: Double,
- z: Double,
- sound: RegistryEntry<SoundEvent>?,
- category: SoundCategory?,
- volume: Float,
- pitch: Float,
- seed: Long
- ) {
- }
-
- override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) {
- }
-
- override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) {
- }
-
- override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) {
- }
-
- override fun playSoundFromEntity(
- source: PlayerEntity?,
- entity: Entity?,
- sound: RegistryEntry<SoundEvent>?,
- category: SoundCategory?,
- volume: Float,
- pitch: Float,
- seed: Long
- ) {
- }
-
- override fun createExplosion(
- entity: Entity?,
- damageSource: DamageSource?,
- behavior: ExplosionBehavior?,
- x: Double,
- y: Double,
- z: Double,
- power: Float,
- createFire: Boolean,
- explosionSourceType: ExplosionSourceType?,
- smallParticle: ParticleEffect?,
- largeParticle: ParticleEffect?,
- soundEvent: RegistryEntry<SoundEvent>?
- ) {
- TODO("Not yet implemented")
- }
-
- override fun asString(): String {
- return "FakeWorld"
- }
-
- override fun getEntityById(id: Int): Entity? {
- return null
- }
-
- override fun getEnderDragonParts(): MutableCollection<EnderDragonPart> {
- return mutableListOf()
- }
-
- override fun getTickManager(): TickManager {
- return TickManager()
- }
-
- override fun getMapState(id: MapIdComponent?): MapState? {
- return null
- }
-
- override fun putMapState(id: MapIdComponent?, state: MapState?) {
- }
-
- override fun increaseAndGetMapId(): MapIdComponent {
- return MapIdComponent(0)
- }
-
- override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) {
- }
-
- override fun getScoreboard(): Scoreboard {
- return Scoreboard()
- }
-
- override fun getRecipeManager(): RecipeManager {
- return object : RecipeManager {
- override fun getPropertySet(key: RegistryKey<RecipePropertySet>?): RecipePropertySet {
- return RecipePropertySet.EMPTY
- }
-
- override fun getStonecutterRecipes(): CuttingRecipeDisplay.Grouping<StonecuttingRecipe> {
- return CuttingRecipeDisplay.Grouping.empty()
- }
- }
- }
-
- object FakeEntityLookup : EntityLookup<Entity> {
- override fun get(id: Int): Entity? {
- return null
- }
-
- override fun get(uuid: UUID?): Entity? {
- return null
- }
-
- override fun iterate(): MutableIterable<Entity> {
- return mutableListOf()
- }
-
- override fun <U : Entity?> forEachIntersects(
- filter: TypeFilter<Entity, U>?,
- box: Box?,
- consumer: LazyIterationConsumer<U>?
- ) {
- }
-
- override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) {
- }
-
- override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) {
- }
-
- }
-
- override fun getEntityLookup(): EntityLookup<Entity> {
- return FakeEntityLookup
- }
-
- override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry {
- return BrewingRecipeRegistry.EMPTY
- }
-
- override fun getFuelRegistry(): FuelRegistry {
- TODO("Not yet implemented")
- }
-}
diff --git a/src/main/kotlin/gui/entity/GuiPlayer.kt b/src/main/kotlin/gui/entity/GuiPlayer.kt
index f728dbf..b53f68c 100644
--- a/src/main/kotlin/gui/entity/GuiPlayer.kt
+++ b/src/main/kotlin/gui/entity/GuiPlayer.kt
@@ -1,62 +1,28 @@
package moe.nea.firmament.gui.entity
-import com.mojang.authlib.GameProfile
-import java.util.UUID
-import net.minecraft.client.network.AbstractClientPlayerEntity
-import net.minecraft.client.util.DefaultSkinHelper
-import net.minecraft.client.util.SkinTextures
-import net.minecraft.client.util.SkinTextures.Model
-import net.minecraft.client.world.ClientWorld
-import net.minecraft.util.Identifier
-import net.minecraft.util.math.BlockPos
-import net.minecraft.util.math.Vec3d
-import net.minecraft.world.World
+import net.minecraft.client.entity.ClientMannequin
+import net.minecraft.client.resources.DefaultPlayerSkin
+import net.minecraft.client.multiplayer.ClientLevel
+import net.minecraft.world.entity.player.PlayerSkin
+import net.minecraft.world.level.Level
+import moe.nea.firmament.util.MC
-/**
- * @see moe.nea.firmament.init.EarlyRiser
- */
-fun makeGuiPlayer(world: World): GuiPlayer {
- val constructor = GuiPlayer::class.java.getDeclaredConstructor(
- World::class.java,
- BlockPos::class.java,
- Float::class.javaPrimitiveType,
- GameProfile::class.java
- )
- val player = constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea"))
- player.postInit()
+fun makeGuiPlayer(world: Level): GuiPlayer {
+ val player = GuiPlayer(MC.instance.level!!)
return player
}
-class GuiPlayer(world: ClientWorld?, profile: GameProfile?) : AbstractClientPlayerEntity(world, profile) {
+class GuiPlayer(world: ClientLevel?) : ClientMannequin(world, MC.instance.playerSkinRenderCache()) {
override fun isSpectator(): Boolean {
return false
}
- fun postInit() {
- skinTexture = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture
- lastVelocity = Vec3d.ZERO
- model = Model.WIDE
- }
-
- override fun isCreative(): Boolean {
- return false
- }
-
- override fun shouldRenderName(): Boolean {
+ override fun shouldShowName(): Boolean {
return false
}
- lateinit var skinTexture: Identifier
- var capeTexture: Identifier? = null
- var model: Model = Model.WIDE
- override fun getSkinTextures(): SkinTextures {
- return SkinTextures(
- skinTexture,
- null,
- capeTexture,
- null,
- model,
- true
- )
+ var skinTextures: PlayerSkin = DefaultPlayerSkin.get(this.uuid) // TODO: 1.21.10
+ override fun getSkin(): PlayerSkin {
+ return skinTextures
}
}
diff --git a/src/main/kotlin/gui/entity/ModifyAge.kt b/src/main/kotlin/gui/entity/ModifyAge.kt
index a65c368..99154ef 100644
--- a/src/main/kotlin/gui/entity/ModifyAge.kt
+++ b/src/main/kotlin/gui/entity/ModifyAge.kt
@@ -2,19 +2,19 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.decoration.ArmorStandEntity
-import net.minecraft.entity.mob.ZombieEntity
-import net.minecraft.entity.passive.PassiveEntity
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.entity.decoration.ArmorStand
+import net.minecraft.world.entity.monster.Zombie
+import net.minecraft.world.entity.AgeableMob
object ModifyAge : EntityModifier {
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
val isBaby = info["baby"]?.asBoolean ?: false
- if (entity is PassiveEntity) {
- entity.breedingAge = if (isBaby) -1 else 1
- } else if (entity is ZombieEntity) {
+ if (entity is AgeableMob) {
+ entity.age = if (isBaby) -1 else 1
+ } else if (entity is Zombie) {
entity.isBaby = isBaby
- } else if (entity is ArmorStandEntity) {
+ } else if (entity is ArmorStand) {
entity.isSmall = isBaby
} else {
error("Cannot set age for $entity")
diff --git a/src/main/kotlin/gui/entity/ModifyCharged.kt b/src/main/kotlin/gui/entity/ModifyCharged.kt
index d22f6e3..23fd495 100644
--- a/src/main/kotlin/gui/entity/ModifyCharged.kt
+++ b/src/main/kotlin/gui/entity/ModifyCharged.kt
@@ -2,13 +2,13 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.mob.CreeperEntity
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.entity.monster.Creeper
object ModifyCharged : EntityModifier {
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
- require(entity is CreeperEntity)
- entity.dataTracker.set(CreeperEntity.CHARGED, true)
+ require(entity is Creeper)
+ entity.entityData.set(Creeper.DATA_IS_POWERED, true)
return entity
}
}
diff --git a/src/main/kotlin/gui/entity/ModifyEquipment.kt b/src/main/kotlin/gui/entity/ModifyEquipment.kt
index a558936..9c43e73 100644
--- a/src/main/kotlin/gui/entity/ModifyEquipment.kt
+++ b/src/main/kotlin/gui/entity/ModifyEquipment.kt
@@ -1,17 +1,18 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.component.DataComponentTypes
-import net.minecraft.component.type.DyedColorComponent
-import net.minecraft.entity.EquipmentSlot
-import net.minecraft.entity.LivingEntity
-import net.minecraft.item.Item
-import net.minecraft.item.ItemStack
-import net.minecraft.item.Items
+import net.minecraft.core.component.DataComponents
+import net.minecraft.world.item.component.DyedItemColor
+import net.minecraft.world.entity.EquipmentSlot
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.item.Item
+import net.minecraft.world.item.ItemStack
+import net.minecraft.world.item.Items
+import moe.nea.firmament.repo.ExpensiveItemCacheApi
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.SkyblockId
+import moe.nea.firmament.util.mc.arbitraryUUID
import moe.nea.firmament.util.mc.setEncodedSkullOwner
-import moe.nea.firmament.util.mc.zeroUUID
object ModifyEquipment : EntityModifier {
val names = mapOf(
@@ -25,18 +26,19 @@ object ModifyEquipment : EntityModifier {
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
names.forEach { (key, slot) ->
info[key]?.let {
- entity.equipStack(slot, createItem(it.asString))
+ entity.setItemSlot(slot, createItem(it.asString))
}
}
return entity
}
+ @OptIn(ExpensiveItemCacheApi::class)
private fun createItem(item: String): ItemStack {
val split = item.split("#")
if (split.size != 2) return SBItemStack(SkyblockId(item)).asImmutableItemStack()
val (type, data) = split
return when (type) {
- "SKULL" -> ItemStack(Items.PLAYER_HEAD).also { it.setEncodedSkullOwner(zeroUUID, data) }
+ "SKULL" -> ItemStack(Items.PLAYER_HEAD).also { it.setEncodedSkullOwner(arbitraryUUID, data) }
"LEATHER_LEGGINGS" -> coloredLeatherArmor(Items.LEATHER_LEGGINGS, data)
"LEATHER_BOOTS" -> coloredLeatherArmor(Items.LEATHER_BOOTS, data)
"LEATHER_HELMET" -> coloredLeatherArmor(Items.LEATHER_HELMET, data)
@@ -47,7 +49,7 @@ object ModifyEquipment : EntityModifier {
private fun coloredLeatherArmor(leatherArmor: Item, data: String): ItemStack {
val stack = ItemStack(leatherArmor)
- stack.set(DataComponentTypes.DYED_COLOR, DyedColorComponent(data.toInt(16), false))
+ stack.set(DataComponents.DYED_COLOR, DyedItemColor(data.toInt(16)))
return stack
}
}
diff --git a/src/main/kotlin/gui/entity/ModifyHorse.kt b/src/main/kotlin/gui/entity/ModifyHorse.kt
index f094ca4..a870bf1 100644
--- a/src/main/kotlin/gui/entity/ModifyHorse.kt
+++ b/src/main/kotlin/gui/entity/ModifyHorse.kt
@@ -1,62 +1,58 @@
-
package moe.nea.firmament.gui.entity
import com.google.gson.JsonNull
import com.google.gson.JsonObject
-import kotlin.experimental.and
-import kotlin.experimental.inv
-import kotlin.experimental.or
-import net.minecraft.entity.EntityType
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.SpawnReason
-import net.minecraft.entity.passive.AbstractHorseEntity
-import net.minecraft.item.ItemStack
-import net.minecraft.item.Items
+import net.minecraft.world.entity.EntityType
+import net.minecraft.world.entity.EquipmentSlot
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.entity.EntitySpawnReason
+import net.minecraft.world.entity.animal.horse.AbstractHorse
+import net.minecraft.world.item.ItemStack
+import net.minecraft.world.item.Items
import moe.nea.firmament.gui.entity.EntityRenderer.fakeWorld
object ModifyHorse : EntityModifier {
- override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
- require(entity is AbstractHorseEntity)
- var entity: AbstractHorseEntity = entity
- info["kind"]?.let {
- entity = when (it.asString) {
- "skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld, SpawnReason.LOAD)!!
- "zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld, SpawnReason.LOAD)!!
- "mule" -> EntityType.MULE.create(fakeWorld, SpawnReason.LOAD)!!
- "donkey" -> EntityType.DONKEY.create(fakeWorld, SpawnReason.LOAD)!!
- "horse" -> EntityType.HORSE.create(fakeWorld, SpawnReason.LOAD)!!
- else -> error("Unknown horse kind $it")
- }
- }
- info["armor"]?.let {
- if (it is JsonNull) {
- entity.setHorseArmor(ItemStack.EMPTY)
- } else {
- when (it.asString) {
- "iron" -> entity.setHorseArmor(ItemStack(Items.IRON_HORSE_ARMOR))
- "golden" -> entity.setHorseArmor(ItemStack(Items.GOLDEN_HORSE_ARMOR))
- "diamond" -> entity.setHorseArmor(ItemStack(Items.DIAMOND_HORSE_ARMOR))
- else -> error("Unknown horse armor $it")
- }
- }
- }
- info["saddled"]?.let {
- entity.setIsSaddled(it.asBoolean)
- }
- return entity
- }
+ override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
+ require(entity is AbstractHorse)
+ var entity: AbstractHorse = entity
+ info["kind"]?.let {
+ entity = when (it.asString) {
+ "skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld, EntitySpawnReason.LOAD)!!
+ "zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld, EntitySpawnReason.LOAD)!!
+ "mule" -> EntityType.MULE.create(fakeWorld, EntitySpawnReason.LOAD)!!
+ "donkey" -> EntityType.DONKEY.create(fakeWorld, EntitySpawnReason.LOAD)!!
+ "horse" -> EntityType.HORSE.create(fakeWorld, EntitySpawnReason.LOAD)!!
+ else -> error("Unknown horse kind $it")
+ }
+ }
+ info["armor"]?.let {
+ if (it is JsonNull) {
+ entity.setHorseArmor(ItemStack.EMPTY)
+ } else {
+ when (it.asString) {
+ "iron" -> entity.setHorseArmor(ItemStack(Items.IRON_HORSE_ARMOR))
+ "golden" -> entity.setHorseArmor(ItemStack(Items.GOLDEN_HORSE_ARMOR))
+ "diamond" -> entity.setHorseArmor(ItemStack(Items.DIAMOND_HORSE_ARMOR))
+ else -> error("Unknown horse armor $it")
+ }
+ }
+ }
+ info["saddled"]?.let {
+ entity.setIsSaddled(it.asBoolean)
+ }
+ return entity
+ }
}
-fun AbstractHorseEntity.setIsSaddled(shouldBeSaddled: Boolean) {
- val oldFlag = dataTracker.get(AbstractHorseEntity.HORSE_FLAGS)
- dataTracker.set(
- AbstractHorseEntity.HORSE_FLAGS,
- if (shouldBeSaddled) oldFlag or AbstractHorseEntity.SADDLED_FLAG.toByte()
- else oldFlag and AbstractHorseEntity.SADDLED_FLAG.toByte().inv()
- )
+fun AbstractHorse.setIsSaddled(shouldBeSaddled: Boolean) {
+ this.setItemSlot(
+ EquipmentSlot.SADDLE,
+ if (shouldBeSaddled) ItemStack(Items.SADDLE)
+ else ItemStack.EMPTY
+ )
}
-fun AbstractHorseEntity.setHorseArmor(itemStack: ItemStack) {
- items.setStack(1, itemStack)
+fun AbstractHorse.setHorseArmor(itemStack: ItemStack) {
+ bodyArmorItem = itemStack
}
diff --git a/src/main/kotlin/gui/entity/ModifyInvisible.kt b/src/main/kotlin/gui/entity/ModifyInvisible.kt
index 8d36991..7a54ab1 100644
--- a/src/main/kotlin/gui/entity/ModifyInvisible.kt
+++ b/src/main/kotlin/gui/entity/ModifyInvisible.kt
@@ -2,7 +2,7 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.entity.LivingEntity
+import net.minecraft.world.entity.LivingEntity
object ModifyInvisible : EntityModifier {
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
diff --git a/src/main/kotlin/gui/entity/ModifyName.kt b/src/main/kotlin/gui/entity/ModifyName.kt
index a03da96..6def0b4 100644
--- a/src/main/kotlin/gui/entity/ModifyName.kt
+++ b/src/main/kotlin/gui/entity/ModifyName.kt
@@ -2,12 +2,12 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.entity.LivingEntity
-import net.minecraft.text.Text
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.network.chat.Component
object ModifyName : EntityModifier {
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
- entity.customName = Text.literal(info.get("name").asString)
+ entity.customName = Component.literal(info.get("name").asString)
return entity
}
diff --git a/src/main/kotlin/gui/entity/ModifyPlayerSkin.kt b/src/main/kotlin/gui/entity/ModifyPlayerSkin.kt
index 28f0070..0b393bb 100644
--- a/src/main/kotlin/gui/entity/ModifyPlayerSkin.kt
+++ b/src/main/kotlin/gui/entity/ModifyPlayerSkin.kt
@@ -1,47 +1,57 @@
-
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import kotlin.experimental.and
import kotlin.experimental.or
-import net.minecraft.client.util.SkinTextures
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.player.PlayerEntity
-import net.minecraft.entity.player.PlayerModelPart
-import net.minecraft.util.Identifier
+import net.minecraft.client.entity.ClientAvatarEntity
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.entity.Avatar
+import net.minecraft.world.entity.player.Player
+import net.minecraft.world.entity.player.PlayerModelPart
+import net.minecraft.world.entity.player.PlayerModelType
+import net.minecraft.world.entity.player.PlayerSkin
+import net.minecraft.core.ClientAsset
+import net.minecraft.resources.ResourceLocation
object ModifyPlayerSkin : EntityModifier {
- val playerModelPartIndex = PlayerModelPart.entries.associateBy { it.getName() }
- override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
- require(entity is GuiPlayer)
- info["cape"]?.let {
- entity.capeTexture = Identifier.of(it.asString)
- }
- info["skin"]?.let {
- entity.skinTexture = Identifier.of(it.asString)
- }
- info["slim"]?.let {
- entity.model = if (it.asBoolean) SkinTextures.Model.SLIM else SkinTextures.Model.WIDE
- }
- info["parts"]?.let {
- var trackedData = entity.dataTracker.get(PlayerEntity.PLAYER_MODEL_PARTS)
- if (it is JsonPrimitive && it.isBoolean) {
- trackedData = (if (it.asBoolean) -1 else 0).toByte()
- } else {
- val obj = it.asJsonObject
- for ((k, v) in obj.entrySet()) {
- val part = playerModelPartIndex[k]!!
- trackedData = if (v.asBoolean) {
- trackedData and (part.bitFlag.inv().toByte())
- } else {
- trackedData or (part.bitFlag.toByte())
- }
- }
- }
- entity.dataTracker.set(PlayerEntity.PLAYER_MODEL_PARTS, trackedData)
- }
- return entity
- }
+ val playerModelPartIndex = PlayerModelPart.entries.associateBy { it.id }
+ override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
+ require(entity is GuiPlayer)
+ var capeTexture = entity.skinTextures.cape
+ var model = entity.skinTextures.model
+ var bodyTexture = entity.skinTextures.body
+ fun mkTexAsset(id: ResourceLocation) = ClientAsset.ResourceTexture(id, id)
+ info["cape"]?.let {
+ capeTexture = mkTexAsset(ResourceLocation.parse(it.asString))
+ }
+ info["skin"]?.let {
+ bodyTexture = mkTexAsset(ResourceLocation.parse(it.asString))
+ }
+ info["slim"]?.let {
+ model = if (it.asBoolean) PlayerModelType.SLIM else PlayerModelType.WIDE
+ }
+ info["parts"]?.let {
+ var trackedData = entity.entityData.get(Avatar.DATA_PLAYER_MODE_CUSTOMISATION)
+ if (it is JsonPrimitive && it.isBoolean) {
+ trackedData = (if (it.asBoolean) -1 else 0).toByte()
+ } else {
+ val obj = it.asJsonObject
+ for ((k, v) in obj.entrySet()) {
+ val part = playerModelPartIndex[k]!!
+ trackedData = if (v.asBoolean) {
+ trackedData and (part.mask.inv().toByte())
+ } else {
+ trackedData or (part.mask.toByte())
+ }
+ }
+ }
+ entity.entityData.set(Player.DATA_PLAYER_MODE_CUSTOMISATION, trackedData)
+ }
+ entity.skinTextures = PlayerSkin(
+ bodyTexture, capeTexture, null, model, true
+ )
+ return entity
+ }
}
diff --git a/src/main/kotlin/gui/entity/ModifyRiding.kt b/src/main/kotlin/gui/entity/ModifyRiding.kt
index 5c4c78d..51fcfae 100644
--- a/src/main/kotlin/gui/entity/ModifyRiding.kt
+++ b/src/main/kotlin/gui/entity/ModifyRiding.kt
@@ -2,13 +2,13 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.entity.LivingEntity
+import net.minecraft.world.entity.LivingEntity
object ModifyRiding : EntityModifier {
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
val newEntity = EntityRenderer.constructEntity(info)
require(newEntity != null)
- newEntity.startRiding(entity, true)
+ newEntity.startRiding(entity, true, false)
return entity
}
diff --git a/src/main/kotlin/gui/entity/ModifyWither.kt b/src/main/kotlin/gui/entity/ModifyWither.kt
index 6083d88..67252b8 100644
--- a/src/main/kotlin/gui/entity/ModifyWither.kt
+++ b/src/main/kotlin/gui/entity/ModifyWither.kt
@@ -2,14 +2,14 @@
package moe.nea.firmament.gui.entity
import com.google.gson.JsonObject
-import net.minecraft.entity.LivingEntity
-import net.minecraft.entity.boss.WitherEntity
+import net.minecraft.world.entity.LivingEntity
+import net.minecraft.world.entity.boss.wither.WitherBoss
object ModifyWither : EntityModifier {
override fun apply(entity: LivingEntity, info: JsonObject): LivingEntity {
- require(entity is WitherEntity)
+ require(entity is WitherBoss)
info["tiny"]?.let {
- entity.setInvulTimer(if (it.asBoolean) 800 else 0)
+ entity.invulnerableTicks = if (it.asBoolean) 800 else 0
}
info["armored"]?.let {
entity.health = if (it.asBoolean) 1F else entity.maxHealth
diff --git a/src/main/kotlin/gui/hud/MoulConfigHud.kt b/src/main/kotlin/gui/hud/MoulConfigHud.kt
index e99b069..b5d7cf7 100644
--- a/src/main/kotlin/gui/hud/MoulConfigHud.kt
+++ b/src/main/kotlin/gui/hud/MoulConfigHud.kt
@@ -1,66 +1,68 @@
-
package moe.nea.firmament.gui.hud
-import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
import io.github.notenoughupdates.moulconfig.gui.GuiContext
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
-import net.minecraft.resource.ResourceManager
-import net.minecraft.resource.SynchronousResourceReloader
+import io.github.notenoughupdates.moulconfig.platform.MoulConfigScreenComponent
+import net.minecraft.server.packs.resources.ResourceManager
+import net.minecraft.server.packs.resources.ResourceManagerReloadListener
+import net.minecraft.network.chat.Component
import moe.nea.firmament.events.FinalizeResourceManagerEvent
import moe.nea.firmament.events.HudRenderEvent
import moe.nea.firmament.gui.config.HudMeta
+import moe.nea.firmament.jarvis.JarvisIntegration
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.MoulConfigUtils
abstract class MoulConfigHud(
- val name: String,
- val hudMeta: HudMeta,
+ val name: String,
+ val hudMeta: HudMeta,
) {
- companion object {
- private val componentWrapper by lazy {
- object : GuiComponentWrapper(GuiContext(TextComponent("§cERROR"))) {
- init {
- this.client = MC.instance
- }
- }
- }
- }
+ companion object {
+ private val componentWrapper by lazy {
+ object : MoulConfigScreenComponent(Component.empty(), GuiContext(TextComponent("§cERROR")), null) {
+ init {
+ this.minecraft = MC.instance
+ }
+ }
+ }
+ }
- private var fragment: GuiContext? = null
+ private var fragment: GuiContext? = null
- fun forceInit() {
- }
+ fun forceInit() {
+ }
- open fun shouldRender(): Boolean {
- return true
- }
+ open fun shouldRender(): Boolean {
+ return true
+ }
- init {
- require(name.matches("^[a-z_/]+$".toRegex()))
- HudRenderEvent.subscribe("MoulConfigHud:render") {
- if (!shouldRender()) return@subscribe
- val renderContext = componentWrapper.createContext(it.context)
- if (fragment == null)
- loadFragment()
- it.context.matrices.push()
- hudMeta.applyTransformations(it.context.matrices)
- val renderContextTranslated =
- renderContext.translated(hudMeta.absoluteX, hudMeta.absoluteY, hudMeta.width, hudMeta.height)
- .scaled(hudMeta.scale)
- fragment!!.root.render(renderContextTranslated)
- it.context.matrices.pop()
- }
- FinalizeResourceManagerEvent.subscribe("MoulConfigHud:finalizeResourceManager") {
- MC.resourceManager.registerReloader(object : SynchronousResourceReloader {
- override fun reload(manager: ResourceManager?) {
- fragment = null
- }
- })
- }
- }
+ init {
+ require(name.matches("^[a-z_/]+$".toRegex()))
+ HudRenderEvent.subscribe("MoulConfigHud:render") {
+ if (!shouldRender()) return@subscribe
+ val renderContext = componentWrapper.createContext(it.context)
+ if (fragment == null)
+ loadFragment()
+ it.context.pose().pushMatrix()
+ hudMeta.applyTransformations(it.context.pose())
+ val pos = hudMeta.getEffectivePosition(JarvisIntegration.jarvis)
+ val renderContextTranslated =
+ renderContext.translated(pos.x(), pos.y(), hudMeta.effectiveWidth, hudMeta.effectiveHeight)
+ .scaled(hudMeta.scale)
+ fragment!!.root.render(renderContextTranslated)
+ it.context.pose().popMatrix()
+ }
+ FinalizeResourceManagerEvent.subscribe("MoulConfigHud:finalizeResourceManager") {
+ MC.resourceManager.registerReloadListener(object : ResourceManagerReloadListener {
+ override fun onResourceManagerReload(manager: ResourceManager?) {
+ fragment = null
+ }
+ })
+ }
+ }
- fun loadFragment() {
- fragment = MoulConfigUtils.loadGui(name, this)
- }
+ fun loadFragment() {
+ fragment = MoulConfigUtils.loadGui(name, this)
+ }
}