aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/eu/olli/cowmoonication/Cowmoonication.java63
-rw-r--r--src/main/java/eu/olli/cowmoonication/Friends.java55
-rw-r--r--src/main/java/eu/olli/cowmoonication/Utils.java57
-rw-r--r--src/main/java/eu/olli/cowmoonication/command/MooCommand.java115
-rw-r--r--src/main/java/eu/olli/cowmoonication/config/MooConfig.java113
-rw-r--r--src/main/java/eu/olli/cowmoonication/config/MooGuiConfig.java39
-rw-r--r--src/main/java/eu/olli/cowmoonication/config/MooGuiFactory.java29
-rw-r--r--src/main/java/eu/olli/cowmoonication/listener/ChatListener.java124
-rw-r--r--src/main/resources/assets/cowmoonication/lang/en_us.lang1
-rw-r--r--src/main/resources/mcmod.info13
10 files changed, 609 insertions, 0 deletions
diff --git a/src/main/java/eu/olli/cowmoonication/Cowmoonication.java b/src/main/java/eu/olli/cowmoonication/Cowmoonication.java
new file mode 100644
index 0000000..d07876c
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/Cowmoonication.java
@@ -0,0 +1,63 @@
+package eu.olli.cowmoonication;
+
+import eu.olli.cowmoonication.command.MooCommand;
+import eu.olli.cowmoonication.config.MooConfig;
+import eu.olli.cowmoonication.listener.ChatListener;
+import net.minecraftforge.client.ClientCommandHandler;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.Mod.EventHandler;
+import net.minecraftforge.fml.common.event.FMLInitializationEvent;
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+
+@Mod(modid = Cowmoonication.MODID, version = Cowmoonication.VERSION, clientSideOnly = true, guiFactory = "eu.olli." + Cowmoonication.MODID + ".config.MooGuiFactory")
+public class Cowmoonication {
+ public static final String MODID = "cowmoonication";
+ public static final String VERSION = "1.0";
+ private MooConfig config;
+ private Friends friends;
+ private Utils utils;
+ private Logger logger;
+
+ @Mod.EventHandler
+ public void preInit(FMLPreInitializationEvent e) {
+ logger = e.getModLog();
+
+ File modDir = new File(e.getModConfigurationDirectory(), MODID + File.separatorChar);
+ if (!modDir.exists()) {
+ modDir.mkdirs();
+ }
+
+ friends = new Friends(this);
+ config = new MooConfig(new Configuration(new File(modDir, MODID + ".cfg")), this);
+ }
+
+ @EventHandler
+ public void init(FMLInitializationEvent e) {
+ utils = new Utils(this);
+
+ MinecraftForge.EVENT_BUS.register(new ChatListener(this));
+ ClientCommandHandler.instance.registerCommand(new MooCommand(this));
+ }
+
+ public MooConfig getConfig() {
+ return config;
+ }
+
+ public Friends getFriends() {
+ return friends;
+ }
+
+ public Utils getUtils() {
+ return utils;
+ }
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+}
diff --git a/src/main/java/eu/olli/cowmoonication/Friends.java b/src/main/java/eu/olli/cowmoonication/Friends.java
new file mode 100644
index 0000000..0cbe517
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/Friends.java
@@ -0,0 +1,55 @@
+package eu.olli.cowmoonication;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class Friends {
+ private final Cowmoonication main;
+ private Set<String> bestFriends = new HashSet<>();
+
+ public Friends(Cowmoonication main) {
+ this.main = main;
+ }
+
+ public boolean addBestFriend(String name, boolean save) {
+ if (name.isEmpty()) {
+ return false;
+ }
+ boolean added = bestFriends.add(name);
+ if (added && save) {
+ saveBestFriends();
+ }
+ return added;
+ }
+
+ public boolean addBestFriend(String name) {
+ return addBestFriend(name, false);
+ }
+
+ public boolean removeBestFriend(String name) {
+ boolean removed = bestFriends.remove(name);
+ if (removed) {
+ saveBestFriends();
+ }
+ return removed;
+ }
+
+ public boolean isBestFriend(String playerName) {
+ return bestFriends.contains(playerName);
+ }
+
+ private void saveBestFriends() {
+
+ }
+
+ public Set<String> getBestFriends() {
+ return new TreeSet<>(bestFriends);
+ }
+
+ public void syncFriends(String[] bestFriends) {
+ this.bestFriends = new HashSet<>();
+ Collections.addAll(this.bestFriends, bestFriends);
+ }
+}
diff --git a/src/main/java/eu/olli/cowmoonication/Utils.java b/src/main/java/eu/olli/cowmoonication/Utils.java
new file mode 100644
index 0000000..ac3167c
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/Utils.java
@@ -0,0 +1,57 @@
+package eu.olli.cowmoonication;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.IChatComponent;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import net.minecraftforge.common.MinecraftForge;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Utils {
+ public static final Pattern VALID_USERNAME = Pattern.compile("^[\\w]{1,16}$");
+ private static final Pattern USELESS_JSON_CONTENT_PATTERN = Pattern.compile("\"[A-Za-z]+\":false,?");
+ private final Cowmoonication main;
+ private String[] aboveChatMessage;
+ private long aboveChatMessageExpiration;
+
+ public Utils(Cowmoonication main) {
+ this.main = main;
+ }
+
+ public void sendMessage(String text) {
+ sendMessage(new ChatComponentText(text));
+ }
+
+ public void sendMessage(IChatComponent chatComponent) {
+ ClientChatReceivedEvent event = new ClientChatReceivedEvent((byte) 1, chatComponent);
+ MinecraftForge.EVENT_BUS.post(event);
+ if (!event.isCanceled()) {
+ Minecraft.getMinecraft().thePlayer.addChatMessage(event.message);
+ }
+ }
+
+ public void sendAboveChatMessage(String... text) {
+ aboveChatMessage = text;
+ aboveChatMessageExpiration = Minecraft.getSystemTime() + 5000;
+ }
+
+ public String[] getAboveChatMessage() {
+ if (aboveChatMessageExpiration < Minecraft.getSystemTime()) {
+ // message expired
+ aboveChatMessage = null;
+ }
+ return aboveChatMessage;
+ }
+
+ public boolean isValidMcName(String username) {
+ return VALID_USERNAME.matcher(username).matches();
+ }
+
+ public String cleanChatComponent(IChatComponent chatComponent) {
+ String component = IChatComponent.Serializer.componentToJson(chatComponent);
+ Matcher jsonMatcher = USELESS_JSON_CONTENT_PATTERN.matcher(component);
+ return jsonMatcher.replaceAll("");
+ }
+}
diff --git a/src/main/java/eu/olli/cowmoonication/command/MooCommand.java b/src/main/java/eu/olli/cowmoonication/command/MooCommand.java
new file mode 100644
index 0000000..ca4722c
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/command/MooCommand.java
@@ -0,0 +1,115 @@
+package eu.olli.cowmoonication.command;
+
+import eu.olli.cowmoonication.Cowmoonication;
+import eu.olli.cowmoonication.config.MooConfig;
+import net.minecraft.client.Minecraft;
+import net.minecraft.command.CommandBase;
+import net.minecraft.command.CommandException;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.util.BlockPos;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.MathHelper;
+
+import java.util.List;
+import java.util.Set;
+
+public class MooCommand extends CommandBase {
+ private final Cowmoonication main;
+
+ public MooCommand(Cowmoonication main) {
+ this.main = main;
+ }
+
+ @Override
+ public void processCommand(ICommandSender sender, String[] args) throws CommandException {
+ if (args.length == 0) {
+ main.getUtils().sendMessage(new ChatComponentTranslation(getCommandUsage(sender)));
+ return;
+ }
+ if (args.length == 2 && args[0].equalsIgnoreCase("add")) {
+ main.getUtils().sendMessage(EnumChatFormatting.RED + "Edit the best friends list via ESC > Mod Options > Cowmoonication > Config > bestFriends.");
+ // TODO replace with a proper command
+ // handleBestFriendAdd(args[1]);
+ } else if (args.length == 2 && args[0].equalsIgnoreCase("remove") && main.getUtils().isValidMcName(args[1])) {
+ main.getUtils().sendMessage(EnumChatFormatting.RED + "Edit the best friends list via ESC > Mod Options > Cowmoonication > Config > bestFriends.");
+ // TODO replace with a proper command
+ // handleBestFriendRemove(args[1]);
+ } else if (args[0].equalsIgnoreCase("list")) {
+ handleListBestFriends();
+ } else if (args[0].equalsIgnoreCase("toggle")) {
+ main.getConfig().toggleNotifications();
+ main.getUtils().sendMessage(EnumChatFormatting.GREEN + "Switched all non-best friend login/logout notifications " + (MooConfig.filterFriendNotifications ? EnumChatFormatting.DARK_GREEN + "off" : EnumChatFormatting.DARK_RED + "on"));
+ } else if (args[0].equalsIgnoreCase("guiscale")) {
+ int currentGuiScale = (Minecraft.getMinecraft()).gameSettings.guiScale;
+ if (args.length == 1) {
+ main.getUtils().sendMessage(EnumChatFormatting.GREEN + "Current GUI scale: " + EnumChatFormatting.DARK_GREEN + currentGuiScale);
+ } else {
+ int scale = Math.min(10, MathHelper.parseIntWithDefault(args[1], 6));
+ Minecraft.getMinecraft().gameSettings.guiScale = scale;
+ main.getUtils().sendMessage(EnumChatFormatting.GREEN + "New GUI scale: " + EnumChatFormatting.DARK_GREEN + scale + EnumChatFormatting.GREEN + " (previous: " + EnumChatFormatting.DARK_GREEN + currentGuiScale + EnumChatFormatting.GREEN + ")");
+ }
+ } else {
+ main.getUtils().sendMessage(new ChatComponentTranslation(getCommandUsage(sender)));
+ }
+ }
+
+ private void handleBestFriendAdd(String username) {
+ if (!main.getUtils().isValidMcName(username)) {
+ main.getUtils().sendMessage(EnumChatFormatting.DARK_RED + username + EnumChatFormatting.RED + "? This... doesn't look like a valid username.");
+ return;
+ }
+
+ // TODO Add check if 'best friend' is on normal friend list
+ boolean added = main.getFriends().addBestFriend(username, true);
+ if (added) {
+ main.getUtils().sendMessage(EnumChatFormatting.GREEN + "Added " + EnumChatFormatting.DARK_GREEN + username + EnumChatFormatting.GREEN + " as best friend.");
+ } else {
+ main.getUtils().sendMessage(EnumChatFormatting.DARK_RED + username + EnumChatFormatting.RED + " is a best friend already.");
+ }
+ }
+
+ private void handleBestFriendRemove(String username) {
+ if (!main.getUtils().isValidMcName(username)) {
+ main.getUtils().sendMessage(EnumChatFormatting.DARK_RED + username + EnumChatFormatting.RED + "? This... doesn't look like a valid username.");
+ return;
+ }
+
+ boolean removed = main.getFriends().removeBestFriend(username);
+ if (removed) {
+ main.getUtils().sendMessage(EnumChatFormatting.GREEN + "Removed " + EnumChatFormatting.DARK_GREEN + username + EnumChatFormatting.GREEN + " from best friends list.");
+ } else {
+ main.getUtils().sendMessage(EnumChatFormatting.DARK_RED + username + EnumChatFormatting.RED + " isn't a best friend.");
+ }
+ }
+
+ private void handleListBestFriends() {
+ Set<String> bestFriends = main.getFriends().getBestFriends();
+
+ // TODO show fancy gui with list of best friends (maybe just the mod's settings?)
+ main.getUtils().sendMessage(EnumChatFormatting.GREEN + "Best friends: " + String.join(", ", bestFriends));
+ }
+
+ @Override
+ public String getCommandName() {
+ return "moo";
+ }
+
+ @Override
+ public String getCommandUsage(ICommandSender sender) {
+ return Cowmoonication.MODID + ":command.moo.usage";
+ }
+
+ @Override
+ public int getRequiredPermissionLevel() {
+ return 0;
+ }
+
+ @Override
+ public List<String> addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) {
+ if (args.length == 1) {
+ return getListOfStringsMatchingLastWord(args, "add", "remove", "list", "toggle", "guiscale");
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/eu/olli/cowmoonication/config/MooConfig.java b/src/main/java/eu/olli/cowmoonication/config/MooConfig.java
new file mode 100644
index 0000000..6165e54
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/config/MooConfig.java
@@ -0,0 +1,113 @@
+package eu.olli.cowmoonication.config;
+
+import eu.olli.cowmoonication.Cowmoonication;
+import eu.olli.cowmoonication.Utils;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.common.config.Property;
+import net.minecraftforge.fml.client.event.ConfigChangedEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MooConfig {
+ public static boolean filterFriendNotifications;
+ private static String[] bestFriends;
+ private static Configuration cfg = null;
+ private final Cowmoonication main;
+
+ public MooConfig(Configuration configuration, Cowmoonication main) {
+ this.main = main;
+ cfg = configuration;
+ initConfig();
+ }
+
+ public static Configuration getConfig() {
+ return cfg;
+ }
+
+ private void initConfig() {
+ syncFromFile();
+ main.getFriends().syncFriends(bestFriends);
+ MinecraftForge.EVENT_BUS.register(new ConfigEventHandler());
+ }
+
+ /**
+ * Load the configuration values from the configuration file
+ */
+ private void syncFromFile() {
+ syncConfig(true, true);
+ }
+
+ /**
+ * Save the GUI-altered values to disk
+ */
+ private void syncFromGUI() {
+ syncConfig(false, true);
+ }
+
+ /**
+ * Save the Configuration variables (fields) to disk
+ */
+ private void syncFromFields() {
+ syncConfig(false, false);
+ }
+
+ /**
+ * Synchronise the three copies of the data
+ * 1) loadConfigFromFile && readFieldsFromConfig -> initialise everything from the disk file
+ * 2) !loadConfigFromFile && readFieldsFromConfig -> copy everything from the config file (altered by GUI)
+ * 3) !loadConfigFromFile && !readFieldsFromConfig -> copy everything from the native fields
+ *
+ * @param loadConfigFromFile if true, load the config field from the configuration file on disk
+ * @param readFieldsFromConfig if true, reload the member variables from the config field
+ */
+ private void syncConfig(boolean loadConfigFromFile, boolean readFieldsFromConfig) {
+ if (loadConfigFromFile) {
+ cfg.load();
+ }
+
+ final boolean FILTER_FRIEND_NOTIFICATIONS = true;
+ Property propFilterFriendNotify = cfg.get(Configuration.CATEGORY_CLIENT, "filterFriendNotifications", FILTER_FRIEND_NOTIFICATIONS, "Set to false to receive all login/logout messages, set to true to only get notifications of 'best friends' joining/leaving");
+
+ final String[] BEST_FRIENDS_DEFAULT_VALUE = new String[]{"Cow"};
+ Property propBestFriends = cfg.get(Configuration.CATEGORY_CLIENT, "bestFriends", BEST_FRIENDS_DEFAULT_VALUE, "List of best friends: receive login/logout notifications from them");
+ propBestFriends.setValidationPattern(Utils.VALID_USERNAME);
+
+ List<String> propOrderGeneral = new ArrayList<>();
+ propOrderGeneral.add(propFilterFriendNotify.getName());
+ propOrderGeneral.add(propBestFriends.getName());
+ cfg.setCategoryPropertyOrder(Configuration.CATEGORY_CLIENT, propOrderGeneral);
+
+ if (readFieldsFromConfig) {
+ filterFriendNotifications = propFilterFriendNotify.getBoolean(FILTER_FRIEND_NOTIFICATIONS);
+ bestFriends = propBestFriends.getStringList();
+ }
+
+ propFilterFriendNotify.set(filterFriendNotifications);
+ propBestFriends.set(bestFriends);
+
+ if (cfg.hasChanged()) {
+ cfg.save();
+ }
+ if (propBestFriends.hasChanged()) {
+ main.getFriends().syncFriends(bestFriends);
+ }
+ }
+
+ public void toggleNotifications() {
+ filterFriendNotifications = !filterFriendNotifications;
+ syncFromFields();
+ }
+
+ public class ConfigEventHandler {
+ @SubscribeEvent(priority = EventPriority.NORMAL)
+ public void onEvent(ConfigChangedEvent.OnConfigChangedEvent e) {
+ if (Cowmoonication.MODID.equals(e.modID)) {
+ syncFromGUI();
+ }
+ }
+ }
+}
diff --git a/src/main/java/eu/olli/cowmoonication/config/MooGuiConfig.java b/src/main/java/eu/olli/cowmoonication/config/MooGuiConfig.java
new file mode 100644
index 0000000..e7b7862
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/config/MooGuiConfig.java
@@ -0,0 +1,39 @@
+package eu.olli.cowmoonication.config;
+
+import eu.olli.cowmoonication.Cowmoonication;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraftforge.common.config.ConfigElement;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.fml.client.config.GuiConfig;
+
+public class MooGuiConfig extends GuiConfig {
+ public MooGuiConfig(GuiScreen parent) {
+ super(parent,
+ new ConfigElement(MooConfig.getConfig().getCategory(Configuration.CATEGORY_CLIENT)).getChildElements(),
+ Cowmoonication.MODID,
+ false,
+ false,
+ "Configuration for Cowmoonication");
+ titleLine2 = MooConfig.getConfig().getConfigFile().getAbsolutePath();
+ }
+
+ @Override
+ public void initGui() {
+ super.initGui();
+ // optional: add buttons and initialize fields
+ }
+
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ // optional: create animations, draw additional elements, etc.
+ }
+
+ @Override
+ protected void actionPerformed(GuiButton button) {
+ super.actionPerformed(button);
+ // optional: process any additional buttons added in initGui
+ }
+}
diff --git a/src/main/java/eu/olli/cowmoonication/config/MooGuiFactory.java b/src/main/java/eu/olli/cowmoonication/config/MooGuiFactory.java
new file mode 100644
index 0000000..dbdb139
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/config/MooGuiFactory.java
@@ -0,0 +1,29 @@
+package eu.olli.cowmoonication.config;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraftforge.fml.client.IModGuiFactory;
+
+import java.util.Set;
+
+public class MooGuiFactory implements IModGuiFactory {
+ @Override
+ public void initialize(Minecraft minecraftInstance) {
+
+ }
+
+ @Override
+ public Class<? extends GuiScreen> mainConfigGuiClass() {
+ return MooGuiConfig.class;
+ }
+
+ @Override
+ public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
+ return null;
+ }
+
+ @Override
+ public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
+ return null;
+ }
+}
diff --git a/src/main/java/eu/olli/cowmoonication/listener/ChatListener.java b/src/main/java/eu/olli/cowmoonication/listener/ChatListener.java
new file mode 100644
index 0000000..83b3890
--- /dev/null
+++ b/src/main/java/eu/olli/cowmoonication/listener/ChatListener.java
@@ -0,0 +1,124 @@
+package eu.olli.cowmoonication.listener;
+
+import eu.olli.cowmoonication.Cowmoonication;
+import eu.olli.cowmoonication.config.MooConfig;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiChat;
+import net.minecraft.client.gui.GuiNewChat;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.IChatComponent;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import net.minecraftforge.client.event.GuiOpenEvent;
+import net.minecraftforge.client.event.GuiScreenEvent;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import org.apache.commons.lang3.CharUtils;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+import java.awt.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ChatListener {
+ private static final Pattern PRIVATE_MESSAGE_RECEIVED_PATTERN = Pattern.compile("^From (?:\\[.*?] )?(\\w+): ");
+ private final Cowmoonication main;
+ private String lastTypedChars = "";
+ private String lastPMSender;
+
+ public ChatListener(Cowmoonication main) {
+ this.main = main;
+ }
+
+ @SubscribeEvent
+ public void onLogInOutMessage(ClientChatReceivedEvent e) {
+ if (e.type != 2 && MooConfig.filterFriendNotifications) { // normal chat or system msg
+ String text = e.message.getUnformattedText();
+ if (text.endsWith(" joined.") || text.endsWith(" left.") // Hypixel
+ || text.endsWith(" joined the game") || text.endsWith(" left the game.")) { // Spigot
+ // TODO maybe check which server thePlayer is on and check for logout pattern accordingly
+ int nameEnd = text.indexOf(" joined");
+ if (nameEnd == -1) {
+ nameEnd = text.indexOf(" left");
+ }
+ boolean isBestFriend = main.getFriends().isBestFriend(text.substring(0, nameEnd));
+ if (!isBestFriend) {
+ e.setCanceled(true);
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void onClickOnChat(GuiScreenEvent.MouseInputEvent.Pre e) {
+ if (e.gui instanceof GuiChat) {
+ if (!Mouse.getEventButtonState() && Mouse.getEventButton() == 1 && Keyboard.isKeyDown(Keyboard.KEY_LMENU)) { // alt key pressed and right mouse button being released
+ IChatComponent chatComponent = Minecraft.getMinecraft().ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY());
+ if (chatComponent != null) {
+ String chatData = main.getUtils().cleanChatComponent(chatComponent);
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ clipboard.setContents(new StringSelection(chatData), null);
+ main.getUtils().sendAboveChatMessage(EnumChatFormatting.YELLOW + "Copied chat component to clipboard:", "" + EnumChatFormatting.BOLD + EnumChatFormatting.GOLD + "\u276E" + EnumChatFormatting.RESET + chatComponent.getUnformattedText() + EnumChatFormatting.BOLD + EnumChatFormatting.GOLD + "\u276F");
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void onReplyToMsg(GuiScreenEvent.KeyboardInputEvent.Pre e) {
+ // TODO Switch to more reliable way: GuiTextField#writeText on GuiChat#inputField (protected field) via reflections [using "Open Command"-key isn't detected currently]
+ if (lastPMSender != null && e.gui instanceof GuiChat && lastTypedChars.length() < 3 && Keyboard.getEventKeyState()) {
+ char eventCharacter = Keyboard.getEventCharacter();
+ if (!CharUtils.isAsciiControl(eventCharacter)) {
+ lastTypedChars += eventCharacter;
+ if (lastTypedChars.equalsIgnoreCase("/r ")) {
+ // replace /r with /msg <last user>
+ main.getUtils().sendAboveChatMessage("Sending message to " + lastPMSender + "!");
+ Minecraft.getMinecraft().displayGuiScreen(new GuiChat("/msg " + lastPMSender + " "));
+ }
+ } else if (Keyboard.getEventKey() == Keyboard.KEY_BACK) { // Backspace
+ lastTypedChars = lastTypedChars.substring(0, Math.max(lastTypedChars.length() - 1, 0));
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void onChatOpen(GuiOpenEvent e) {
+ if (e.gui instanceof GuiChat) {
+ lastTypedChars = "";
+ }
+ }
+
+ @SubscribeEvent
+ public void onPrivateMsgReceive(ClientChatReceivedEvent e) {
+ if (e.type != 2) {
+ Matcher matcher = PRIVATE_MESSAGE_RECEIVED_PATTERN.matcher(e.message.getUnformattedText());
+ if (matcher.find()) {
+ this.lastPMSender = matcher.group(1);
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public void onRenderChatGui(RenderGameOverlayEvent.Chat e) {
+ if (e.type == RenderGameOverlayEvent.ElementType.CHAT) {
+ // render message above chat box
+ String[] aboveChatMessage = main.getUtils().getAboveChatMessage();
+ if (aboveChatMessage != null) {
+ float chatHeightFocused = Minecraft.getMinecraft().gameSettings.chatHeightFocused;
+ float chatScale = Minecraft.getMinecraft().gameSettings.chatScale;
+ int chatBoxHeight = (int) (GuiNewChat.calculateChatboxHeight(chatHeightFocused) * chatScale);
+
+ int defaultTextY = e.resolution.getScaledHeight() - chatBoxHeight - 30;
+
+ for (int i = 0; i < aboveChatMessage.length; i++) {
+ String msg = aboveChatMessage[i];
+ int textY = defaultTextY - (aboveChatMessage.length - i) * (Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT + 1);
+ Minecraft.getMinecraft().fontRendererObj.drawStringWithShadow(msg, 2, textY, 0xffffff);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/resources/assets/cowmoonication/lang/en_us.lang b/src/main/resources/assets/cowmoonication/lang/en_us.lang
new file mode 100644
index 0000000..b7e77d4
--- /dev/null
+++ b/src/main/resources/assets/cowmoonication/lang/en_us.lang
@@ -0,0 +1 @@
+cowmoonication:command.moo.usage=/moo
diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info
new file mode 100644
index 0000000..e6c8f6e
--- /dev/null
+++ b/src/main/resources/mcmod.info
@@ -0,0 +1,13 @@
+[{
+ "modid": "cowmoonication",
+ "name": "Cowmoonication",
+ "description": "Adding various things related to communication.",
+ "version": "${version}",
+ "mcversion": "${mcversion}",
+ "url": "https://github.com/cow-mc/Cowmoonication",
+ "updateUrl": "",
+ "authorList": ["Cow"],
+ "logoFile": "",
+ "screenshots": [],
+ "dependencies": []
+}]