diff --git a/gradle.properties b/gradle.properties index fa25158..2d1a769 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ yarn_mappings=1.20.4+build.3 loader_version=0.15.3 # Mod Properties -mod_version=1.7.0 +mod_version=1.7.1 maven_group=dev.dfonline archives_base_name=CodeClient diff --git a/src/main/java/dev/dfonline/codeclient/config/Config.java b/src/main/java/dev/dfonline/codeclient/config/Config.java index ebc77b6..ca1f32c 100644 --- a/src/main/java/dev/dfonline/codeclient/config/Config.java +++ b/src/main/java/dev/dfonline/codeclient/config/Config.java @@ -71,6 +71,7 @@ public class Config { public boolean ActionViewer = true; public boolean InvertActionViewerScroll = false; public ActionViewerAlignment ActionViewerLocation = ActionViewerAlignment.TOP; + public int RecentValues = 0; public Config() { } @@ -145,6 +146,7 @@ public void save() { object.addProperty("ActionViewer",ActionViewer); object.addProperty("InvertActionViewerScroll",InvertActionViewerScroll); object.addProperty("ActionViewerLocation",ActionViewerLocation.name()); + object.addProperty("RecentValues", RecentValues); FileManager.writeConfig(object.toString()); } catch (Exception e) { CodeClient.LOGGER.info("Couldn't save config: " + e); @@ -522,6 +524,16 @@ public YetAnotherConfigLib getLibConfig() { ) .controller(TickBoxControllerBuilder::create) .build()) + .option(Option.createBuilder(int.class) + .name(Text.literal("Recent Values")) + .description(OptionDescription.of(Text.literal("Amount of recently used values to remember."))) + .binding( + 0, + () -> RecentValues, + opt -> RecentValues = opt + ) + .controller(integerOption -> IntegerFieldControllerBuilder.create(integerOption).range(0, 100)) + .build()) .build()) // // diff --git a/src/main/java/dev/dfonline/codeclient/dev/RecentValues.java b/src/main/java/dev/dfonline/codeclient/dev/RecentValues.java new file mode 100644 index 0000000..9a3558a --- /dev/null +++ b/src/main/java/dev/dfonline/codeclient/dev/RecentValues.java @@ -0,0 +1,190 @@ +package dev.dfonline.codeclient.dev; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import dev.dfonline.codeclient.CodeClient; +import dev.dfonline.codeclient.FileManager; +import dev.dfonline.codeclient.config.Config; +import dev.dfonline.codeclient.location.Dev; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents; +import net.minecraft.SharedConstants; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.datafixer.DataFixTypes; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.StringNbtReader; +import net.minecraft.screen.slot.Slot; +import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.random.Random; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class RecentValues { + + private static final Path file = FileManager.Path().resolve("recent_values.json"); + private static final List pinned = new ArrayList<>(); + private static final List recent = new ArrayList<>(); + private static ItemStack hoveredItem = null; + private static List hoveredOrigin = null; + + static { + try { + if (Files.exists(file)) { + JsonObject data = JsonParser.parseString(Files.readString(file)).getAsJsonObject(); + + int version = data.get("version").getAsInt(); + JsonArray pinnedJson = data.getAsJsonArray("pinned"); + JsonArray recentJson = data.getAsJsonArray("recent"); + + for (JsonElement item : pinnedJson) { + pinned.add(readItem(version, item)); + } + for (JsonElement item : recentJson) { + recent.add(readItem(version, item)); + } + } + + ClientLifecycleEvents.CLIENT_STOPPING.register(mc -> { + try { + if (!Files.exists(file.getParent())) Files.createDirectories(file.getParent()); + JsonObject data = new JsonObject(); + data.addProperty("version", SharedConstants.getGameVersion().getSaveVersion().getId()); + data.add("pinned", saveItems(pinned)); + data.add("recent", saveItems(recent)); + Files.writeString(file, data.toString()); + } catch (Exception err) { + CodeClient.LOGGER.error("Failed to save recent_values.json!"); + err.printStackTrace(); + } + }); + } catch (Exception err) { + CodeClient.LOGGER.error("Failed reading recent_values.json!"); + err.printStackTrace(); + } + + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (screen instanceof GenericContainerScreen container) { + if (!InsertOverlay.isCodeChest) return; + if (!(CodeClient.location instanceof Dev)) return; + + ScreenEvents.afterRender(screen).register((screen1, ctx, mouseX, mouseY, tickDelta) -> { + if(recent.isEmpty() && pinned.isEmpty()) return; + + int y = (int) (scaledHeight * 0.25); + int xEnd = (int) (scaledWidth * 0.25); + + ctx.drawGuiTexture(new Identifier("recipe_book/overlay_recipe"), 5, y - 5, + Math.min(Math.max(pinned.size(), recent.size()),16) * 15 + 10, + (((int) Math.ceil((double) pinned.size() / 16)) + ((int) Math.ceil((double) recent.size() / 16))) * 16 + 10 + ); + + hoveredItem = null; + hoveredOrigin = null; + for (List group : List.of(pinned, recent)) { + int x = 10; + for (ItemStack item : group) { + ctx.drawItem(item, x, y); + ctx.drawItemInSlot(CodeClient.MC.textRenderer, item, x, y); + if (mouseX > x && mouseY > y && mouseX < x + 15 && mouseY < y + 15) { + ctx.drawItemTooltip(CodeClient.MC.textRenderer, item, mouseX, mouseY); + hoveredItem = item; + hoveredOrigin = group; + } + x += 15; + if (x > xEnd) { + x = 10; + y += 15; + } + } + if (x != 10) y += 15; + } + }); + + ScreenMouseEvents.afterMouseClick(screen).register((screen1, mouseX, mouseY, button) -> { + if (hoveredItem == null) return; + + if (button != 1) { + for (Slot slot : container.getScreenHandler().slots) { + if (slot.hasStack()) continue; + CodeClient.MC.getSoundManager().play(new PositionedSoundInstance( + SoundEvents.ENTITY_ITEM_PICKUP, + SoundCategory.PLAYERS, + 2, 1f, Random.create(), + CodeClient.MC.player.getBlockPos() + )); + + if (!CodeClient.MC.player.isCreative()) return; + ItemStack previous = CodeClient.MC.player.getInventory().getStack(0); + CodeClient.MC.interactionManager.clickCreativeStack(hoveredItem, 36); + CodeClient.MC.interactionManager.clickSlot( + container.getScreenHandler().syncId, + slot.id, 0, SlotActionType.SWAP, CodeClient.MC.player + ); + CodeClient.MC.interactionManager.clickCreativeStack(previous, 36); + return; + } + } else { + hoveredOrigin.remove(hoveredItem); + if (hoveredOrigin == pinned) { + CodeClient.MC.getSoundManager().play(new PositionedSoundInstance( + SoundEvents.UI_BUTTON_CLICK.value(), + SoundCategory.PLAYERS, + 2, 0.5f, Random.create(), + CodeClient.MC.player.getBlockPos() + )); + return; + } + CodeClient.MC.getSoundManager().play(new PositionedSoundInstance( + SoundEvents.UI_BUTTON_CLICK.value(), + SoundCategory.PLAYERS, + 2, 0.6f, Random.create(), + CodeClient.MC.player.getBlockPos() + )); + pinned.add(hoveredItem); + } + }); + } + }); + } + + private static JsonArray saveItems(List list) { + JsonArray out = new JsonArray(); + + for (ItemStack item : list) { + out.add(item.writeNbt(new NbtCompound()).asString()); + } + + return out; + } + + private static ItemStack readItem(int version, JsonElement item) throws Exception { + return ItemStack.fromNbt(DataFixTypes.HOTBAR.update(CodeClient.MC.getDataFixer(), StringNbtReader.parse(item.getAsString()), version)); + } + + public static void remember(ItemStack item) { + if (item.getNbt() == null || item.getNbt().get("PublicBukkitValues") == null || item.getSubNbt("PublicBukkitValues").get("hypercube:varitem") == null) return; + for (ItemStack it : pinned) { + if (item.getItem() == it.getItem() && item.getNbt().equals(it.getNbt())) return; + } + + ItemStack lambdaItem = item; + recent.removeIf(it -> lambdaItem.getItem() == it.getItem() && lambdaItem.getNbt().equals(it.getNbt())); + item = item.copyWithCount(1); + recent.add(0, item); + + while (recent.size() > Config.getConfig().RecentValues) { + recent.remove(recent.size() - 1); + } + } +} diff --git a/src/main/java/dev/dfonline/codeclient/mixin/entity/player/MPlayerInventory.java b/src/main/java/dev/dfonline/codeclient/mixin/entity/player/MPlayerInventory.java new file mode 100644 index 0000000..4f74d08 --- /dev/null +++ b/src/main/java/dev/dfonline/codeclient/mixin/entity/player/MPlayerInventory.java @@ -0,0 +1,25 @@ +package dev.dfonline.codeclient.mixin.entity.player; + +import dev.dfonline.codeclient.CodeClient; +import dev.dfonline.codeclient.dev.RecentValues; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerInventory.class) +public class MPlayerInventory { + + @Shadow @Final public PlayerEntity player; + + @Inject(method = "setStack", at = @At("HEAD")) + public void onSetStack(int slot, ItemStack item, CallbackInfo ci) { + if (player == CodeClient.MC.player) RecentValues.remember(item); + } + +} diff --git a/src/main/resources/CodeClient.mixins.json b/src/main/resources/CodeClient.mixins.json index 0b7389e..9949148 100644 --- a/src/main/resources/CodeClient.mixins.json +++ b/src/main/resources/CodeClient.mixins.json @@ -13,6 +13,7 @@ "entity.player.MAbstractClientPlayerEntity", "entity.player.MClientPlayerEntity", "entity.player.MClientPlayerInteractionManager", + "entity.player.MPlayerInventory", "entity.player.MPlayerEntity", "network.MClientCommonPlayNetworkHandler", "network.MClientConnection", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 68cdac1..ca2c06e 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -15,7 +15,8 @@ }, "contributors": [ "RedVortex", - "msvae" + "msvae", + "BlazeMCworld" ], "license": "MIT", "icon": "assets/codeclient/icon.png",