aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCow <cow@volloeko.de>2021-07-08 11:56:52 +0200
committerCow <cow@volloeko.de>2021-07-08 11:56:52 +0200
commit060839b4960cf95d428d9ecbc69e455c913d720d (patch)
tree99e8a969ee8aaa6aaf16069c78ad43051b18c585
parent09e28b36a3159e35710b06e82a60c6814716f230 (diff)
downloadCowlection-060839b4960cf95d428d9ecbc69e455c913d720d.tar.gz
Cowlection-060839b4960cf95d428d9ecbc69e455c913d720d.tar.bz2
Cowlection-060839b4960cf95d428d9ecbc69e455c913d720d.zip
Added new command /commandslist
- list all client-side commands added by all installed mods
-rw-r--r--CHANGELOG.md1
-rw-r--r--README.md1
-rw-r--r--src/main/java/de/cowtipper/cowlection/Cowlection.java6
-rw-r--r--src/main/java/de/cowtipper/cowlection/command/NumerousCommandsCommand.java79
-rw-r--r--src/main/java/de/cowtipper/cowlection/config/gui/MooConfigMenuList.java4
-rw-r--r--src/main/java/de/cowtipper/cowlection/listener/ChatListener.java3
-rw-r--r--src/main/java/de/cowtipper/cowlection/numerouscommands/CommandInfo.java40
-rw-r--r--src/main/java/de/cowtipper/cowlection/numerouscommands/ModInfo.java70
-rw-r--r--src/main/java/de/cowtipper/cowlection/numerouscommands/NumerousCommandsGui.java203
9 files changed, 399 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41fd013..263ecf2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [1.8.9-0.14.0] - unreleased
### Added
+- New command: `/commandslist` to list all client-side commands added by all installed mods
- Chest Tracker & Analyzer:
- added support for 'lowest BIN' prices
- double clicking an analysis row now highlights chests that contain the clicked item
diff --git a/README.md b/README.md
index f8fad6d..19d15f0 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@ It is a collection of different features mainly focused on Hypixel SkyBlock. ðŸ
| Search through your Minecraft log files | `/moo search` (click the `?` for more info) |
| Stalk a player (check online status, current game, ...) | `/moo stalk` |
| Toggle join/leave notifications for friends, guild members or best friends separately | `/moo config` &rarr; Notifications |
+| Show all client-side commands added by all installed mods | `/commandslist` |
| Copy chat component | <kbd>ALT</kbd> + <kbd>right click</kbd><br>Hold <kbd>shift</kbd> to copy full component |
| Copy inventories to clipboard as JSON | <kbd>CTRL</kbd> + <kbd>C</kbd> (whole inventory)<br><kbd>CTRL</kbd> + <kbd>SHIFT</kbd> + <kbd>C</kbd> (single item) |
| Copy info of "the thing" you're looking at (NPC or mob + nearby "text-only" armor stands; armor stand, placed skull, banner, sign, dropped item, item in item frame, map on wall) | `/moo whatAmILookingAt` |
diff --git a/src/main/java/de/cowtipper/cowlection/Cowlection.java b/src/main/java/de/cowtipper/cowlection/Cowlection.java
index fad3338..e91f0b6 100644
--- a/src/main/java/de/cowtipper/cowlection/Cowlection.java
+++ b/src/main/java/de/cowtipper/cowlection/Cowlection.java
@@ -1,10 +1,7 @@
package de.cowtipper.cowlection;
import de.cowtipper.cowlection.chesttracker.ChestTracker;
-import de.cowtipper.cowlection.command.MooCommand;
-import de.cowtipper.cowlection.command.ReplyCommand;
-import de.cowtipper.cowlection.command.ShrugCommand;
-import de.cowtipper.cowlection.command.TabCompletableCommand;
+import de.cowtipper.cowlection.command.*;
import de.cowtipper.cowlection.config.CredentialStorage;
import de.cowtipper.cowlection.config.MooConfig;
import de.cowtipper.cowlection.handler.DungeonCache;
@@ -75,6 +72,7 @@ public class Cowlection {
MinecraftForge.EVENT_BUS.register(new ChatListener(this));
MinecraftForge.EVENT_BUS.register(new PlayerListener(this));
ClientCommandHandler.instance.registerCommand(new MooCommand(this));
+ ClientCommandHandler.instance.registerCommand(new NumerousCommandsCommand());
ClientCommandHandler.instance.registerCommand(new ReplyCommand());
ClientCommandHandler.instance.registerCommand(new ShrugCommand(this));
for (String tabCompletableNamesCommand : MooConfig.tabCompletableNamesCommands) {
diff --git a/src/main/java/de/cowtipper/cowlection/command/NumerousCommandsCommand.java b/src/main/java/de/cowtipper/cowlection/command/NumerousCommandsCommand.java
new file mode 100644
index 0000000..08486e3
--- /dev/null
+++ b/src/main/java/de/cowtipper/cowlection/command/NumerousCommandsCommand.java
@@ -0,0 +1,79 @@
+package de.cowtipper.cowlection.command;
+
+import de.cowtipper.cowlection.numerouscommands.ModInfo;
+import de.cowtipper.cowlection.numerouscommands.NumerousCommandsGui;
+import de.cowtipper.cowlection.util.TickDelay;
+import net.minecraft.client.Minecraft;
+import net.minecraft.command.CommandBase;
+import net.minecraft.command.CommandException;
+import net.minecraft.command.ICommand;
+import net.minecraft.command.ICommandSender;
+import net.minecraftforge.client.ClientCommandHandler;
+import net.minecraftforge.fml.common.Loader;
+import net.minecraftforge.fml.common.ModContainer;
+
+import java.util.*;
+
+public class NumerousCommandsCommand extends CommandBase {
+ @Override
+ public String getCommandName() {
+ return "commandslist";
+ }
+
+ @Override
+ public List<String> getCommandAliases() {
+ return Arrays.asList("clientcommands", "listcommands");
+ }
+
+ @Override
+ public String getCommandUsage(ICommandSender sender) {
+ return "/" + getCommandName();
+ }
+
+ @Override
+ public void processCommand(ICommandSender sender, String[] args) throws CommandException {
+ HashSet<String> ignoredMods = new HashSet<>(Arrays.asList("Minecraft Coder Pack", "Forge Mod Loader", "Minecraft Forge"));
+
+ Map<String, ModInfo> modsInfo = new TreeMap<>();
+
+ for (ModContainer mod : Loader.instance().getActiveModList()) {
+ String modName = mod.getName();
+ if (ignoredMods.contains(modName)) {
+ // ignored mod
+ continue;
+ }
+ modsInfo.put(modName, new ModInfo(mod));
+ }
+
+ ModInfo unknownMod = new ModInfo();
+ for (Map.Entry<String, ICommand> cmdEntry : ClientCommandHandler.instance.getCommands().entrySet()) {
+ String cmdName = cmdEntry.getKey();
+ ICommand cmd = cmdEntry.getValue();
+ if (!cmdName.equalsIgnoreCase(cmd.getCommandName())) {
+ // skip command alias
+ continue;
+ }
+ String cmdPackageName = cmd.getClass().getPackage().getName();
+ boolean foundOwningMod = false;
+ for (ModInfo modInfo : modsInfo.values()) {
+ if (modInfo.isOwnedPackage(cmdPackageName)) {
+ modInfo.addCommand(cmd, sender);
+ foundOwningMod = true;
+ break;
+ }
+ }
+ if (!foundOwningMod) {
+ unknownMod.addCommand(cmd, sender);
+ }
+ }
+ if (unknownMod.getCommandsCount() > 0) {
+ modsInfo.put("zzzzzzzz_unknown", unknownMod);
+ }
+ new TickDelay(() -> Minecraft.getMinecraft().displayGuiScreen(new NumerousCommandsGui(modsInfo.values())), 1);
+ }
+
+ @Override
+ public int getRequiredPermissionLevel() {
+ return 0;
+ }
+}
diff --git a/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigMenuList.java b/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigMenuList.java
index e72d27b..bd29d4a 100644
--- a/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigMenuList.java
+++ b/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigMenuList.java
@@ -5,7 +5,7 @@ import de.cowtipper.cowlection.config.MooConfigCategory;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.Tessellator;
-import net.minecraft.util.StringUtils;
+import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.fml.client.GuiScrollingList;
/**
@@ -54,7 +54,7 @@ public class MooConfigMenuList extends GuiScrollingList {
@Override
protected void drawSlot(int idx, int right, int top, int height, Tessellator tess) {
MooConfigCategory configCategory = MooConfig.getConfigCategories().get(idx);
- String name = StringUtils.stripControlCodes(configCategory.getMenuDisplayName());
+ String name = EnumChatFormatting.getTextWithoutFormattingCodes(configCategory.getMenuDisplayName());
FontRenderer font = Minecraft.getMinecraft().fontRendererObj;
font.drawString(font.trimStringToWidth(name, listWidth - 10), this.left + 3, top + 2, 0xFFFFFF);
diff --git a/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java b/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java
index e0ce09b..00f9649 100644
--- a/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java
+++ b/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java
@@ -16,7 +16,6 @@ import net.minecraft.client.gui.GuiNewChat;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
-import net.minecraft.util.StringUtils;
import net.minecraftforge.client.ClientCommandHandler;
import net.minecraftforge.client.event.ClientChatReceivedEvent;
import net.minecraftforge.client.event.GuiOpenEvent;
@@ -119,7 +118,7 @@ public class ChatListener {
if (copyWithFormatting) {
chatData = main.getChatHelper().cleanChatComponent(chatComponent);
} else {
- chatData = StringUtils.stripControlCodes(chatComponent.getUnformattedText());
+ chatData = EnumChatFormatting.getTextWithoutFormattingCodes(chatComponent.getUnformattedText());
if (chatData.startsWith(": ")) {
chatData = chatData.substring(2);
}
diff --git a/src/main/java/de/cowtipper/cowlection/numerouscommands/CommandInfo.java b/src/main/java/de/cowtipper/cowlection/numerouscommands/CommandInfo.java
new file mode 100644
index 0000000..2807068
--- /dev/null
+++ b/src/main/java/de/cowtipper/cowlection/numerouscommands/CommandInfo.java
@@ -0,0 +1,40 @@
+package de.cowtipper.cowlection.numerouscommands;
+
+import net.minecraft.command.ICommand;
+import net.minecraft.command.ICommandSender;
+
+import java.util.List;
+
+public class CommandInfo {
+ private final String name;
+ private final List<String> aliases;
+ private final String usage;
+ private boolean isListCommandsCommand;
+
+ public CommandInfo(ICommand cmd, ICommandSender sender) {
+ name = cmd.getCommandName();
+ aliases = cmd.getCommandAliases();
+ usage = cmd.getCommandUsage(sender);
+ isListCommandsCommand = false;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List<String> getAliases() {
+ return aliases;
+ }
+
+ public String getUsage() {
+ return (usage != null && !usage.replace("/", "").equalsIgnoreCase(name)) ? usage : null;
+ }
+
+ public boolean isListCommandsCommand() {
+ return isListCommandsCommand;
+ }
+
+ public void setIsListCommandsCommand() {
+ isListCommandsCommand = true;
+ }
+}
diff --git a/src/main/java/de/cowtipper/cowlection/numerouscommands/ModInfo.java b/src/main/java/de/cowtipper/cowlection/numerouscommands/ModInfo.java
new file mode 100644
index 0000000..15f4522
--- /dev/null
+++ b/src/main/java/de/cowtipper/cowlection/numerouscommands/ModInfo.java
@@ -0,0 +1,70 @@
+package de.cowtipper.cowlection.numerouscommands;
+
+import de.cowtipper.cowlection.command.NumerousCommandsCommand;
+import net.minecraft.command.ICommand;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.ForgeVersion;
+import net.minecraftforge.fml.common.ModContainer;
+import net.minecraftforge.fml.common.ModMetadata;
+
+import java.util.*;
+
+public class ModInfo {
+ private final String name;
+ private final ModMetadata metadata;
+ private final Set<String> ownedPackages;
+ private final Map<String, CommandInfo> commands;
+ private final boolean hasUpdate;
+
+ public ModInfo(ModContainer mod) {
+ name = mod.getName();
+ metadata = mod.getMetadata();
+ ownedPackages = new HashSet<>(mod.getOwnedPackages());
+ commands = new TreeMap<>();
+ hasUpdate = ForgeVersion.getResult(mod).status == ForgeVersion.Status.OUTDATED;
+ }
+
+ public ModInfo() {
+ name = "Unknown mod";
+ metadata = new ModMetadata();
+ metadata.modId = "unknownmodwithoutanid";
+ metadata.name = name;
+ metadata.authorList = Collections.singletonList(EnumChatFormatting.ITALIC + "Unknown");
+ ownedPackages = new HashSet<>();
+ commands = new TreeMap<>();
+ hasUpdate = false;
+ }
+
+ public void addCommand(ICommand cmd, ICommandSender sender) {
+ CommandInfo commandInfo = new CommandInfo(cmd, sender);
+ if (cmd instanceof NumerousCommandsCommand) {
+ commandInfo.setIsListCommandsCommand();
+ }
+ commands.put(cmd.getClass().getSimpleName() + cmd.getCommandName(), commandInfo);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ModMetadata getModMetadata() {
+ return metadata;
+ }
+
+ public Collection<CommandInfo> getCommands() {
+ return commands.values();
+ }
+
+ public int getCommandsCount() {
+ return commands.size();
+ }
+
+ public boolean hasUpdate() {
+ return hasUpdate;
+ }
+
+ public boolean isOwnedPackage(String packAge) {
+ return ownedPackages.contains(packAge);
+ }
+}
diff --git a/src/main/java/de/cowtipper/cowlection/numerouscommands/NumerousCommandsGui.java b/src/main/java/de/cowtipper/cowlection/numerouscommands/NumerousCommandsGui.java
new file mode 100644
index 0000000..cc06055
--- /dev/null
+++ b/src/main/java/de/cowtipper/cowlection/numerouscommands/NumerousCommandsGui.java
@@ -0,0 +1,203 @@
+package de.cowtipper.cowlection.numerouscommands;
+
+
+import com.google.common.base.Joiner;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.GuiUtilRenderComponents;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.command.CommandBase;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.IChatComponent;
+import net.minecraftforge.common.ForgeHooks;
+import net.minecraftforge.fml.client.GuiScrollingList;
+import net.minecraftforge.fml.client.config.GuiButtonExt;
+import net.minecraftforge.fml.common.ModMetadata;
+import net.minecraftforge.fml.relauncher.ReflectionHelper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Based on GuiModList
+ */
+public class NumerousCommandsGui extends GuiScreen {
+ private final List<String> lines;
+ private CommandsListGui commandsList;
+ private GuiButtonExt btnClose;
+ private static float lastScrollDistance;
+
+ public NumerousCommandsGui(Collection<ModInfo> modsInfo) {
+ this.lines = new ArrayList<>();
+ String unknown = EnumChatFormatting.ITALIC + "unknown";
+ for (ModInfo modInfo : modsInfo) {
+ if (!lines.isEmpty()) {
+ lines.add(null);
+ lines.add("" + EnumChatFormatting.GRAY + EnumChatFormatting.BOLD + EnumChatFormatting.STRIKETHROUGH + "--------------------------------------------------");
+ lines.add(null);
+ }
+ lines.add("" + EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + modInfo.getName());
+ ModMetadata modMetadata = modInfo.getModMetadata();
+ if (modMetadata != null) {
+ int authorsCount = modMetadata.authorList.size();
+ addKeyValue("Author" + (authorsCount > 1 ? "s" : ""), (authorsCount == 0 ? unknown : CommandBase.joinNiceStringFromCollection(modMetadata.authorList)), true);
+ addKeyValue("Version", (modMetadata.version.isEmpty() ? unknown : modMetadata.version) + (modInfo.hasUpdate() ? EnumChatFormatting.GREEN + " (update available!)" : ""), true);
+ if (!modMetadata.url.isEmpty()) {
+ addKeyValue("URL", modMetadata.url, true);
+ }
+ }
+ lines.add("" + EnumChatFormatting.UNDERLINE + EnumChatFormatting.ITALIC + "Commands" + EnumChatFormatting.RESET + EnumChatFormatting.GRAY + " (" + modInfo.getCommandsCount() + ")");
+ Collection<CommandInfo> commands = modInfo.getCommands();
+ for (CommandInfo cmd : commands) {
+ String cmdNameAndAliases = EnumChatFormatting.YELLOW + " ‣ /" + cmd.getName();
+ if (cmd.getAliases().size() > 0) {
+ cmdNameAndAliases += EnumChatFormatting.DARK_GRAY + " (" + (cmd.getAliases().size() == 1 ? "alias" : "aliases") + ": " + EnumChatFormatting.GRAY + Joiner.on(", ").join(cmd.getAliases()) + EnumChatFormatting.DARK_GRAY + ")";
+ }
+ lines.add(cmdNameAndAliases);
+ String cmdUsage = cmd.getUsage();
+ if (cmdUsage != null) {
+ if (cmdUsage.contains("\n")) {
+ addKeyValue("Usage", "", false);
+ for (String usageLine : cmdUsage.split("\n")) {
+ lines.add(" " + usageLine);
+ }
+ } else {
+ addKeyValue("Usage", cmdUsage, false);
+ }
+ } else if (cmd.isListCommandsCommand()) {
+ addKeyValue("Usage", EnumChatFormatting.GREEN + "You have just used this command to open this GUI", false);
+ }
+ }
+ }
+ }
+
+ private void addKeyValue(String key, String value, boolean light) {
+ EnumChatFormatting colorCodeKey = light ? EnumChatFormatting.GRAY : EnumChatFormatting.DARK_GRAY;
+ EnumChatFormatting colorCodeValue = light ? EnumChatFormatting.RESET : EnumChatFormatting.GRAY;
+ lines.add(" " + colorCodeKey + key + ":" + colorCodeValue + " " + value);
+ }
+
+ @Override
+ public void initGui() {
+ // close button
+ this.buttonList.add(this.btnClose = new GuiButtonExt(1, this.width - 25, 4, 22, 20, EnumChatFormatting.RED + "X"));
+ updateLastScrollDistance();
+ // scrollable commands list
+ commandsList = new CommandsListGui(this.width - 30, this.lines, lastScrollDistance);
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ drawDefaultBackground();
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ GlStateManager.pushMatrix();
+ double scaleFactor = 1.5;
+ GlStateManager.scale(scaleFactor, scaleFactor, 0);
+ this.drawString(this.fontRendererObj, "All client-side commands", 30, 6, 0xFFCC00);
+ GlStateManager.popMatrix();
+ this.commandsList.drawScreen(mouseX, mouseY, partialTicks);
+ }
+
+ @Override
+ protected void actionPerformed(GuiButton button) {
+ if (button == btnClose) {
+ this.mc.displayGuiScreen(null);
+ }
+ }
+
+ @Override
+ public void onGuiClosed() {
+ updateLastScrollDistance();
+ }
+
+ private void updateLastScrollDistance() {
+ if (this.commandsList != null) {
+ try {
+ lastScrollDistance = ReflectionHelper.getPrivateValue(GuiScrollingList.class, this.commandsList, "scrollDistance");
+ } catch (ReflectionHelper.UnableToAccessFieldException ignored) {
+ lastScrollDistance = 0;
+ }
+ }
+ }
+
+ /**
+ * Based on GuiModList.Info
+ */
+ private class CommandsListGui extends GuiScrollingList {
+ private final List<IChatComponent> lines;
+
+ public CommandsListGui(int width, List<String> lines, float lastScrollDistance) {
+ super(NumerousCommandsGui.this.mc,
+ width,
+ NumerousCommandsGui.this.height,
+ 30, NumerousCommandsGui.this.height - 5, 12, 11,
+ NumerousCommandsGui.this.width,
+ NumerousCommandsGui.this.height);
+ this.lines = resizeContent(lines);
+ try {
+ // scroll to previous location
+ ReflectionHelper.setPrivateValue(GuiScrollingList.class, this, lastScrollDistance, "scrollDistance");
+ } catch (ReflectionHelper.UnableToAccessFieldException ignored) {
+ }
+ }
+
+ private List<IChatComponent> resizeContent(List<String> lines) {
+ List<IChatComponent> ret = new ArrayList<>();
+ for (String line : lines) {
+ if (line == null) {
+ ret.add(null);
+ continue;
+ }
+ IChatComponent chat = ForgeHooks.newChatWithLinks(line, false);
+ ret.addAll(GuiUtilRenderComponents.splitText(chat, this.listWidth - 8, NumerousCommandsGui.this.fontRendererObj, false, true));
+ }
+ return ret;
+ }
+
+ @Override
+ protected int getSize() {
+ return lines != null ? lines.size() : 0;
+ }
+
+ @Override
+ protected void elementClicked(int index, boolean doubleClick) {
+ IChatComponent line = lines.get(index);
+ if (line != null) {
+ int xOffset = this.left;
+ for (IChatComponent part : line) {
+ if (!(part instanceof ChatComponentText)) {
+ continue;
+ }
+ xOffset += NumerousCommandsGui.this.fontRendererObj.getStringWidth(((ChatComponentText) part).getChatComponentText_TextValue());
+ if (xOffset >= this.mouseX) {
+ NumerousCommandsGui.this.handleComponentClick(part);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ protected boolean isSelected(int index) {
+ return false;
+ }
+
+ @Override
+ protected void drawBackground() {
+ }
+
+ @Override
+ protected void drawSlot(int slotIdx, int entryRight, int slotTop, int slotBuffer, Tessellator tess) {
+ IChatComponent line = lines.get(slotIdx);
+ if (line != null) {
+ GlStateManager.enableBlend();
+ NumerousCommandsGui.this.fontRendererObj.drawStringWithShadow(line.getFormattedText(), this.left + 4, slotTop, 0xFFFFFF);
+ GlStateManager.disableAlpha();
+ GlStateManager.disableBlend();
+ }
+ }
+ }
+}