diff --git a/src/main/java/io/github/homchom/recode/feature/RenderingFeatureGroup.kt b/src/main/java/io/github/homchom/recode/feature/RenderingFeatureGroup.kt index 576e94d9..e0cf2b8b 100644 --- a/src/main/java/io/github/homchom/recode/feature/RenderingFeatureGroup.kt +++ b/src/main/java/io/github/homchom/recode/feature/RenderingFeatureGroup.kt @@ -1,9 +1,11 @@ package io.github.homchom.recode.feature import io.github.homchom.recode.feature.rendering.FCodeSearch +import io.github.homchom.recode.feature.rendering.FQuickChestVars import io.github.homchom.recode.feature.rendering.FSignRenderDistance val RenderingFeatureGroup = featureGroup("Rendering", FSignRenderDistance, - FCodeSearch + FCodeSearch, + FQuickChestVars ) \ No newline at end of file diff --git a/src/main/java/io/github/homchom/recode/feature/rendering/QuickChestVars.kt b/src/main/java/io/github/homchom/recode/feature/rendering/QuickChestVars.kt new file mode 100644 index 00000000..8a668f95 --- /dev/null +++ b/src/main/java/io/github/homchom/recode/feature/rendering/QuickChestVars.kt @@ -0,0 +1,271 @@ +package io.github.homchom.recode.feature.rendering + +import com.google.gson.* +import com.mojang.blaze3d.vertex.PoseStack +import com.mojang.math.Vector4f +import io.github.homchom.recode.feature.feature +import io.github.homchom.recode.init.ClientStopEvent +import io.github.homchom.recode.mc +import io.github.homchom.recode.mod.config.Config +import io.github.homchom.recode.sys.file.ExternalFile +import io.github.homchom.recode.sys.networking.LegacyState +import io.github.homchom.recode.sys.player.DFInfo +import io.github.homchom.recode.sys.util.ItemUtil +import io.github.homchom.recode.sys.util.SoundUtil +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents +import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents +import net.minecraft.DetectedVersion +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.screens.inventory.ContainerScreen +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtUtils +import net.minecraft.nbt.TagParser +import net.minecraft.sounds.SoundEvents +import net.minecraft.util.datafix.DataFixTypes +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import java.io.File +import java.nio.file.Files + +const val GRID_SIZE = 20 + +val pinnedVars = mutableListOf() +val recentVars = mutableListOf() + +val file: File = ExternalFile.QUICK_VARS.path.toFile() + +val FQuickChestVars = feature("Quick Chest Vars") { + onLoad { + if (file.exists()) { + val json = JsonParser.parseString(Files.readString(file.toPath())) as JsonObject + + if (json.keySet().size != 0) { + val dataVer = json.get("dataVer").asInt + val pinned = json.getAsJsonArray("pinned") + val recent = json.getAsJsonArray("recent") + + for (item in pinned) { + pinnedVars.add(ItemStack.of(updateItem(dataVer, TagParser.parseTag(item.asString)))) + } + + for (item in recent) { + recentVars.add(ItemStack.of(updateItem(dataVer, TagParser.parseTag(item.asString)))) + } + } + } + + ScreenEvents.AFTER_INIT.register { mc: Minecraft, screen: Screen, width: Int, height: Int -> + if (mc.screen is ContainerScreen + && Config.getBoolean("quickChestVars") + ) { + val xStart = width * 0.75 + val yStart = height * 0.25 + val xEnd = width - GRID_SIZE + + ScreenEvents.afterRender(screen) + .register { _: Screen, stack: PoseStack, mouseX: Int, mouseY: Int, _: Float -> + if (DFInfo.currentState.getMode() != LegacyState.Mode.DEV + || !mc.player!!.isCreative + ) { + return@register + } + + stack.pushPose() + + stack.translate(xStart, yStart, 0.0) + + for (group in listOf(pinnedVars, recentVars)) { + var x = 0.0 + for (item in group) { + val pos = getOrigin(stack) + + mc.itemRenderer.renderGuiItem( + item, + pos.x().toInt(), + pos.y().toInt() + ) + + if (pos.x() < mouseX + && pos.y() < mouseY + && pos.x() + GRID_SIZE > mouseX + && pos.y() + GRID_SIZE > mouseY + ) { + stack.pushPose() + //mouse offset + stack.translate( + (mouseX - pos.x()).toDouble(), + (mouseY - pos.y()).toDouble(), + 0.0 + ) + + //pixel center + val currPos = getOrigin(stack) + stack.translate( + (currPos.x() % 1).toDouble(), + (currPos.y() % 1).toDouble(), + 0.0 + ) + screen.renderTooltip( + stack, + screen.getTooltipFromItem(item), + item.tooltipImage, + 0, + 0 + ) + stack.popPose() + } + + stack.translate(GRID_SIZE.toDouble(), 0.0, 0.0) + x += GRID_SIZE + if (getOrigin(stack).x() > xEnd) { + stack.translate(-x, GRID_SIZE.toDouble(), 0.0) + x = 0.0 + } + } + stack.translate(-x, GRID_SIZE * 1.5, 0.0) + } + + stack.popPose() + } + + ScreenMouseEvents.afterMouseClick(screen) + .register { _: Screen, mouseX: Double, mouseY: Double, button: Int -> + val relativeX = mouseX - xStart + var relativeY = mouseY - yStart + + if (relativeX < 0.0 + || relativeY < 0.0 + || mouseX > xEnd + ) { + return@register + } + + val gridX = (relativeX / GRID_SIZE).toInt() + val rowSize = ((xEnd - xStart) / GRID_SIZE).toInt() + 1 + val pinnedSize = pinnedVars.size / rowSize * GRID_SIZE + GRID_SIZE + + var area = pinnedVars + + if (relativeY > pinnedSize) { + area = recentVars + relativeY -= pinnedSize + GRID_SIZE / 2 + } + + val gridY = (relativeY / GRID_SIZE).toInt() + + val index = gridY * rowSize + gridX + + if (area.size > index) { + if (button != 1) { + val freeSlot = (screen as ContainerScreen).menu.slots.find { + it.item == ItemStack.EMPTY + } + if (freeSlot != null) { + ItemUtil.setContainerItem(freeSlot.index, area[index]) + SoundUtil.playSound(SoundEvents.ITEM_PICKUP) + } + } else { + //un/pin the item + if (area == recentVars) { + pinnedVars.add(area[index]) + SoundUtil.playSound(SoundEvents.ARROW_HIT) + } else { + SoundUtil.playSound(SoundEvents.SHULKER_BULLET_HIT) + } + area.removeAt(index) + } + } + + } + } + } + + ClientStopEvent.listen { _, _ -> + val currentVer = DetectedVersion.BUILT_IN.dataVersion.version + + val json = JsonObject() + json.addProperty("dataVer", currentVer) + + val pinnedArr = JsonArray() + for (item in pinnedVars) { + pinnedArr.add(item.save(CompoundTag()).asString) + } + json.add("pinned", pinnedArr) + + val recentArr = JsonArray() + for (item in recentVars) { + recentArr.add(item.save(CompoundTag()).asString) + } + json.add("recent", recentArr) + + Files.writeString(file.toPath(), json.toString()) + } + } +} + +fun recentItem(itemStack: ItemStack) { + val item = itemStack.copy() + + if (item.item == Items.AIR) { + return + } + + getDfData(item) ?: return + + item.count = 1 + + if (pinnedVars.any { same(it, item) }) { + return + } + + val i = recentVars.indexOfFirst { same(it, item) } + if (i != -1) { + recentVars.removeAt(i) + recentVars.add(0, item) + return + } + + recentVars.add(0, item) + if (recentVars.size > 50) { + recentVars.removeAt(recentVars.size - 1) + } +} + +fun same(x: ItemStack, y: ItemStack): Boolean { + val xData = getDfData(x) ?: return false + val yData = getDfData(y) ?: return false + + val xJson = tryParse(xData) ?: return false + val yJson = tryParse(yData) ?: return false + + return xJson == yJson +} + +fun getDfData(item: ItemStack?): String? { + if (item == null) return null + + val tag = item.getTagElement("PublicBukkitValues") ?: return null + + if (!tag.contains("hypercube:varitem")) return null + + return tag.getString("hypercube:varitem") +} + +fun updateItem(dataVer: Int, tag: CompoundTag): CompoundTag { + return NbtUtils.update(mc.fixerUpper, DataFixTypes.HOTBAR, tag, dataVer) +} + +fun getOrigin(poseStack: PoseStack): Vector4f { + val out = Vector4f(0f, 0f, 0f, 1f) + out.transform(poseStack.last().pose()) + return out +} + +fun tryParse(data: String): JsonElement? { + return try { + JsonParser.parseString(data) + } catch (e: JsonSyntaxException) { + null + } +} \ No newline at end of file diff --git a/src/main/java/io/github/homchom/recode/mixin/player/MInventory.java b/src/main/java/io/github/homchom/recode/mixin/player/MInventory.java new file mode 100644 index 00000000..14c49d96 --- /dev/null +++ b/src/main/java/io/github/homchom/recode/mixin/player/MInventory.java @@ -0,0 +1,22 @@ +package io.github.homchom.recode.mixin.player; + +import io.github.homchom.recode.GlobalsKt; +import io.github.homchom.recode.feature.rendering.QuickChestVarsKt; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Inventory.class) +public abstract class MInventory { + + @Inject(method = "setItem", at = @At("RETURN")) + private void onSetItem(int i, ItemStack itemStack, CallbackInfo ci) { + if ((Object) this == GlobalsKt.getMc().player.getInventory()) { + QuickChestVarsKt.recentItem(itemStack); + } + } + +} diff --git a/src/main/java/io/github/homchom/recode/mod/config/impl/ScreenGroup.java b/src/main/java/io/github/homchom/recode/mod/config/impl/ScreenGroup.java index 8e2f425f..2376691b 100644 --- a/src/main/java/io/github/homchom/recode/mod/config/impl/ScreenGroup.java +++ b/src/main/java/io/github/homchom/recode/mod/config/impl/ScreenGroup.java @@ -26,6 +26,7 @@ public void initialize() { // Code ConfigSubGroup code = new ConfigSubGroup("code"); code.register(new BooleanSetting("chestToolTip", true)); + code.register(new BooleanSetting("quickChestVars", true)); code.register(new BooleanSetting("templatePeeking", false)); code.register(new BooleanSetting("cpuOnScreen", true)); code.register(new BooleanSetting("variableScopeView", true)); diff --git a/src/main/java/io/github/homchom/recode/sys/file/ExternalFile.java b/src/main/java/io/github/homchom/recode/sys/file/ExternalFile.java index 19db955f..19a0d56f 100644 --- a/src/main/java/io/github/homchom/recode/sys/file/ExternalFile.java +++ b/src/main/java/io/github/homchom/recode/sys/file/ExternalFile.java @@ -1,5 +1,7 @@ package io.github.homchom.recode.sys.file; +import com.google.gson.JsonObject; + import java.nio.file.Path; public enum ExternalFile { @@ -11,7 +13,9 @@ public enum ExternalFile { .isDirectory(true) .setName("Images") .build()), - TEMPLATE_DB(ExternalFileBuilder.nbt("Templates.nbt")); + TEMPLATE_DB(ExternalFileBuilder.nbt("Templates.nbt")), + + QUICK_VARS(ExternalFileBuilder.json("QuickVars.json", new JsonObject())); private final Path path; diff --git a/src/main/java/io/github/homchom/recode/sys/file/ExternalFileBuilder.java b/src/main/java/io/github/homchom/recode/sys/file/ExternalFileBuilder.java index ccea528c..c8305195 100644 --- a/src/main/java/io/github/homchom/recode/sys/file/ExternalFileBuilder.java +++ b/src/main/java/io/github/homchom/recode/sys/file/ExternalFileBuilder.java @@ -1,5 +1,6 @@ package io.github.homchom.recode.sys.file; +import com.google.gson.JsonElement; import io.github.homchom.recode.Constants; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.nbt.*; @@ -80,4 +81,20 @@ static Path nbt(String name) { } }); } + + public static Path json(String name, JsonElement initVal) { + return new ExternalFileBuilder() + .isDirectory(false) + .setName(name) + .build(path -> { + try { + if (path.toFile().exists()) return; + + Files.writeString(path, initVal.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + } diff --git a/src/main/resources/assets/recode/lang/en_us.json b/src/main/resources/assets/recode/lang/en_us.json index 6b2030be..dad0c0b8 100644 --- a/src/main/resources/assets/recode/lang/en_us.json +++ b/src/main/resources/assets/recode/lang/en_us.json @@ -188,6 +188,8 @@ "config.recode.option.chestToolTip": "Chest Tooltip", "config.recode.option.chestToolTip.tooltip": "Display chest params when you are in dev", + "config.recode.option.quickChestVars": "Quick Chest Vars", + "config.recode.option.quickChestVars.tooltip": "Quickly get recent variables and similar while inside a chest.", "config.recode.option.chestToolTipType": "Chest Tooltip Style", "config.recode.option.chestToolTipType.tooltip": "Choose between regular Minecraft tooltip style \nor raw text in the top left corner.", diff --git a/src/main/resources/recode.mixins.json b/src/main/resources/recode.mixins.json index f387deef..1bb9c1ce 100644 --- a/src/main/resources/recode.mixins.json +++ b/src/main/resources/recode.mixins.json @@ -8,7 +8,8 @@ "client": [ "render.MBlockEntityRenderDispatcher", "render.MLevelRenderer", - "server.MClientPacketListener" + "server.MClientPacketListener", + "player.MInventory" ], "server": [], "injectors": {