From 1f58cab966b15f12f4dd979f48e047c936c5841a Mon Sep 17 00:00:00 2001 From: Cow Date: Mon, 21 Dec 2020 17:39:35 +0100 Subject: Added server freshness-indicators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ≈ when server was last restarted - cmd: `/moo worldage` - notifications when switching worlds (toggleable via config) --- CHANGELOG.md | 4 ++ README.md | 1 + .../cowtipper/cowlection/command/MooCommand.java | 34 ++++++++++++++- .../de/cowtipper/cowlection/config/MooConfig.java | 16 +++++++ .../cowlection/listener/PlayerListener.java | 51 ++++++++++++++++++++++ .../resources/assets/cowlection/lang/en_US.lang | 4 ++ 6 files changed, 109 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b96f2f0..73c6627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Short alias `/m` for `/moo` command - Copy inventories to clipboard as JSON with CTRL + C - Added sound when a best friend comes online (deactivated by default) +- Check how long current world has been loaded + - ≈ when server was last restarted + - via command `/moo worldage` + - notification when joining a recently loaded or a very old server (toggleable via config) ### Changed - Item age: show timestamp in the local timezone instead of "SkyBlock"-timezone (Eastern Time; also fixed the incorrect 12h ↔ 24h clock conversion) diff --git a/README.md b/README.md index 1661b74..54ade8d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ It is a collection of different features mainly focused on Hypixel SkyBlock. | Analyze minions on a private island | `/moo analyzeIsland` | | Dungeon interfaces enhancements (normalize dungeon item stats, improved party finder) | Hold shift (configurable) while viewing a dungeon item tooltip | | Dungeon performance tracker and overlay: Skill score calculation, class milestone tracker, destroyed crypts tracker, and elapsed time indicator | automatically; or with `/moo dungeon` | +| Check how long current world has been loaded (≈ when the server was last restarted) | `/moo worldage` + `/moo config` → SkyBlock | ## Download You can download the compiled .jar files from the [release section](https://github.com/cow-mc/Cowlection/releases). diff --git a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java index d2f433d..51a14fe 100644 --- a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java +++ b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java @@ -16,6 +16,7 @@ import de.cowtipper.cowlection.search.GuiSearch; import de.cowtipper.cowlection.util.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.settings.GameSettings; import net.minecraft.command.*; import net.minecraft.entity.Entity; @@ -121,6 +122,36 @@ public class MooCommand extends CommandBase { } else if (args[0].equalsIgnoreCase("whatyearisit") || args[0].equalsIgnoreCase("year")) { long year = ((System.currentTimeMillis() - 1560275700000L) / (TimeUnit.HOURS.toMillis(124))) + 1; main.getChatHelper().sendMessage(EnumChatFormatting.YELLOW, "It is SkyBlock year " + EnumChatFormatting.GOLD + year + EnumChatFormatting.YELLOW + "."); + } else if (args[0].equalsIgnoreCase("worldage")) { + long worldTime = Minecraft.getMinecraft().theWorld.getWorldTime(); + new TickDelay(() -> { + WorldClient world = Minecraft.getMinecraft().theWorld; + if (world == null) { + return; + } + String msgPrefix; + long worldTime2 = world.getWorldTime(); + if (worldTime > worldTime2 || (worldTime2 - worldTime) < 15) { + // time is frozen + worldTime2 = world.getTotalWorldTime(); + msgPrefix = "World time seems to be frozen at around " + worldTime + " ticks. "; + if (worldTime2 > 24 * 60 * 60 * 20) { + // total world time >24h + main.getChatHelper().sendMessage(EnumChatFormatting.GOLD, msgPrefix + "However, how long this world is loaded cannot be determined."); + return; + } + msgPrefix += "However, this world is probably"; + } else { + msgPrefix = "This world is"; + } + long days = worldTime2 / 24000L + 1; + long minutes = days * 20; + long hours = minutes / 60; + minutes -= hours * 60; + + main.getChatHelper().sendMessage(EnumChatFormatting.YELLOW, msgPrefix + " loaded around " + EnumChatFormatting.GOLD + days + " ingame days " + + EnumChatFormatting.YELLOW + "(= less than " + EnumChatFormatting.GOLD + (hours > 0 ? hours + " hours " : "") + (minutes > 0 ? minutes + " mins " : "") + ")"); + }, 20); } //endregion //region sub-commands: update mod @@ -795,6 +826,7 @@ public class MooCommand extends CommandBase { .appendSibling(createCmdHelpEntry("dungeon party", "SkyBlock Dungeons: Shows armor and dungeon info about current party members " + EnumChatFormatting.GRAY + "(alias: " + EnumChatFormatting.WHITE + "/" + getCommandName() + " dp" + EnumChatFormatting.GRAY + ") §d§l⚷")) .appendSibling(createCmdHelpSection(3, "Miscellaneous")) .appendSibling(createCmdHelpEntry("search", "Open Minecraft log search")) + .appendSibling(createCmdHelpEntry("worldage", "Check how long the current world is loaded")) .appendSibling(createCmdHelpEntry("guiScale", "Change GUI scale")) .appendSibling(createCmdHelpEntry("rr", "Alias for /r without auto-replacement to /msg")) .appendSibling(createCmdHelpEntry("shrug", "¯\\_(ツ)_/¯")) @@ -828,7 +860,7 @@ public class MooCommand extends CommandBase { return getListOfStringsMatchingLastWord(args, /* Best friends, friends & other players */ "stalk", "add", "remove", "list", "online", "nameChangeCheck", /* SkyBlock */ "stalkskyblock", "skyblockstalk", "analyzeIsland", "dungeon", - /* miscellaneous */ "config", "search", "guiscale", "rr", "shrug", "apikey", + /* miscellaneous */ "config", "search", "worldage", "guiscale", "rr", "shrug", "apikey", /* update mod */ "update", "updateHelp", "version", "directory", /* help */ "help", /* rarely used aliases */ "askPolitelyWhereTheyAre", "askPolitelyAboutTheirSkyBlockProgress", "year", "whatyearisit"); diff --git a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java index 6922c2e..5266438 100644 --- a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java +++ b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java @@ -64,6 +64,8 @@ public class MooConfig { public static boolean doBestFriendsOnlineCheck; // Category: SkyBlock private static String enableSkyBlockOnlyFeatures; + public static int notifyFreshServer; + public static int notifyOldServer; public static int tooltipToggleKeyBinding; private static String tooltipAuctionHousePriceEach; private static String tooltipItemAge; @@ -295,6 +297,16 @@ public class MooConfig { Property propEnableSkyBlockOnlyFeatures = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "enableSkyBlockOnlyFeatures", "on SkyBlock", "Enable SkyBlock-only features?", new String[]{"on SkyBlock", "always", "never"})); + // Sub-Category: Server age notifications + subCat = configCat.addSubCategory("Server age notifications"); + subCat.addExplanations("Servers usually restart once they exceed " + EnumChatFormatting.YELLOW + "30-38 ingame days " + EnumChatFormatting.RESET + "(10-13 hours)", + "Use the command " + EnumChatFormatting.YELLOW + "/moo worldage " + EnumChatFormatting.RESET + "to check how long the current world is loaded.", + EnumChatFormatting.ITALIC + "Set a value to 0 to disable that notification."); + Property propNotifyFreshServer = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "notifyFreshServer", 1, "Notify when a world is loaded X ingame days", 0, 40)); + // Sub-Category: Tooltip enhancements subCat = configCat.addSubCategory("Tooltip enhancements"); @@ -454,6 +466,8 @@ public class MooConfig { doBestFriendsOnlineCheck = propDoBestFriendsOnlineCheck.getBoolean(); // Category: SkyBlock enableSkyBlockOnlyFeatures = propEnableSkyBlockOnlyFeatures.getString(); + notifyFreshServer = propNotifyFreshServer.getInt(); + notifyOldServer = propNotifyOldServer.getInt(); tooltipToggleKeyBinding = propTooltipToggleKeyBinding.getInt(); tooltipAuctionHousePriceEach = propTooltipAuctionHousePriceEach.getString(); tooltipItemAge = propTooltipItemAge.getString(); @@ -503,6 +517,8 @@ public class MooConfig { propDoBestFriendsOnlineCheck.set(doBestFriendsOnlineCheck); // Category: SkyBlock propEnableSkyBlockOnlyFeatures.set(enableSkyBlockOnlyFeatures); + propNotifyFreshServer.set(notifyFreshServer); + propNotifyOldServer.set(notifyOldServer); propTooltipToggleKeyBinding.set(tooltipToggleKeyBinding); propTooltipAuctionHousePriceEach.set(tooltipAuctionHousePriceEach); propTooltipItemAge.set(tooltipItemAge); diff --git a/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java b/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java index dcf3bbf..a6f3660 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java @@ -8,12 +8,14 @@ import de.cowtipper.cowlection.listener.skyblock.DungeonsListener; import de.cowtipper.cowlection.listener.skyblock.SkyBlockListener; import de.cowtipper.cowlection.util.AbortableRunnable; import de.cowtipper.cowlection.util.GsonUtils; +import de.cowtipper.cowlection.util.MooChatComponent; import de.cowtipper.cowlection.util.TickDelay; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiChat; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.gui.inventory.GuiInventory; +import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ContainerChest; @@ -116,6 +118,7 @@ public class PlayerListener { main.getLogger().info("Registering SkyBlock listeners"); isOnSkyBlock = true; registerSkyBlockListeners(); + checkWorldAge(); } else if (MooConfig.getEnableSkyBlockOnlyFeatures() == MooConfig.Setting.SPECIAL) { // only on SkyBlock stopScoreboardChecker(); @@ -147,10 +150,14 @@ public class PlayerListener { // player wasn't on SkyBlock before but now is on SkyBlock main.getLogger().info("Entered SkyBlock! Registering SkyBlock listeners"); registerSkyBlockListeners(); + checkWorldAge(); } else if (wasOnSkyBlock && !isOnSkyBlock) { // player was on SkyBlock before and is now in another gamemode unregisterSkyBlockListeners(); main.getLogger().info("Leaving SkyBlock! Un-registering SkyBlock listeners"); + } else if (wasOnSkyBlock /* && isOnSkyBlock */) { + // player is still on SkyBlock + checkWorldAge(); } stop(); } @@ -179,6 +186,50 @@ public class PlayerListener { } } + private void checkWorldAge() { + WorldClient theWorld = Minecraft.getMinecraft().theWorld; + if (MooConfig.notifyFreshServer == 0 && MooConfig.notifyOldServer == 0 || theWorld == null) { + return; + } + long worldTime = theWorld.getWorldTime(); + new TickDelay(() -> { + WorldClient world = Minecraft.getMinecraft().theWorld; + + if (world == null || theWorld != world || main.getDungeonCache().isInDungeon()) { + // no longer in a world, or not in the same world as before, or inside dungeons + return; + } + long worldTime2 = world.getWorldTime(); + + String infix = ""; + if (worldTime > worldTime2 || (worldTime2 - worldTime) < 30) { + // time is frozen + worldTime2 = world.getTotalWorldTime(); + if (worldTime2 > 24 * 60 * 60 * 20) { + // total world time >24h + return; + } + infix = "probably "; + } + + long days = worldTime2 / 24000L + 1; + if (MooConfig.notifyFreshServer > 0 && days <= MooConfig.notifyFreshServer) { + // fresh server + long minutes = days * 20; + long hours = minutes / 60; + minutes -= hours * 60; + main.getChatHelper().sendMessage(new MooChatComponent("⚠ ").darkGreen() + .appendSibling(new MooChatComponent("This world is " + infix + "loaded around " + EnumChatFormatting.DARK_GREEN + days + " ingame days.").green() + .setHover(new MooChatComponent("= less than " + EnumChatFormatting.DARK_GREEN + (hours > 0 ? hours + " hours " : "") + (minutes > 0 ? minutes + " mins " : "")).green()))); + } else if (MooConfig.notifyOldServer > 0 && days > MooConfig.notifyOldServer) { + // old server + main.getChatHelper().sendMessage(new MooChatComponent("⚠ ").red() + .appendSibling(new MooChatComponent("This server has not been restarted for " + EnumChatFormatting.RED + days + "+ ingame days!").gold() + .setHover(new MooChatComponent("Servers usually restart once they exceed 30-38 ingame days (10-13 hours)").yellow()))); + } + }, 40); + } + private void stopScoreboardChecker() { if (checkScoreboard != null) { // there is still a scoreboard-checker running, stop it diff --git a/src/main/resources/assets/cowlection/lang/en_US.lang b/src/main/resources/assets/cowlection/lang/en_US.lang index ba15568..c1c7be5 100644 --- a/src/main/resources/assets/cowlection/lang/en_US.lang +++ b/src/main/resources/assets/cowlection/lang/en_US.lang @@ -32,6 +32,10 @@ cowlection.config.doBestFriendsOnlineCheck=Do best friends online check §d§l cowlection.config.doBestFriendsOnlineCheck.tooltip=Set to true to check best friends' online status when joining a server, set to false to disable.\n§fDoes §dnot §fwork for staff members and players hiding their online status.\n§d§l⚷ §eRequires a valid API key! cowlection.config.enableSkyBlockOnlyFeatures=Enable SkyBlock-only features cowlection.config.enableSkyBlockOnlyFeatures.tooltip=§6When should SkyBlock-only features be active?\n§7§o(relog or change worlds after changing this option)\n §d➊ §fonly while being on SkyBlock §e(= disabled on other servers and in other gamemodes\n §d➋ §falways §e(= including other servers and gamemodes)\n §d➌ §fnever §e(§cNone §eof the SkyBlock-only features will work!) +cowlection.config.notifyFreshServer=Notify when world is loaded