From e86e6258a231af2d97a2bab5b3b54e040f31debd Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 20 Mar 2024 13:42:42 +0000 Subject: [PATCH] bob --- .gitignore | 38 ++++ .idea/.gitignore | 5 + .idea/copilot/chatSessions/00000000000.xd | Bin 0 -> 5933 bytes .idea/copilot/chatSessions/blobs/version | Bin 0 -> 4 bytes .idea/encodings.xml | 7 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/misc.xml | 19 ++ .idea/uiDesigner.xml | 124 ++++++++++++++ .idea/vcs.xml | 6 + pom.xml | 39 +++++ src/main/java/cc/fascinated/Aetheria.java | 26 +++ .../java/cc/fascinated/command/Command.java | 23 +++ .../cc/fascinated/command/CommandManager.java | 21 +++ .../command/impl/TotalJoinsCommand.java | 18 ++ .../cc/fascinated/utils/FormatterUtils.java | 13 ++ .../java/cc/fascinated/utils/MathUtils.java | 22 +++ .../java/cc/fascinated/utils/TimeUtils.java | 162 ++++++++++++++++++ .../worldsize/WorldSizeManager.java | 64 +++++++ .../worldsize/impl/WorldSizeCommand.java | 20 +++ src/main/resources/plugin.yml | 10 ++ 20 files changed, 623 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/copilot/chatSessions/00000000000.xd create mode 100644 .idea/copilot/chatSessions/blobs/version create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 pom.xml create mode 100644 src/main/java/cc/fascinated/Aetheria.java create mode 100644 src/main/java/cc/fascinated/command/Command.java create mode 100644 src/main/java/cc/fascinated/command/CommandManager.java create mode 100644 src/main/java/cc/fascinated/command/impl/TotalJoinsCommand.java create mode 100644 src/main/java/cc/fascinated/utils/FormatterUtils.java create mode 100644 src/main/java/cc/fascinated/utils/MathUtils.java create mode 100644 src/main/java/cc/fascinated/utils/TimeUtils.java create mode 100644 src/main/java/cc/fascinated/worldsize/WorldSizeManager.java create mode 100644 src/main/java/cc/fascinated/worldsize/impl/WorldSizeCommand.java create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..8f00030 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/.idea/copilot/chatSessions/00000000000.xd b/.idea/copilot/chatSessions/00000000000.xd new file mode 100644 index 0000000000000000000000000000000000000000..7122368a3097a77a65a1a699c223fc988b7d27b6 GIT binary patch literal 5933 zcmcgwdu&tJ89%;G{EF?o4BIFxS;MOv@!}*<7@)zRVKB%TEfhxA&arRE4UTR6prm7+ z&xw6($2U$wLf!-t^5#)k(b{U$v`yTk2w@CIv3-ej%u`xM`pb9lUh%M++^ zwAusK8u%030p91fyR3db5D?sF{A>`R5J;N5l7nt`d72vq7k`*{p9!@35f|#Dcw#gM z!@C1Qpu-yI@bZ3O5~g@|N)b@)0>cbQYI5#dFtl zU|yfc%lkkhN6;Vew5J$yp)QJFzwvlvhBp^VP<(BCw`Brv3A97|K zimVHZz6ces3^xT`E^wt&0B%)2zQyhccznDQbpUfBMlj*HQxu1>u_LT3h{KA4bP@41 zrQ+oHmmmoIo2uN|^1MFY4~|a>i;^(M{Ujx>C6Y(n14VJKM8rc;D&D!3Hf2Bf@!587 zpyQ}dXt(=14nuT2Z*O+-{>@KU*}Yzuz>Eb`6|L z{t^3G{*lam9=Bic2f*ef(i7oWO1k>4B)>VDM}8jrqvE0XQu#+=4`iDa<#$KAGV+V$ z9cYdzYf$7<+<}gmc({v-_wJ^5pi#Kc#GkPTg!6o(%VQ7hbvyTag5UwB)>1F@P%))P z(HrW!DNWkqc{F*bsEN8uJduDHA7%Tm5P0DDzGxCe7VqXxscl(8ztN>EwD_E&&M!8rTNnlk5OCl1qM!Sd7L>$r`#O2-q(m;~x za`zHrd;Gq@#3jK<*!P1=r{Dw~ zfk(T?DYQV20iIe)qY*dbreZVf>p4%*=im`XX;Oq6r;T?6Bynm64f=T>Dk5|wG0168 zYFo5HZA3evy{mm&`>ytv+7GoK0SF_IM59rxk~q_7H|DZ9(`XW_vpF+pk;IusMO^&I zVj4|gO=de7A8nTItgp-34;t;o`dqVVbPyX>+9DEV7UXR$qu{TsNb&V^#OfYKdN8Qztz)2JCRC-@qhWz+Y{|m8P;-!%Hf`&*xM?@ z2F_qm8&pO5e-`WiqS60etN-;-Zx2qW;12|1U5BKl#jYniqT5^vLb;Z)#kI0ZT_y;-+XEf~u zM24l&hjgT`2ac+%<&i588DVoJ6?C*KmUajtqtc+2j%5#yNrU_7c=q79G#R!2vTP zL#*BgRobcqCvShG3L-O1bEBHh_H^ecgvhKkvx&}iC$iK+WKJ62J2bzDXA3(^aPpuC zJU9(fBPaKu$T3)7~ZkOqkJBB3u zx^K#~$}Q8+_+Da3cN*&1+ z5T8q4k=cMOiy(p*W^(+Q13Yhc1$pMWTBHg=3>Jj70T$w~AZ#6AA?6Cg)&myeuOMs# zU`GnXZUrp-CQuOHZGass5c>>Z$MboL1^q-mPi4mi{9ds*tK=zLb;eVWkX50%d@5#v zw*z*jKbf$M|#Oed7nl-x~jH{K)vwRAe%mET*-l^`%ocNf`n7L9W_Ft|nZIX#-~5l#HKjXCou$2{ zKPdfUnWbz`nX7EL?5AbFD>s+#EI*@4{Vq`6Fa0ub;QIWwncPZdr&l_ zh;*q|Kwv_;=U_7-Z$GhN=5a<>+)^^S;@X}9qPX&BG#Mel43XsJ_@$hhk!^hC-!jGGaL;J`$14_l zC6-FM(21c~USPMqWe4*NR}M!aj5S*%qCGAV^S2~Yx^`aj+S6YIs(i+3v(6YxaSH^m z$G@n;oaKNV*On&B@$9&^yl8Q(9M}0=Y-#&XBHQ9CD7M!vER^1$i(!nKyT~HjmQ`Ps zZ2s(tMT+~n`3oLr&~H8Cum-)5lokozjNf1X@td~lrH|LRRV~udeRXm1?9J?>`=`HN zTOL8Htt^5vhZiuZ!@K&pba;2)ln-z7&BvL)x=qQmtIsJPUQ65m$o!6*i*2|64JXM; AGXMYp literal 0 HcmV?d00001 diff --git a/.idea/copilot/chatSessions/blobs/version b/.idea/copilot/chatSessions/blobs/version new file mode 100644 index 0000000000000000000000000000000000000000..720d64f4baafc33efdf971f02084aca5f25b34a5 GIT binary patch literal 4 LcmZQzU|<9Q00jU7 literal 0 HcmV?d00001 diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6a35cd4 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3394e6a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..31c840a --- /dev/null +++ b/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + cc.fascinated + aetheria + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + + + + io.papermc.paper + paper-api + 1.20.4-R0.1-SNAPSHOT + provided + + + org.projectlombok + lombok + 1.18.32 + provided + + + + \ No newline at end of file diff --git a/src/main/java/cc/fascinated/Aetheria.java b/src/main/java/cc/fascinated/Aetheria.java new file mode 100644 index 0000000..7176746 --- /dev/null +++ b/src/main/java/cc/fascinated/Aetheria.java @@ -0,0 +1,26 @@ +package cc.fascinated; + +import cc.fascinated.command.CommandManager; +import cc.fascinated.worldsize.WorldSizeManager; +import cc.fascinated.worldsize.impl.WorldSizeCommand; +import org.bukkit.plugin.java.JavaPlugin; + +public class Aetheria extends JavaPlugin { + + /** + * The instance of the plugin. + */ + public static Aetheria INSTANCE; + + public static final String PREFIX = "§6§lAetheria §7» §f"; + + public Aetheria() { + INSTANCE = this; + } + + @Override + public void onEnable() { + new CommandManager(); + new WorldSizeManager(); + } +} \ No newline at end of file diff --git a/src/main/java/cc/fascinated/command/Command.java b/src/main/java/cc/fascinated/command/Command.java new file mode 100644 index 0000000..b41d121 --- /dev/null +++ b/src/main/java/cc/fascinated/command/Command.java @@ -0,0 +1,23 @@ +package cc.fascinated.command; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +@RequiredArgsConstructor @Getter +public abstract class Command implements CommandExecutor { + + private final String command; + + private final String[] permissions; + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, org.bukkit.command.@NotNull Command command, @NotNull String s, @NotNull String[] strings) { + execute(commandSender, strings); + return true; + } + + public abstract void execute(CommandSender commandSender, String[] args); +} diff --git a/src/main/java/cc/fascinated/command/CommandManager.java b/src/main/java/cc/fascinated/command/CommandManager.java new file mode 100644 index 0000000..269863c --- /dev/null +++ b/src/main/java/cc/fascinated/command/CommandManager.java @@ -0,0 +1,21 @@ +package cc.fascinated.command; + +import cc.fascinated.Aetheria; +import cc.fascinated.command.impl.TotalJoinsCommand; +import org.bukkit.Bukkit; + +import java.util.Objects; + +public class CommandManager { + + public CommandManager() { + registerCommand(new TotalJoinsCommand()); + } + + public static void registerCommand(Command command) { + if (command == null) { + throw new IllegalArgumentException("Command cannot be null."); + } + Objects.requireNonNull(Aetheria.INSTANCE.getCommand(command.getCommand())).setExecutor(command); + } +} diff --git a/src/main/java/cc/fascinated/command/impl/TotalJoinsCommand.java b/src/main/java/cc/fascinated/command/impl/TotalJoinsCommand.java new file mode 100644 index 0000000..89453cc --- /dev/null +++ b/src/main/java/cc/fascinated/command/impl/TotalJoinsCommand.java @@ -0,0 +1,18 @@ +package cc.fascinated.command.impl; + +import cc.fascinated.Aetheria; +import cc.fascinated.command.Command; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +public class TotalJoinsCommand extends Command { + + public TotalJoinsCommand() { + super("totaljoins", new String[]{ "aetheria.command.totaljoins" }); + } + + @Override + public void execute(CommandSender commandSender, String[] args) { + commandSender.sendPlainMessage(Aetheria.PREFIX + "§fTotal joins: §e" + Bukkit.getOfflinePlayers().length + "§f."); + } +} diff --git a/src/main/java/cc/fascinated/utils/FormatterUtils.java b/src/main/java/cc/fascinated/utils/FormatterUtils.java new file mode 100644 index 0000000..4538642 --- /dev/null +++ b/src/main/java/cc/fascinated/utils/FormatterUtils.java @@ -0,0 +1,13 @@ +package cc.fascinated.utils; + +public class FormatterUtils { + + public static String formatBytes(long bytes) { + if (bytes < 1024) { + return bytes + " B"; + } + int exp = (int) (Math.log(bytes) / Math.log(1024)); + char pre = "KMGTPE".charAt(exp - 1); + return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre); + } +} diff --git a/src/main/java/cc/fascinated/utils/MathUtils.java b/src/main/java/cc/fascinated/utils/MathUtils.java new file mode 100644 index 0000000..965e789 --- /dev/null +++ b/src/main/java/cc/fascinated/utils/MathUtils.java @@ -0,0 +1,22 @@ +package cc.fascinated.utils; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; + +public class MathUtils { + + /** + * Format a number to a specific amount of decimal places. + * + * @param number the number to format + * @param additional the additional decimal places to format + * @return the formatted number + */ + public static double format(double number, int additional) { + return Double.parseDouble( + new DecimalFormat("#.#" + "#".repeat(Math.max(0, additional - 1)), + new DecimalFormatSymbols() + ).format(number) + ); + } +} diff --git a/src/main/java/cc/fascinated/utils/TimeUtils.java b/src/main/java/cc/fascinated/utils/TimeUtils.java new file mode 100644 index 0000000..0608fca --- /dev/null +++ b/src/main/java/cc/fascinated/utils/TimeUtils.java @@ -0,0 +1,162 @@ +package cc.fascinated.utils; + +import lombok.*; +import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.Nullable; + +import java.text.SimpleDateFormat; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@UtilityClass +public final class TimeUtils { + /** + * Format a time in millis to a readable time format. + * + * @param millis the millis to format + * @return the formatted time + */ + public static String format(long millis) { + return format(millis, WildTimeUnit.FIT); + } + + /** + * Format a time in millis to a readable time format. + * + * @param millis the millis to format + * @param timeUnit the time unit to format the millis to + * @return the formatted time + */ + public static String format(long millis, WildTimeUnit timeUnit) { + return format(millis, timeUnit, false); + } + + /** + * Format a time in millis to a readable time format. + * + * @param millis the millis to format + * @param timeUnit the time unit to format the millis to + * @param compact whether to use a compact display + * @return the formatted time + */ + public static String format(long millis, WildTimeUnit timeUnit, boolean compact) { + return format(millis, timeUnit, true, compact); + } + + /** + * Format a time in millis to a readable time format. + * + * @param millis the millis to format + * @param timeUnit the time unit to format the millis to + * @param decimals whether to include decimals + * @param compact whether to use a compact display + * @return the formatted time + */ + public static String format(long millis, WildTimeUnit timeUnit, boolean decimals, boolean compact) { + if (millis == -1L) { // Format permanent + return "Perm" + (compact ? "" : "anent"); + } + // Format the time to the best fitting time unit + if (timeUnit == WildTimeUnit.FIT) { + for (WildTimeUnit otherTimeUnit : WildTimeUnit.VALUES) { + if (otherTimeUnit != WildTimeUnit.FIT && millis >= otherTimeUnit.getMillis()) { + timeUnit = otherTimeUnit; + break; + } + } + } + double time = MathUtils.format((double) millis / timeUnit.getMillis(), 1); // Format the time + if (!decimals) { // Remove decimals + time = (int) time; + } + String formatted = time + (compact ? timeUnit.getSuffix() : " " + timeUnit.getDisplay()); // Append the time unit + if (time != 1.0 && !compact) { // Pluralize the time unit + formatted+= "s"; + } + return formatted; + } + + /** + * Convert the given input into a time in millis. + *

+ * E.g: 1d, 1h, 1d1h, etc + *

+ * + * @param input the input to parse + * @return the time in millis + */ + public static long fromString(String input) { + Matcher matcher = WildTimeUnit.SUFFIX_PATTERN.matcher(input); // Match the given input + long millis = 0; // The total millis + + // Match corresponding suffixes and add up the total millis + while (matcher.find()) { + int amount = Integer.parseInt(matcher.group(1)); // The amount of time to add + String suffix = matcher.group(2); // The unit suffix + WildTimeUnit timeUnit = WildTimeUnit.fromSuffix(suffix); // The time unit to add + if (timeUnit != null) { // Increment the total millis + millis+= amount * timeUnit.getMillis(); + } + } + return millis; + } + + /** + * Represents a unit of time. + */ + @NoArgsConstructor @AllArgsConstructor + @Getter(AccessLevel.PRIVATE) @ToString + public enum WildTimeUnit { + FIT, + YEARS("Year", "y", TimeUnit.DAYS.toMillis(365L)), + MONTHS("Month", "mo", TimeUnit.DAYS.toMillis(30L)), + WEEKS("Week", "w", TimeUnit.DAYS.toMillis(7L)), + DAYS("Day", "d", TimeUnit.DAYS.toMillis(1L)), + HOURS("Hour", "h", TimeUnit.HOURS.toMillis(1L)), + MINUTES("Minute", "m", TimeUnit.MINUTES.toMillis(1L)), + SECONDS("Second", "s", TimeUnit.SECONDS.toMillis(1L)), + MILLISECONDS("Millisecond", "ms", 1L); + + /** + * Our cached unit values. + */ + public static final WildTimeUnit[] VALUES = values(); + + /** + * Our cached suffix pattern. + */ + public static final Pattern SUFFIX_PATTERN = Pattern.compile("(\\d+)(mo|ms|[ywdhms])"); + + /** + * The display of this time unit. + */ + private String display; + + /** + * The suffix of this time unit. + */ + private String suffix; + + /** + * The amount of millis in this time unit. + */ + private long millis; + + /** + * Get the time unit with the given suffix. + * + * @param suffix the time unit suffix + * @return the time unit, null if not found + */ + @Nullable + public static WildTimeUnit fromSuffix(String suffix) { + for (WildTimeUnit unit : VALUES) { + if (unit != FIT && unit.getSuffix().equals(suffix)) { + return unit; + } + } + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/cc/fascinated/worldsize/WorldSizeManager.java b/src/main/java/cc/fascinated/worldsize/WorldSizeManager.java new file mode 100644 index 0000000..4b30627 --- /dev/null +++ b/src/main/java/cc/fascinated/worldsize/WorldSizeManager.java @@ -0,0 +1,64 @@ +package cc.fascinated.worldsize; + +import cc.fascinated.Aetheria; +import cc.fascinated.command.CommandManager; +import cc.fascinated.utils.FormatterUtils; +import cc.fascinated.worldsize.impl.WorldSizeCommand; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +public class WorldSizeManager { + + private static final int INTERVAL = 30; // 30 mins + + /** + * The total size of all worlds. + */ + @Getter + private static long totalWorldSize; + + /** + * The last time the total world size was updated. + */ + @Getter + private static long lastUpdated; + + public WorldSizeManager() { + CommandManager.registerCommand(new WorldSizeCommand()); + + totalWorldSize = calculateTotalWorldSize(); + Aetheria.INSTANCE.getServer().getAsyncScheduler().runAtFixedRate(Aetheria.INSTANCE, (task) -> { + totalWorldSize = calculateTotalWorldSize(); + }, INTERVAL, INTERVAL, TimeUnit.MINUTES); + } + + /** + * Get the total size of all worlds formatted. + * + * @return The total size of all worlds formatted. + */ + public static String getWorldSizeFormatted() { + return FormatterUtils.formatBytes(totalWorldSize); + } + + /** + * Calculate the total size of all worlds. + * + * @return The total size of all worlds. + */ + public long calculateTotalWorldSize() { + long size = 0; + for (World world : Bukkit.getWorlds()) { + File worldFolder = world.getWorldFolder(); + + size += FileUtils.sizeOfDirectory(worldFolder); + } + lastUpdated = System.currentTimeMillis(); + return size; + } +} \ No newline at end of file diff --git a/src/main/java/cc/fascinated/worldsize/impl/WorldSizeCommand.java b/src/main/java/cc/fascinated/worldsize/impl/WorldSizeCommand.java new file mode 100644 index 0000000..5ea8a93 --- /dev/null +++ b/src/main/java/cc/fascinated/worldsize/impl/WorldSizeCommand.java @@ -0,0 +1,20 @@ +package cc.fascinated.worldsize.impl; + +import cc.fascinated.Aetheria; +import cc.fascinated.command.Command; +import cc.fascinated.utils.TimeUtils; +import cc.fascinated.worldsize.WorldSizeManager; +import org.bukkit.command.CommandSender; + +public class WorldSizeCommand extends Command { + + public WorldSizeCommand() { + super("worldsize", new String[]{ "aetheria.command.worldsize" }); + } + + @Override + public void execute(CommandSender commandSender, String[] args) { + commandSender.sendPlainMessage(Aetheria.PREFIX + "§fTotal size of all worlds: §e" + WorldSizeManager.getWorldSizeFormatted() + "§f."); + commandSender.sendPlainMessage(Aetheria.PREFIX + "§fLast updated: §e" + TimeUtils.format(System.currentTimeMillis() - WorldSizeManager.getLastUpdated()) + " ago§f."); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..c3c94db --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,10 @@ +name: Aetheria +main: cc.fascinated.Aetheria +version: 1.0 +commands: + totaljoins: + description: "Shows the total amount of joins" + usage: "/totaljoins" + worldsize: + description: "Shows the size of all worlds" + usage: "/worldsize" \ No newline at end of file