diff options
Diffstat (limited to 'src/main/java/eu')
8 files changed, 274 insertions, 7 deletions
diff --git a/src/main/java/eu/olli/cowmoonication/Cowmoonication.java b/src/main/java/eu/olli/cowmoonication/Cowmoonication.java index 2b46e71..3f0ab44 100644 --- a/src/main/java/eu/olli/cowmoonication/Cowmoonication.java +++ b/src/main/java/eu/olli/cowmoonication/Cowmoonication.java @@ -25,7 +25,7 @@ import java.io.File; updateJSON = "https://raw.githubusercontent.com/cow-mc/Cowmoonication/master/update.json") public class Cowmoonication { public static final String MODID = "cowmoonication"; - public static final String VERSION = "1.8.9-0.2.0"; + public static final String VERSION = "1.8.9-0.3.0"; public static final String MODNAME = "Cowmoonication"; private File modsDir; private MooConfig config; diff --git a/src/main/java/eu/olli/cowmoonication/command/MooCommand.java b/src/main/java/eu/olli/cowmoonication/command/MooCommand.java index 22d510a..e933c30 100644 --- a/src/main/java/eu/olli/cowmoonication/command/MooCommand.java +++ b/src/main/java/eu/olli/cowmoonication/command/MooCommand.java @@ -3,6 +3,9 @@ package eu.olli.cowmoonication.command; import eu.olli.cowmoonication.Cowmoonication; import eu.olli.cowmoonication.config.MooConfig; import eu.olli.cowmoonication.config.MooGuiConfig; +import eu.olli.cowmoonication.friends.Friend; +import eu.olli.cowmoonication.util.ApiUtils; +import eu.olli.cowmoonication.util.HyStalking; import eu.olli.cowmoonication.util.TickDelay; import eu.olli.cowmoonication.util.Utils; import net.minecraft.client.Minecraft; @@ -33,7 +36,15 @@ public class MooCommand extends CommandBase { return; } // sub commands: friends - if (args.length == 2 && args[0].equalsIgnoreCase("add")) { + if (args[0].equalsIgnoreCase("stalk")) { + if (args.length != 2) { + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Usage: /" + getCommandName() + " stalk <playerName>"); + } else if (!Utils.isValidMcName(args[1])) { + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "\"" + args[1] + "\" is not a valid player name."); + } else { + handleStalking(args[1]); + } + } else if (args.length == 2 && args[0].equalsIgnoreCase("add")) { handleBestFriendAdd(args[1]); } else if (args.length == 2 && args[0].equalsIgnoreCase("remove")) { handleBestFriendRemove(args[1]); @@ -58,6 +69,8 @@ public class MooCommand extends CommandBase { Minecraft.getMinecraft().gameSettings.guiScale = scale; main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "\u2714 New GUI scale: " + EnumChatFormatting.DARK_GREEN + scale + EnumChatFormatting.GREEN + " (previous: " + EnumChatFormatting.DARK_GREEN + currentGuiScale + EnumChatFormatting.GREEN + ")"); } + } else if (args[0].equalsIgnoreCase("apikey")) { + handleApiKey(args); } // sub-commands: update mod else if (args[0].equalsIgnoreCase("update")) { @@ -101,6 +114,75 @@ public class MooCommand extends CommandBase { } } + private void handleApiKey(String[] args) { + if (args.length == 1) { + String firstSentence; + EnumChatFormatting color; + EnumChatFormatting colorSecondary; + if (Utils.isValidUuid(MooConfig.moo)) { + firstSentence = "You already set your Hypixel API key."; + color = EnumChatFormatting.GREEN; + colorSecondary = EnumChatFormatting.DARK_GREEN; + } else { + firstSentence = "You haven't set your Hypixel API key yet."; + color = EnumChatFormatting.RED; + colorSecondary = EnumChatFormatting.DARK_RED; + } + main.getChatHelper().sendMessage(color, firstSentence + " Use " + colorSecondary + "/api new" + color + " to request a new API key from Hypixel or use " + colorSecondary + "/" + this.getCommandName() + " apikey <key>" + color + " to manually set your existing API key."); + } else { + String key = args[1]; + if (Utils.isValidUuid(key)) { + MooConfig.moo = key; + main.getConfig().syncFromFields(); + main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Updated API key!"); + } else { + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "That doesn't look like a valid API key..."); + } + } + } + + private void handleStalking(String playerName) { + if (!Utils.isValidUuid(MooConfig.moo)) { + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "You haven't set your Hypixel API key yet. Use " + EnumChatFormatting.DARK_RED + "/api new" + EnumChatFormatting.RED + " to request a new API key from Hypixel or use " + EnumChatFormatting.DARK_RED + "/" + this.getCommandName() + " apikey <key>" + EnumChatFormatting.RED + " to manually set your existing API key."); + return; + } + main.getChatHelper().sendMessage(EnumChatFormatting.YELLOW, "Stalking " + EnumChatFormatting.GOLD + playerName + EnumChatFormatting.YELLOW + ". This may take a few seconds."); + boolean isBestFriend = main.getFriends().isBestFriend(playerName, true); + if (isBestFriend) { + Friend stalkedPlayer = main.getFriends().getBestFriend(playerName); + // we have the uuid already, so stalk the player + stalkPlayer(stalkedPlayer); + } else { + // fetch player uuid + ApiUtils.fetchFriendData(playerName, stalkedPlayer -> { + if (stalkedPlayer == null) { + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Sorry, could contact Mojang's API and thus couldn't stalk " + EnumChatFormatting.DARK_RED + playerName); + } else if (stalkedPlayer.equals(Friend.FRIEND_NOT_FOUND)) { + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "There is no player with the name " + EnumChatFormatting.DARK_RED + playerName + EnumChatFormatting.RED + "."); + } else { + // ... then stalk the player + stalkPlayer(stalkedPlayer); + } + }); + } + } + + private void stalkPlayer(Friend stalkedPlayer) { + ApiUtils.fetchPlayerStatus(stalkedPlayer, hyStalking -> { + if (hyStalking != null && hyStalking.isSuccess()) { + HyStalking.HySession session = hyStalking.getSession(); + if (session.isOnline()) { + main.getChatHelper().sendMessage(EnumChatFormatting.YELLOW, EnumChatFormatting.GOLD + stalkedPlayer.getName() + EnumChatFormatting.YELLOW + " is currently playing " + session.getGameType() + ": " + session.getMode() + (session.getMap() != null ? " (Map: " + session.getMap() + ")" : "")); + } else { + main.getChatHelper().sendMessage(EnumChatFormatting.YELLOW, EnumChatFormatting.GOLD + stalkedPlayer.getName() + EnumChatFormatting.YELLOW + " is currently " + EnumChatFormatting.RED + "offline" + EnumChatFormatting.YELLOW + " (or deactivated API access)."); + } + } else { + String cause = (hyStalking != null) ? hyStalking.getCause() : null; + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Something went wrong contacting the Hypixel API. Couldn't stalk " + EnumChatFormatting.DARK_RED + stalkedPlayer.getName() + EnumChatFormatting.RED + (cause != null ? " (Reason: " + EnumChatFormatting.DARK_RED + cause + EnumChatFormatting.RED + ")" : "") + "."); + } + }); + } + private void handleBestFriendAdd(String username) { if (!Utils.isValidMcName(username)) { main.getChatHelper().sendMessage(EnumChatFormatting.RED, EnumChatFormatting.DARK_RED + username + EnumChatFormatting.RED + "? This... doesn't look like a valid username."); @@ -147,6 +229,7 @@ public class MooCommand extends CommandBase { public String getCommandUsage(ICommandSender sender) { IChatComponent usage = new ChatComponentText("\u279C Cowmoonication commands:").setChatStyle(new ChatStyle().setColor(EnumChatFormatting.GOLD).setBold(true)) .appendSibling(createCmdHelpSection(1, "Friends")) + .appendSibling(createCmdHelpEntry("stalk", "Get info of player's status")) .appendSibling(createCmdHelpEntry("add", "Add best friends")) .appendSibling(createCmdHelpEntry("remove", "Remove best friends")) .appendSibling(createCmdHelpEntry("list", "View list of best friends")) @@ -187,8 +270,8 @@ public class MooCommand extends CommandBase { public List<String> addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) { if (args.length == 1) { return getListOfStringsMatchingLastWord(args, - /* friends */ "add", "remove", "list", "nameChangeCheck", "toggle", - /* miscellaneous */ "guiscale", "config", + /* friends */ "stalk", "add", "remove", "list", "nameChangeCheck", "toggle", + /* miscellaneous */ "config", "guiscale", "apikey", /* update mod */ "update", "updateHelp", "version", "folder", /* help */ "help"); } diff --git a/src/main/java/eu/olli/cowmoonication/config/MooConfig.java b/src/main/java/eu/olli/cowmoonication/config/MooConfig.java index d13758c..73e8bbc 100644 --- a/src/main/java/eu/olli/cowmoonication/config/MooConfig.java +++ b/src/main/java/eu/olli/cowmoonication/config/MooConfig.java @@ -1,6 +1,7 @@ package eu.olli.cowmoonication.config; import eu.olli.cowmoonication.Cowmoonication; +import eu.olli.cowmoonication.util.Utils; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; @@ -14,6 +15,7 @@ import java.util.List; public class MooConfig { public static boolean doUpdateCheck; public static boolean filterFriendNotifications; + public static String moo; private static Configuration cfg = null; public MooConfig(Configuration configuration) { @@ -47,7 +49,7 @@ public class MooConfig { /** * Save the Configuration variables (fields) to disk */ - private void syncFromFields() { + public void syncFromFields() { syncConfig(false, false); } @@ -71,18 +73,25 @@ public class MooConfig { 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 MOO = ""; + Property propMoo = cfg.get(Configuration.CATEGORY_CLIENT, "moo", MOO, "The answer to life the universe and everything. Don't edit this entry manually!", Utils.VALID_UUID_PATTERN); + propMoo.setShowInGui(false); + List<String> propOrderGeneral = new ArrayList<>(); propOrderGeneral.add(propDoUpdateCheck.getName()); propOrderGeneral.add(propFilterFriendNotify.getName()); + propOrderGeneral.add(propMoo.getName()); cfg.setCategoryPropertyOrder(Configuration.CATEGORY_CLIENT, propOrderGeneral); if (readFieldsFromConfig) { doUpdateCheck = propDoUpdateCheck.getBoolean(DO_UPDATE_CHECK); filterFriendNotifications = propFilterFriendNotify.getBoolean(FILTER_FRIEND_NOTIFICATIONS); + moo = propMoo.getString(); } propDoUpdateCheck.set(doUpdateCheck); propFilterFriendNotify.set(filterFriendNotifications); + propMoo.set(moo); if (cfg.hasChanged()) { cfg.save(); diff --git a/src/main/java/eu/olli/cowmoonication/friends/Friends.java b/src/main/java/eu/olli/cowmoonication/friends/Friends.java index e37b4a4..8c29d16 100644 --- a/src/main/java/eu/olli/cowmoonication/friends/Friends.java +++ b/src/main/java/eu/olli/cowmoonication/friends/Friends.java @@ -80,6 +80,10 @@ public class Friends { return bestFriends.stream().map(Friend::getName).collect(Collectors.toCollection(TreeSet::new)); } + public Friend getBestFriend(String name) { + return bestFriends.stream().filter(friend -> friend.getName().equalsIgnoreCase(name)).findFirst().orElse(Friend.FRIEND_NOT_FOUND); + } + private Friend getBestFriend(UUID uuid) { return bestFriends.stream().filter(friend -> friend.getUuid().equals(uuid)).findFirst().orElse(Friend.FRIEND_NOT_FOUND); } diff --git a/src/main/java/eu/olli/cowmoonication/listener/ChatListener.java b/src/main/java/eu/olli/cowmoonication/listener/ChatListener.java index 42a45e0..78a8f29 100644 --- a/src/main/java/eu/olli/cowmoonication/listener/ChatListener.java +++ b/src/main/java/eu/olli/cowmoonication/listener/ChatListener.java @@ -2,6 +2,7 @@ package eu.olli.cowmoonication.listener; import eu.olli.cowmoonication.Cowmoonication; import eu.olli.cowmoonication.config.MooConfig; +import eu.olli.cowmoonication.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiChat; import net.minecraft.client.gui.GuiNewChat; @@ -34,9 +35,9 @@ public class ChatListener { @SubscribeEvent public void onLogInOutMessage(ClientChatReceivedEvent e) { - if (e.type != 2 && MooConfig.filterFriendNotifications) { // normal chat or system msg + if (e.type != 2) { // normal chat or system msg String text = e.message.getUnformattedText(); - if (text.length() < 42 && // to prevent the party disbanded message from being filtered: "The party was disbanded because all invites have expired and all members have left." + if (MooConfig.filterFriendNotifications && text.length() < 42 && // to prevent the party disbanded message from being filtered: "The party was disbanded because all invites have expired and all members have left." (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 @@ -48,6 +49,14 @@ public class ChatListener { if (!isBestFriend) { e.setCanceled(true); } + } else if (text.length() == 56 && text.startsWith("Your new API key is ")) { + // Your new API key is 00000000-0000-0000-0000-000000000000 + String moo = text.substring(20, 56); + if (Utils.isValidUuid(moo)) { + MooConfig.moo = moo; + main.getConfig().syncFromFields(); + main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Added updated API key in Cowmoonication config!"); + } } } } diff --git a/src/main/java/eu/olli/cowmoonication/util/ApiUtils.java b/src/main/java/eu/olli/cowmoonication/util/ApiUtils.java index 5ff7d5d..d408721 100644 --- a/src/main/java/eu/olli/cowmoonication/util/ApiUtils.java +++ b/src/main/java/eu/olli/cowmoonication/util/ApiUtils.java @@ -5,10 +5,13 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonParser; import com.mojang.util.UUIDTypeAdapter; +import eu.olli.cowmoonication.Cowmoonication; +import eu.olli.cowmoonication.config.MooConfig; import eu.olli.cowmoonication.friends.Friend; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; @@ -21,6 +24,7 @@ public class ApiUtils { public static final String UUID_NOT_FOUND = "UUID-NOT-FOUND"; private static final String NAME_TO_UUID_URL = "https://api.mojang.com/users/profiles/minecraft/"; private static final String UUID_TO_NAME_URL = "https://api.mojang.com/user/profiles/%s/names"; + private static final String STALKING_URL = "https://api.hypixel.net/status?key=%s&uuid=%s"; private static ExecutorService pool = Executors.newCachedThreadPool(); private static Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).registerTypeAdapter(Friend.class, new Friend.FriendCreator()).create(); @@ -67,4 +71,34 @@ public class ApiUtils { } return null; } + + public static void fetchPlayerStatus(Friend friend, Consumer<HyStalking> action) { + pool.execute(() -> action.accept(stalkPlayer(friend))); + } + + private static HyStalking stalkPlayer(Friend friend) { + try { + HttpURLConnection connection = (HttpURLConnection) new URL(String.format(STALKING_URL, MooConfig.moo, UUIDTypeAdapter.fromUUID(friend.getUuid()))).openConnection(); + connection.setReadTimeout(5000); + connection.addRequestProperty("User-Agent", "Forge Mod " + Cowmoonication.MODNAME + "/" + Cowmoonication.VERSION + " (https://github.com/cow-mc/Cowmoonication/)"); + + connection.getResponseCode(); + if (connection.getResponseCode() == 204) { + return null; + } else { // various possible http status code: 200, 403, 422 + BufferedReader reader; + InputStream errorStream = connection.getErrorStream(); + if (errorStream != null) { + reader = new BufferedReader(new InputStreamReader(errorStream)); + } else { + reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + } + + return gson.fromJson(reader, HyStalking.class); + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } } diff --git a/src/main/java/eu/olli/cowmoonication/util/HyStalking.java b/src/main/java/eu/olli/cowmoonication/util/HyStalking.java new file mode 100644 index 0000000..a46e1b5 --- /dev/null +++ b/src/main/java/eu/olli/cowmoonication/util/HyStalking.java @@ -0,0 +1,123 @@ +package eu.olli.cowmoonication.util; + +import org.apache.commons.lang3.text.WordUtils; + +public class HyStalking { + private boolean success; + private String cause; + private HySession session; + + public HyStalking() { + } + + public boolean isSuccess() { + return success; + } + + public String getCause() { + return cause; + } + + public HySession getSession() { + return session; + } + + public static class HySession { + private boolean online; + private String gameType; + private String mode; + private String map; + + public HySession() { + } + + public boolean isOnline() { + return online; + } + + public String getGameType() { + String cleanGameType; + try { + cleanGameType = GameType.valueOf(gameType).getCleanName(); + } catch (IllegalArgumentException e) { + // no matching game type found + cleanGameType = WordUtils.capitalizeFully(gameType.replace('_', ' ')); + } + return cleanGameType; + } + + public String getMode() { + // list partially taken from https://api.hypixel.net/gameCounts?key=MOO + switch (mode) { + // SkyBlock related + case "dynamic": + return "Private Island"; + case "hub": + return "Hub"; + case "combat_1": + return "Spider's Den"; + case "combat_2": + return "Blazing Fortress"; + case "combat_3": + return "The End"; + case "farming_1": + return "The Barn"; + case "farming_2": + return "Mushroom Desert"; + case "foraging_1": + return "The Park"; + case "mining_1": + return "Gold Mine"; + case "mining_2": + return "Deep Caverns"; + default: + return WordUtils.capitalizeFully(mode.replace('_', ' ')); + } + } + + public String getMap() { + return map; + } + + // TODO replace with api request: https://github.com/HypixelDev/PublicAPI/blob/master/Documentation/misc/GameType.md + public enum GameType { + QUAKECRAFT("Quakecraft"), + WALLS("Walls"), + PAINTBALL("Paintball"), + SURVIVAL_GAMES("Blitz Survival Games"), + TNTGAMES("The TNT Games"), + VAMPIREZ("VampireZ"), + WALLS3("Mega Walls"), + ARCADE("Arcade"), + ARENA("Arena Brawl"), + UHC("UHC Champions"), + MCGO("Cops and Crims"), + BATTLEGROUND("Warlords"), + SUPER_SMASH("Smash Heroes"), + GINGERBREAD("Turbo Kart Racers"), + HOUSING("Housing"), + SKYWARS("SkyWars"), + TRUE_COMBAT("Crazy Walls"), + SPEED_UHC("Speed UHC"), + SKYCLASH("SkyClash"), + LEGACY("Classic Games"), + PROTOTYPE("Prototype"), + BEDWARS("Bed Wars"), + MURDER_MYSTERY("Murder Mystery"), + BUILD_BATTLE("Build Battle"), + DUELS("Duels"), + SKYBLOCK("SkyBlock"), + PIT("Pit"); + + private final String cleanName; + + GameType(String cleanName) { + this.cleanName = cleanName; + } + + public String getCleanName() { + return cleanName; + } + } + } +} diff --git a/src/main/java/eu/olli/cowmoonication/util/Utils.java b/src/main/java/eu/olli/cowmoonication/util/Utils.java index 0b890f0..ec96bf1 100644 --- a/src/main/java/eu/olli/cowmoonication/util/Utils.java +++ b/src/main/java/eu/olli/cowmoonication/util/Utils.java @@ -3,11 +3,16 @@ package eu.olli.cowmoonication.util; import java.util.regex.Pattern; public final class Utils { + public static final Pattern VALID_UUID_PATTERN = Pattern.compile("^(\\w{8})-(\\w{4})-(\\w{4})-(\\w{4})-(\\w{12})$"); private static final Pattern VALID_USERNAME = Pattern.compile("^[\\w]{1,16}$"); private Utils() { } + public static boolean isValidUuid(String uuid) { + return VALID_UUID_PATTERN.matcher(uuid).matches(); + } + public static boolean isValidMcName(String username) { return VALID_USERNAME.matcher(username).matches(); } |