diff options
Diffstat (limited to 'src/main/kotlin/util/mc')
-rw-r--r-- | src/main/kotlin/util/mc/InitLevel.kt | 25 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/MCTabListAPI.kt | 96 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/NbtUtil.kt | 10 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/SNbtFormatter.kt | 7 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/SkullItemData.kt | 3 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/SlotUtils.kt | 18 |
6 files changed, 152 insertions, 7 deletions
diff --git a/src/main/kotlin/util/mc/InitLevel.kt b/src/main/kotlin/util/mc/InitLevel.kt new file mode 100644 index 0000000..2c3eedb --- /dev/null +++ b/src/main/kotlin/util/mc/InitLevel.kt @@ -0,0 +1,25 @@ +package moe.nea.firmament.util.mc + +enum class InitLevel { + STARTING, + MC_INIT, + RENDER_INIT, + RENDER, + MAIN_MENU, + ; + + companion object { + var initLevel = InitLevel.STARTING + private set + + @JvmStatic + fun isAtLeast(wantedLevel: InitLevel): Boolean = initLevel >= wantedLevel + + @JvmStatic + fun bump(nextLevel: InitLevel) { + if (nextLevel.ordinal != initLevel.ordinal + 1) + error("Cannot bump initLevel $nextLevel from $initLevel") + initLevel = nextLevel + } + } +} diff --git a/src/main/kotlin/util/mc/MCTabListAPI.kt b/src/main/kotlin/util/mc/MCTabListAPI.kt new file mode 100644 index 0000000..66bdd55 --- /dev/null +++ b/src/main/kotlin/util/mc/MCTabListAPI.kt @@ -0,0 +1,96 @@ +package moe.nea.firmament.util.mc + +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import java.util.Optional +import org.jetbrains.annotations.TestOnly +import net.minecraft.client.gui.hud.PlayerListHud +import net.minecraft.nbt.NbtOps +import net.minecraft.scoreboard.Team +import net.minecraft.text.Text +import net.minecraft.text.TextCodecs +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.commands.thenExecute +import moe.nea.firmament.commands.thenLiteral +import moe.nea.firmament.events.CommandEvent +import moe.nea.firmament.events.TickEvent +import moe.nea.firmament.features.debug.DeveloperFeatures +import moe.nea.firmament.features.debug.ExportedTestConstantMeta +import moe.nea.firmament.mixins.accessor.AccessorPlayerListHud +import moe.nea.firmament.util.ClipboardUtils +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.intoOptional +import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString + +object MCTabListAPI { + + fun PlayerListHud.cast() = this as AccessorPlayerListHud + + @Subscribe + fun onTick(event: TickEvent) { + _currentTabList = null + } + + @Subscribe + fun devCommand(event: CommandEvent.SubCommand) { + event.subcommand(DeveloperFeatures.DEVELOPER_SUBCOMMAND) { + thenLiteral("copytablist") { + thenExecute { + currentTabList.body.forEach { + MC.sendChat(Text.literal(TextCodecs.CODEC.encodeStart(NbtOps.INSTANCE, it).orThrow.toString())) + } + var compound = CurrentTabList.CODEC.encodeStart(NbtOps.INSTANCE, currentTabList).orThrow + compound = ExportedTestConstantMeta.SOURCE_CODEC.encode( + ExportedTestConstantMeta.current, + NbtOps.INSTANCE, + compound + ).orThrow + ClipboardUtils.setTextContent( + compound.toPrettyString() + ) + } + } + } + } + + @get:TestOnly + @set:TestOnly + var _currentTabList: CurrentTabList? = null + + val currentTabList get() = _currentTabList ?: getTabListNow().also { _currentTabList = it } + + data class CurrentTabList( + val header: Optional<Text>, + val footer: Optional<Text>, + val body: List<Text>, + ) { + companion object { + val CODEC: Codec<CurrentTabList> = RecordCodecBuilder.create { + it.group( + TextCodecs.CODEC.optionalFieldOf("header").forGetter(CurrentTabList::header), + TextCodecs.CODEC.optionalFieldOf("footer").forGetter(CurrentTabList::footer), + TextCodecs.CODEC.listOf().fieldOf("body").forGetter(CurrentTabList::body), + ).apply(it, ::CurrentTabList) + } + } + } + + private fun getTabListNow(): CurrentTabList { + // This is a precondition for PlayerListHud.collectEntries to be valid + MC.networkHandler ?: return CurrentTabList(Optional.empty(), Optional.empty(), emptyList()) + val hud = MC.inGameHud.playerListHud.cast() + val entries = hud.collectPlayerEntries_firmament() + .map { + it.displayName ?: run { + val team = it.scoreboardTeam + val name = it.profile.name + Team.decorateName(team, Text.literal(name)) + } + } + return CurrentTabList( + header = hud.header_firmament.intoOptional(), + footer = hud.footer_firmament.intoOptional(), + body = entries, + ) + } +} diff --git a/src/main/kotlin/util/mc/NbtUtil.kt b/src/main/kotlin/util/mc/NbtUtil.kt new file mode 100644 index 0000000..2cab1c7 --- /dev/null +++ b/src/main/kotlin/util/mc/NbtUtil.kt @@ -0,0 +1,10 @@ +package moe.nea.firmament.util.mc + +import net.minecraft.nbt.NbtElement +import net.minecraft.nbt.NbtList + +fun Iterable<NbtElement>.toNbtList() = NbtList().also { + for (element in this) { + it.add(element) + } +} diff --git a/src/main/kotlin/util/mc/SNbtFormatter.kt b/src/main/kotlin/util/mc/SNbtFormatter.kt index e2c24f6..7617d17 100644 --- a/src/main/kotlin/util/mc/SNbtFormatter.kt +++ b/src/main/kotlin/util/mc/SNbtFormatter.kt @@ -110,7 +110,7 @@ class SNbtFormatter private constructor() : NbtElementVisitor { keys.forEachIndexed { index, key -> writeIndent() val element = compound[key] ?: error("Key '$key' found but not present in compound: $compound") - val escapedName = if (key.matches(SIMPLE_NAME)) key else NbtString.escape(key) + val escapedName = escapeName(key) result.append(escapedName).append(": ") element.accept(this) if (keys.size != index + 1) { @@ -134,6 +134,9 @@ class SNbtFormatter private constructor() : NbtElementVisitor { fun NbtElement.toPrettyString() = prettify(this) - private val SIMPLE_NAME = "[A-Za-z0-9._+-]+".toRegex() + fun escapeName(key: String): String = + if (key.matches(SIMPLE_NAME)) key else NbtString.escape(key) + + val SIMPLE_NAME = "[A-Za-z0-9._+-]+".toRegex() } } diff --git a/src/main/kotlin/util/mc/SkullItemData.kt b/src/main/kotlin/util/mc/SkullItemData.kt index 0405b65..1b7dcba 100644 --- a/src/main/kotlin/util/mc/SkullItemData.kt +++ b/src/main/kotlin/util/mc/SkullItemData.kt @@ -10,7 +10,6 @@ import kotlinx.datetime.Clock import kotlinx.datetime.Instant import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers -import kotlinx.serialization.encodeToString import net.minecraft.component.DataComponentTypes import net.minecraft.component.type.ProfileComponent import net.minecraft.item.ItemStack @@ -51,7 +50,7 @@ fun ItemStack.setEncodedSkullOwner(uuid: UUID, encodedData: String) { this.set(DataComponentTypes.PROFILE, ProfileComponent(gameProfile)) } -val zeroUUID = UUID.fromString("d3cb85e2-3075-48a1-b213-a9bfb62360c1") +val arbitraryUUID = UUID.fromString("d3cb85e2-3075-48a1-b213-a9bfb62360c1") fun createSkullItem(uuid: UUID, url: String) = ItemStack(Items.PLAYER_HEAD) .also { it.setSkullOwner(uuid, url) } diff --git a/src/main/kotlin/util/mc/SlotUtils.kt b/src/main/kotlin/util/mc/SlotUtils.kt index 4709dcf..9eb4918 100644 --- a/src/main/kotlin/util/mc/SlotUtils.kt +++ b/src/main/kotlin/util/mc/SlotUtils.kt @@ -1,5 +1,6 @@ package moe.nea.firmament.util.mc +import org.lwjgl.glfw.GLFW import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType @@ -10,7 +11,7 @@ object SlotUtils { MC.interactionManager?.clickSlot( handler.syncId, this.id, - 2, + GLFW.GLFW_MOUSE_BUTTON_MIDDLE, SlotActionType.CLONE, MC.player ) @@ -20,14 +21,25 @@ object SlotUtils { MC.interactionManager?.clickSlot( handler.syncId, this.id, hotbarIndex, SlotActionType.SWAP, - MC.player) + MC.player + ) } fun Slot.clickRightMouseButton(handler: ScreenHandler) { MC.interactionManager?.clickSlot( handler.syncId, this.id, - 1, + GLFW.GLFW_MOUSE_BUTTON_RIGHT, + SlotActionType.PICKUP, + MC.player + ) + } + + fun Slot.clickLeftMouseButton(handler: ScreenHandler) { + MC.interactionManager?.clickSlot( + handler.syncId, + this.id, + GLFW.GLFW_MOUSE_BUTTON_LEFT, SlotActionType.PICKUP, MC.player ) |