diff --git a/README.md b/README.md index c3d3058..8cfef9c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ are commonly exploited on online servers. - **Get Support / Contact Us** - Please join the [ArcanePlugins Discord](https://discord.gg/arcaneplugins-752310043214479462) and use the `#other-lokka30-plugins` channel. + Please join the [ArcanePlugins Discord](https://discord.gg/arcaneplugins-752310043214479462) and use + the `#other-lokka30-plugins` channel. Alternatively, [please message lokka30](https://www.spigotmc.org/conversations/add?to=lokka30) on SpigotMC. @@ -28,7 +29,8 @@ are commonly exploited on online servers. - **Command Blocking** - Featuring a comprehensive command blocker, supporting blacklist/whitelisting, regex, powerful custom command rule chains, colon syntax blocking (`/plugin:command`), and more. + Featuring a comprehensive command blocker, supporting blacklist/whitelisting, regex, powerful custom command rule + chains, colon syntax blocking (`/plugin:command`), and more. The default configuration blocks common commands used by players to check installed plugins and versions. @@ -38,17 +40,21 @@ are commonly exploited on online servers. - **Unit Testing** - Standard component logic, such as the Command Blocking logic, is unit-tested to ensure it is working correctly before any version can be shipped out. + Standard component logic, such as the Command Blocking logic, is unit-tested to ensure it is working correctly before + any version can be shipped out. - **Simple & Reliable** - BlackWidow is built to be robust and lightweight, and doesn't mash half-baked features together to seem appealing (..only to break next update). + BlackWidow is built to be robust and lightweight, and doesn't mash half-baked features together to seem appealing ( + ..only to break next update). - **Plenty more to come!** - There are a [variety of other features](https://github.com/orgs/ArcanePlugins/projects/5) planned, some major, such as command spying. It has the goal of being a pretty comprehensive security suite. + There are a [variety of other features](https://github.com/orgs/ArcanePlugins/projects/5) planned, some major, such as + command spying. It has the goal of being a pretty comprehensive security suite. - However, we currently have no plans for BlackWidow to become an 'anticheat' plugin in the usual sense, which seems to be fulfilled by existing solutions well enough. + However, we currently have no plans for BlackWidow to become an 'anticheat' plugin in the usual sense, which seems to + be fulfilled by existing solutions well enough. Feel free to see a variety of screenshots below: @@ -83,7 +89,8 @@ Feel free to see a variety of screenshots below: Firstly, make sure your software setup is compatible with BlackWidow. -Please reference the [Requirements](https://github.com/ArcanePlugins/BlackWidow/wiki/Requirements) page for the most up-to-date and descriptive information on the requirements of running BlackWidow. +Please reference the [Requirements](https://github.com/ArcanePlugins/BlackWidow/wiki/Requirements) page for the most +up-to-date and descriptive information on the requirements of running BlackWidow. The best-case scenario to run BlackWidow is: @@ -95,11 +102,15 @@ Please be advised: > We are considering adding future support for Velocity, BungeeCord, and Minestom. Let us know if you're interested! -> Derivatives of Spigot/Paper, such as Purpur or Pufferfish may work fine, but we don't support these setups. That being said, still give it a shot and see if everything works. :) +> Derivatives of Spigot/Paper, such as Purpur or Pufferfish may work fine, but we don't support these setups. That being +> said, still give it a shot and see if everything works. :) -> Please note that we are not interested in backporting BlackWidow to older versions of Minecraft/Java/etc. Please update your software, or feel free to fork BlackWidow and backport it. +> Please note that we are not interested in backporting BlackWidow to older versions of Minecraft/Java/etc. Please +> update your software, or feel free to fork BlackWidow and backport it. -> **We do NOT recommend** using any server software like Magma, Mohist, and Arclight which *try* to make Forge mods work with Bukkit plugins. Bukkit was *never* designed to work with Forge/Fabric/etc mods. Trying to mix the two often causes lots of unusual issues which burden server owners and plugin maintainers. +> **We do NOT recommend** using any server software like Magma, Mohist, and Arclight which *try* to make Forge mods work +> with Bukkit plugins. Bukkit was *never* designed to work with Forge/Fabric/etc mods. Trying to mix the two often causes +> lots of unusual issues which burden server owners and plugin maintainers. ## Projects Used @@ -128,14 +139,14 @@ Thanks to all the other projects used, such as SpigotMC, IntelliJ, etc. Copyright (C) 2024 lokka30 ([email](mailto:lachy@lachy.space)) > This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or +> it under the terms of the GNU General Public License as published by +> the Free Software Foundation, either version 3 of the License, or (at your option) any later version. > > This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +> but WITHOUT ANY WARRANTY; without even the implied warranty of +> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +> GNU General Public License for more details. You should have [received a copy](LICENSE.md) of the GNU General Public License -along with this program. If not, see . +along with this program. If not, see . diff --git a/blackwidowlib/pom.xml b/blackwidowlib/pom.xml index 6c5eebf..e64b3d7 100644 --- a/blackwidowlib/pom.xml +++ b/blackwidowlib/pom.xml @@ -65,6 +65,13 @@ test + + org.jetbrains + annotations + 26.0.1 + provided + + diff --git a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Chain.java b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Chain.java index a90de37..9035c83 100644 --- a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Chain.java +++ b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Chain.java @@ -64,12 +64,12 @@ public class Chain { * @since 1.0.0 */ public Chain( - final String id, - final boolean enabled, - final Policy policy, - final Collection rules, - final boolean isRegex, - final Collection causeFilters + final String id, + final boolean enabled, + final Policy policy, + final Collection rules, + final boolean isRegex, + final Collection causeFilters ) { this.id = Objects.requireNonNull(id, "id"); this.enabled = enabled; @@ -86,12 +86,12 @@ public Chain( if (!isRegex()) { for (final String rule : rules()) { - if(rule.startsWith("/")) { + if (rule.startsWith("/")) { continue; } throw new IllegalArgumentException("Rule string='" + rule + "' in chain id='" + id() + "' does" + - "not start with a slash (/); for a non-regex chain, all rules must start with a slash"); + "not start with a slash (/); for a non-regex chain, all rules must start with a slash"); } } @@ -233,7 +233,7 @@ public final String id() { * @since 1.0.0 */ protected static String[] transformToArgs( - final String str + final String str ) { if (str.isEmpty()) { throw new IllegalArgumentException("str parameter must not be empty"); @@ -244,10 +244,10 @@ protected static String[] transformToArgs( } return str - .substring(1) // we don't want the starting slash. - .toLowerCase(Locale.ROOT) // let's use lowercase for case insensitivity. - .trim() // trim leading and trialing whitespace otherwise it can break String#split below. - .split("\\s+"); // finally, split by whitespace via regex. + .substring(1) // we don't want the starting slash. + .toLowerCase(Locale.ROOT) // let's use lowercase for case insensitivity. + .trim() // trim leading and trialing whitespace otherwise it can break String#split below. + .split("\\s+"); // finally, split by whitespace via regex. } /** @@ -262,11 +262,11 @@ protected static String[] transformToArgs( * @since 1.0.0 */ private MatchResult matchRulePattern( - final String command, - final String rule, - final Pattern pattern, - final EvalCause cause, - final Consumer> debugger + final String command, + final String rule, + final Pattern pattern, + final EvalCause cause, + final Consumer> debugger ) { Objects.requireNonNull(command, "command"); Objects.requireNonNull(rule, "rule"); @@ -298,9 +298,9 @@ private MatchResult matchRulePattern( } return new MatchResult( - pattern.matcher(command).find(), - rule, - "regex pattern match find result" + pattern.matcher(command).find(), + rule, + "regex pattern match find result" ); } @@ -315,12 +315,12 @@ private MatchResult matchRulePattern( * @since 1.0.0 */ private MatchResult matchRuleArgs( - final String cmd, - final String[] cmdArgs, - final String rule, - final String[] ruleArgs, - final EvalCause cause, - final Consumer> debugger + final String cmd, + final String[] cmdArgs, + final String rule, + final String[] ruleArgs, + final EvalCause cause, + final Consumer> debugger ) { Objects.requireNonNull(cmd, "cmd"); Objects.requireNonNull(cmdArgs, "cmdArgs"); @@ -339,10 +339,10 @@ private MatchResult matchRuleArgs( final int minLen = Math.min(cmdArgs.length, ruleArgs.length); debugger.accept(() -> "matchRule: Checking cmd='" + cmd + "', " + - "rule='" + rule + "', " + - "minLen='" + minLen + "', " + - "cmdArgs='" + Arrays.toString(cmdArgs) + "' (len='" + cmdArgs.length + "'), " + - "ruleArgs='" + Arrays.toString(ruleArgs) + "' (len='" + ruleArgs.length + "')."); + "rule='" + rule + "', " + + "minLen='" + minLen + "', " + + "cmdArgs='" + Arrays.toString(cmdArgs) + "' (len='" + cmdArgs.length + "'), " + + "ruleArgs='" + Arrays.toString(ruleArgs) + "' (len='" + ruleArgs.length + "')."); if (ruleArgs.length == 0 && cmdArgs.length == 0) { debugger.accept(() -> "matchRule: Yes, because rule args and cmd args are both empty arrays."); @@ -351,9 +351,9 @@ private MatchResult matchRuleArgs( if (ruleArgs.length > cmdArgs.length) { debugger.accept(() -> "matchRule: No, because it's impossible for this command to match this rule, " + - "because it doesn't have an equal or greater number args."); + "because it doesn't have an equal or greater number args."); return new MatchResult(false, rule, "rule has more args than cmd, thus " + - "impossible to match"); + "impossible to match"); } for (int i = 0; i < minLen; i++) { @@ -367,7 +367,7 @@ private MatchResult matchRuleArgs( if (!cmdArg.equals(ruleArg) && !ruleArg.equals("*")) { debugger.accept(() -> "matchRule: No, since args don't match and ruleArg is not a wildcard"); return new MatchResult(false, rule, "arg at index '" + i + "' didn't match, and " + - "ruleArg is not a wildcard"); + "ruleArg is not a wildcard"); } // if we're looking at the last arg of the rule, we've found a match. @@ -394,9 +394,9 @@ private MatchResult matchRuleArgs( * @since 1.0.0 */ public final MatchResult matches( - final String command, - final EvalCause cause, - final Consumer> debugger + final String command, + final EvalCause cause, + final Consumer> debugger ) { Objects.requireNonNull(command, "command"); Objects.requireNonNull(debugger, "debugger"); @@ -422,15 +422,15 @@ public final MatchResult matches( debugger.accept(() -> "matches: Checking rule '" + rule + "'."); final MatchResult res = isRegex() ? - matchRulePattern(command, rule, rulesRegexPatterns().get(rule), cause, debugger) : - matchRuleArgs(command, transformToArgs(command), rule, rulesAsArgs().get(rule), cause, debugger); + matchRulePattern(command, rule, rulesRegexPatterns().get(rule), cause, debugger) : + matchRuleArgs(command, transformToArgs(command), rule, rulesAsArgs().get(rule), cause, debugger); if (res.matched()) { debugger.accept(() -> "matches: Matched!"); return new MatchResult( - true, - res.rule(), - "in rule '" + rule + "'; " + res.description() + true, + res.rule(), + "in rule '" + rule + "'; " + res.description() ); } diff --git a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluation.java b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluation.java index 96f9929..d4d17cc 100644 --- a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluation.java +++ b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluation.java @@ -53,11 +53,11 @@ public final class Evaluation { * @since 1.0.0 */ public Evaluation( - final String command, - final Policy policy, - final Chain chain, - final String rule, - final String description + final String command, + final Policy policy, + final Chain chain, + final String rule, + final String description ) { this.command = Objects.requireNonNull(command, "command"); this.policy = Objects.requireNonNull(policy, "policy"); diff --git a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluator.java b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluator.java index feccac8..182da1e 100644 --- a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluator.java +++ b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/Evaluator.java @@ -29,13 +29,13 @@ public class Evaluator { public static final AtomicBoolean SUPPRESS_EXCEPTION_MESSAGES = new AtomicBoolean(false); public static Evaluation evaluate( - final String cmd, - final Collection chains, - final Policy defaultPolicy, - final boolean denyColonInFirstArg, - final EvalCause cause, - final Consumer> debugLogger, - final Consumer> warningLogger + final String cmd, + final Collection chains, + final Policy defaultPolicy, + final boolean denyColonInFirstArg, + final EvalCause cause, + final Consumer> debugLogger, + final Consumer> warningLogger ) { try { Objects.requireNonNull(cmd, "cmd"); @@ -57,11 +57,11 @@ public static Evaluation evaluate( final MatchResult res = chain.matches(cmd, cause, debugLogger); if (res.matched()) { return new Evaluation( - cmd, - chain.policy(), - chain, - res.rule(), - "in chain #" + i + " (id='" + chain.id() + "'; " + res.description() + cmd, + chain.policy(), + chain, + res.rule(), + "in chain #" + i + " (id='" + chain.id() + "'; " + res.description() ); } i++; @@ -69,33 +69,33 @@ public static Evaluation evaluate( if (denyColonInFirstArg && Chain.transformToArgs(cmd)[0].contains(":")) { return new Evaluation(cmd, Policy.DENY, null, null, "colon found in first arg") - .withDueToColonInFirstArg(true); + .withDueToColonInFirstArg(true); } } catch (Exception ex) { if (!SUPPRESS_EXCEPTION_MESSAGES.get()) { warningLogger.accept(() -> - "An exception was caught whilst evaluating cmd '" + cmd + "': '" + ex.getClass().getSimpleName() + - "'. For best security practice, this cmd will be forcefully denied due to the error. " + - "Message: '" + ex.getMessage() + "'; Stack trace:\n" + "An exception was caught whilst evaluating cmd '" + cmd + "': '" + ex.getClass().getSimpleName() + + "'. For best security practice, this cmd will be forcefully denied due to the error. " + + "Message: '" + ex.getMessage() + "'; Stack trace:\n" ); //noinspection CallToPrintStackTrace ex.printStackTrace(); } return new Evaluation( - cmd == null ? "/??? NULL COMMAND ???" : cmd, - Policy.DENY, - null, - null, - "exception caught, denying for security" + cmd == null ? "/??? NULL COMMAND ???" : cmd, + Policy.DENY, + null, + null, + "exception caught, denying for security" ).withDueToException(true); } return new Evaluation( - cmd, - defaultPolicy, - null, - null, - "default policy, no chains matched" + cmd, + defaultPolicy, + null, + null, + "default policy, no chains matched" ).withDueToDefaultPolicy(true); } } diff --git a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/MatchResult.java b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/MatchResult.java index 8061e25..ae9dc73 100644 --- a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/MatchResult.java +++ b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/MatchResult.java @@ -46,9 +46,9 @@ public final class MatchResult { * @since 1.0.0 */ public MatchResult( - final boolean matched, - final String rule, - final String description + final boolean matched, + final String rule, + final String description ) { this.matched = matched; this.rule = rule; diff --git a/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/UpdateChecker.java b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/UpdateChecker.java new file mode 100644 index 0000000..5d0b84c --- /dev/null +++ b/blackwidowlib/src/main/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/UpdateChecker.java @@ -0,0 +1,24 @@ +package io.github.arcaneplugins.blackwidow.lib.cmdblocking; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Scanner; +import java.util.function.Consumer; + +public class UpdateChecker { + public static void getLatestVersion(final String resourceName, final Consumer consumer) + throws IOException, URISyntaxException { + try (InputStream inputStream = new URI( + "https://hangar.papermc.io/api/v1/projects/" + + resourceName + "/latest?channel=Release") + .toURL().openStream()){ + + final Scanner scanner = new Scanner(inputStream); + if (scanner.hasNext()){ + consumer.accept(scanner.next()); + } + } + } +} diff --git a/blackwidowlib/src/test/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/TestCmdEvaluation.java b/blackwidowlib/src/test/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/TestCmdEvaluation.java index d9ecd99..4cbdf3a 100644 --- a/blackwidowlib/src/test/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/TestCmdEvaluation.java +++ b/blackwidowlib/src/test/java/io/github/arcaneplugins/blackwidow/lib/cmdblocking/TestCmdEvaluation.java @@ -34,66 +34,66 @@ public final class TestCmdEvaluation { // Various 'plugin checking' commands private static final String[] TEST_PLUGIN_CMDS = { - "/plugins", "/pl", "/version", "/ver", "/icanhasbukkit", - "/about", "/?", "/help", "/ehelp", "/paper", "/spigot" + "/plugins", "/pl", "/version", "/ver", "/icanhasbukkit", + "/about", "/?", "/help", "/ehelp", "/paper", "/spigot" }; // Various programatically defined chains of rules, some regex ones provided at the bottom. // Chains should NOT contain 'donotuseinchains', doing so will make some tests fail (by design). private static final Collection TEST_CHAINS = Arrays.asList( - new Chain("0", true, Policy.DENY, Arrays.asList("/cd reload *", "/"), false, EvalCause.setValues()), - new Chain("1", true, Policy.ALLOW, Collections.singletonList("/cd reload"), false, EvalCause.setValues()), - new Chain("2", true, Policy.DENY, Collections.singletonList("/cd"), false, EvalCause.setValues()), - new Chain("3", true, Policy.DENY, Arrays.asList(TEST_PLUGIN_CMDS), false, EvalCause.setValues()), - new Chain("4", true, Policy.ALLOW, Collections.singletonList("/es version"), false, EvalCause.setValues()), - new Chain("5", true, Policy.DENY, Arrays.asList("/es give", "/es enchant"), false, EvalCause.setValues()), - new Chain("6", true, Policy.ALLOW, Collections.singletonList("^(/heywhats(up)?(?:$|\\W)cool(beans)?(?:$|\\W).*)"), true, EvalCause.setValues()), - new Chain("7", true, Policy.DENY, Collections.singletonList("^(/heywhats(up)?(?:$|\\W).*)"), true, EvalCause.setValues()), - new Chain("8", false, Policy.DENY, Collections.singletonList("/thisshouldnotbedenied"), false, EvalCause.setValues()), - new Chain("9", true, Policy.DENY, Collections.singletonList("/blocksuggestionsonly"), false, EnumSet.of(EvalCause.CMD_SUGGESTION)) + new Chain("0", true, Policy.DENY, Arrays.asList("/cd reload *", "/"), false, EvalCause.setValues()), + new Chain("1", true, Policy.ALLOW, Collections.singletonList("/cd reload"), false, EvalCause.setValues()), + new Chain("2", true, Policy.DENY, Collections.singletonList("/cd"), false, EvalCause.setValues()), + new Chain("3", true, Policy.DENY, Arrays.asList(TEST_PLUGIN_CMDS), false, EvalCause.setValues()), + new Chain("4", true, Policy.ALLOW, Collections.singletonList("/es version"), false, EvalCause.setValues()), + new Chain("5", true, Policy.DENY, Arrays.asList("/es give", "/es enchant"), false, EvalCause.setValues()), + new Chain("6", true, Policy.ALLOW, Collections.singletonList("^(/heywhats(up)?(?:$|\\W)cool(beans)?(?:$|\\W).*)"), true, EvalCause.setValues()), + new Chain("7", true, Policy.DENY, Collections.singletonList("^(/heywhats(up)?(?:$|\\W).*)"), true, EvalCause.setValues()), + new Chain("8", false, Policy.DENY, Collections.singletonList("/thisshouldnotbedenied"), false, EvalCause.setValues()), + new Chain("9", true, Policy.DENY, Collections.singletonList("/blocksuggestionsonly"), false, EnumSet.of(EvalCause.CMD_SUGGESTION)) ); // Commands to be tested which are expected to be evaluated with a DENY policy. public static final String[] TEST_CMDS_EXPECTING_DENY = { - "/cd abc", "/cd reloada", "/cd reloa", "/cd reload abc", - "/plugins", "/pl", "/version", "/ver", "/icanhasbukkit", - "/about", "/?", "/help", "/ehelp", "/paper", "/spigot", - "/es give", "/es enchant", "/cd", "/", "/:", "/hello:how", - "/the:quick brown fox", "/bukkit:help", - // regex: - "/heywhats", - "/heywhats coolio", - "/heywhats verycool", - "/heywhatsup", - "/heywhatsup coolio", - "/heywhatsup verycool", + "/cd abc", "/cd reloada", "/cd reloa", "/cd reload abc", + "/plugins", "/pl", "/version", "/ver", "/icanhasbukkit", + "/about", "/?", "/help", "/ehelp", "/paper", "/spigot", + "/es give", "/es enchant", "/cd", "/", "/:", "/hello:how", + "/the:quick brown fox", "/bukkit:help", + // regex: + "/heywhats", + "/heywhats coolio", + "/heywhats verycool", + "/heywhatsup", + "/heywhatsup coolio", + "/heywhatsup verycool", }; // Commands to be tested which are expected to be evaluated with an ALLOW policy. public static final String[] TEST_CMDS_EXPECTING_ALLOW = { - "/cd reload", "/CD RELOAD", "//", "/es", "/es version", "/abcdefg", "/thisshouldnotbedenied", - // regex: - "/heywhats cool", - "/heywhats coolbeans", - "/heywhatsup cool", - "/heywhatsup coolbeans", - "/heywhatsup cool beans", + "/cd reload", "/CD RELOAD", "//", "/es", "/es version", "/abcdefg", "/thisshouldnotbedenied", + // regex: + "/heywhats cool", + "/heywhats coolbeans", + "/heywhatsup cool", + "/heywhatsup coolbeans", + "/heywhatsup cool beans", }; // Commands to be tested which contains colons. // 'donotuseinchains' term is used to make it clear PLEASE DO NOT USE THIS TERM IN ANY CHAINS or it can mess // up the tests. public static final String[] TEST_CMDS_WITH_COLONS = { - "/donotuseinchains:donotuseinchains", - "/donotuseinchains:donotuseinchains donotuseinchains", + "/donotuseinchains:donotuseinchains", + "/donotuseinchains:donotuseinchains donotuseinchains", }; // Commands to be tested which do NOT contain colons. // 'donotuseinchains' term is used to make it clear PLEASE DO NOT USE THIS TERM IN ANY CHAINS or it can mess // up the tests. public static final String[] TEST_CMDS_WITHOUT_COLONS = { - "/donotuseinchains", - "/donotuseinchains donotuseinchains", + "/donotuseinchains", + "/donotuseinchains donotuseinchains", }; // Default policy to be used during these tests. ALLOW here represents a whitelist which is the usual setup. @@ -114,17 +114,17 @@ public final class TestCmdEvaluation { * @return {@link Evaluation Evaluation} made with the provided parameters. */ private static Evaluation testEvaluation( - final String cmd, - final boolean denyColonInFirstArg + final String cmd, + final boolean denyColonInFirstArg ) { return Evaluator.evaluate( - cmd, - TEST_CHAINS, - TEST_DEFAULT_POLICY, - denyColonInFirstArg, - EvalCause.CMD_EXECUTION, - debugLogger, - warningLogger + cmd, + TEST_CHAINS, + TEST_DEFAULT_POLICY, + denyColonInFirstArg, + EvalCause.CMD_EXECUTION, + debugLogger, + warningLogger ); } @@ -137,14 +137,14 @@ private static Evaluation testEvaluation( @Test public void testChainsMustStartWithSlash() { for (final Chain chain : TEST_CHAINS) { - if(chain.isRegex()) { + if (chain.isRegex()) { continue; } - for(final String rule : chain.rules()) { + for (final String rule : chain.rules()) { Assertions.assertTrue( - rule.startsWith("/"), - "Non-regex chain rules must start with a slash, failed on rule='" + rule + "'" + rule.startsWith("/"), + "Non-regex chain rules must start with a slash, failed on rule='" + rule + "'" ); } } @@ -162,7 +162,7 @@ public void testRulesExpectingDeny() { final Evaluation eval = testEvaluation(cmd, true); Assertions.assertFalse(eval.dueToException(), "Exception not expected here"); Assertions.assertEquals(eval.policy(), Policy.DENY, "Wrong policy evaluated for cmd='" + - cmd + "'; description='" + eval.description() + "'"); + cmd + "'; description='" + eval.description() + "'"); } } @@ -178,7 +178,7 @@ public void testRulesExpectingAllow() { final Evaluation eval = testEvaluation(cmd, true); Assertions.assertFalse(eval.dueToException(), "Exception not expected here"); Assertions.assertEquals(eval.policy(), Policy.ALLOW, "Wrong policy evaluated for cmd='" + - cmd + "'; description='" + eval.description() + "'"); + cmd + "'; description='" + eval.description() + "'"); } } @@ -198,8 +198,8 @@ public void testColonBlocking() { Assertions.assertFalse(eval.dueToException(), "Exception not expected here"); Assertions.assertTrue(eval.dueToColonInFirstArg(), "Colon in first arg expected to be cause"); Assertions.assertEquals(eval.policy(), Policy.DENY, - "Wrong policy evaluated for cmd='" + cmd + "'; description='" + - eval.description() + "'"); + "Wrong policy evaluated for cmd='" + cmd + "'; description='" + + eval.description() + "'"); } { @@ -208,8 +208,8 @@ public void testColonBlocking() { Assertions.assertFalse(eval.dueToException(), "Exception not expected here"); Assertions.assertFalse(eval.dueToColonInFirstArg(), "Colon in first arg unexpected to be cause"); Assertions.assertEquals(eval.policy(), Policy.ALLOW, - "Wrong policy evaluated for cmd='" + cmd + "'; description='" + - eval.description() + "'"); + "Wrong policy evaluated for cmd='" + cmd + "'; description='" + + eval.description() + "'"); } } @@ -220,8 +220,8 @@ public void testColonBlocking() { Assertions.assertFalse(eval.dueToException(), "Exception not expected here"); Assertions.assertFalse(eval.dueToColonInFirstArg(), "Colon in first arg unexpected to be cause"); Assertions.assertEquals(eval.policy(), Policy.ALLOW, - "Wrong policy evaluated for cmd='" + cmd + "'; description='" + - eval.description() + "'"); + "Wrong policy evaluated for cmd='" + cmd + "'; description='" + + eval.description() + "'"); } { @@ -229,8 +229,8 @@ public void testColonBlocking() { Assertions.assertFalse(eval.dueToException(), "Exception not expected here"); Assertions.assertFalse(eval.dueToColonInFirstArg(), "Colon in first arg unexpected to be cause"); Assertions.assertEquals(eval.policy(), Policy.ALLOW, - "Wrong policy evaluated for cmd='" + cmd + "'; description='" + - eval.description() + "'"); + "Wrong policy evaluated for cmd='" + cmd + "'; description='" + + eval.description() + "'"); } } } @@ -269,13 +269,13 @@ public void testPluginCheckingRegex() { @Test public void testDenyDefaultPolicy() { final Evaluation eval = Evaluator.evaluate( - "/donotuseinchains", - TEST_CHAINS, - Policy.DENY, - true, - EvalCause.CMD_EXECUTION, - debugLogger, - warningLogger + "/donotuseinchains", + TEST_CHAINS, + Policy.DENY, + true, + EvalCause.CMD_EXECUTION, + debugLogger, + warningLogger ); Assertions.assertFalse(eval.dueToException(), "Exception not expected here"); @@ -317,13 +317,13 @@ public void testEvalCauseFiltering() { final String cmd = "/blocksuggestionsonly"; final Function eval = (cause) -> Evaluator.evaluate( - cmd, - TEST_CHAINS, - TEST_DEFAULT_POLICY, - true, - cause, - debugLogger, - warningLogger + cmd, + TEST_CHAINS, + TEST_DEFAULT_POLICY, + true, + cause, + debugLogger, + warningLogger ); final Evaluation evalExecution = eval.apply(EvalCause.CMD_EXECUTION); diff --git a/blackwidowpluginbukkit/pom.xml b/blackwidowpluginbukkit/pom.xml index 1733e65..b3d8b94 100644 --- a/blackwidowpluginbukkit/pom.xml +++ b/blackwidowpluginbukkit/pom.xml @@ -101,7 +101,8 @@ 3.6.0 - package + package + shade diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/BlackWidow.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/BlackWidow.java index 67a6175..a1f9ec8 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/BlackWidow.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/BlackWidow.java @@ -24,6 +24,7 @@ import io.github.arcaneplugins.blackwidow.plugin.bukkit.command.CommandManager; import io.github.arcaneplugins.blackwidow.plugin.bukkit.component.cmdblocking.CmdBlocker; import io.github.arcaneplugins.blackwidow.plugin.bukkit.listener.ListenerManager; +import io.github.arcaneplugins.blackwidow.plugin.bukkit.logic.BukkitVersionChecker; import io.github.arcaneplugins.blackwidow.plugin.bukkit.logic.LogicManager; import io.github.arcaneplugins.blackwidow.plugin.bukkit.util.ClassUtil; import io.github.arcaneplugins.blackwidow.plugin.bukkit.util.DebugCategory; @@ -31,6 +32,7 @@ import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; import java.util.EnumSet; import java.util.List; @@ -54,6 +56,7 @@ public final class BlackWidow extends JavaPlugin { private final LogicManager logicManager = new LogicManager(this); private final EnumSet debugCategories = EnumSet.noneOf(DebugCategory.class); private final MiniMessage miniMessage = MiniMessage.miniMessage(); + private final BukkitVersionChecker bukkitVersionChecker = new BukkitVersionChecker(this); private boolean usingPaper = false; private BukkitAudiences adventure = null; @@ -97,6 +100,7 @@ public void onEnable() { loadComponents(); listenerManager().load(); commandManager().load(); + bukkitVersionChecker.load(true); } catch (Exception ex) { ExceptionUtil.logException(this, ex, "An error occurred whilst enabling BlackWidow."); return; @@ -149,6 +153,7 @@ public void softReload() { loadConfigs(); logicManager().load(); loadComponents(); + bukkitVersionChecker.load(false); } catch (Exception ex) { ExceptionUtil.logException(this, ex, "An error occurred whilst performing a soft-reload."); return; @@ -208,8 +213,8 @@ private void loadComponents() { * @since 1.0.0 */ public void debugLog( - final DebugCategory cat, - final Supplier strSupp + final DebugCategory cat, + final Supplier strSupp ) { if (!enabledDebugCategories().contains(cat)) { return; @@ -318,4 +323,9 @@ public BukkitAudiences adventure() { public MiniMessage miniMessage() { return Objects.requireNonNull(miniMessage, "miniMessage"); } + + @NotNull + public BukkitVersionChecker bukkitVersionChecker() { + return Objects.requireNonNull(bukkitVersionChecker, "bukkitVersionChecker"); + } } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/YamlCfg.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/YamlCfg.java index 53cd711..e18ca48 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/YamlCfg.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/YamlCfg.java @@ -40,11 +40,11 @@ public abstract class YamlCfg { private CommentedConfigurationNode root; public YamlCfg( - final BlackWidow plugin, - final String filePathStr, - final String fileName, - final String description, - final int latestFileVersion + final BlackWidow plugin, + final String filePathStr, + final String fileName, + final String description, + final int latestFileVersion ) { this.plugin = Objects.requireNonNull(plugin, "plugin"); this.filePathStr = Objects.requireNonNull(filePathStr, "filePathStr"); @@ -67,10 +67,10 @@ public YamlCfg( } this.loader = YamlConfigurationLoader.builder() - .path(filePath()) - .indent(2) - .nodeStyle(NodeStyle.BLOCK) - .build(); + .path(filePath()) + .indent(2) + .nodeStyle(NodeStyle.BLOCK) + .build(); } public final void load() { @@ -83,22 +83,30 @@ public final void load() { final int latestVer = latestFileVersion(); if (installedVer <= 0) { throw new IllegalArgumentException("Config '" + fileName() + "' has an installed file version below " + - "or equal to 0, indicating it has almost certainly been manually tampered with. Please address " + - "this issue ASAP, as a manually-adjusted file version can cause instability."); + "or equal to 0, indicating it has almost certainly been manually tampered with. Please address " + + "this issue ASAP, as a manually-adjusted file version can cause instability."); } if (installedVer < latestVer) { + int lastVersion = installedFileVersion(); plugin().getLogger().info("Config '" + fileName() + "' is outdated, automatically upgrading."); while (installedVer < latestFileVersion()) { plugin().getLogger().info("Upgrading '" + fileName() + "' from v" + - installedVer + " to v" + (installedVer + 1) + "."); + installedVer + " to v" + (installedVer + 1) + "."); upgradeFile(); write(); installedVer = installedFileVersion(); + if (installedVer == lastVersion) { + // prevent potential endless loop + plugin().getLogger().warning("There was an error upgrading the file version, " + + "the version number was not incremented"); + break; + } + lastVersion = installedVer; } } else if (installedVer > latestVer) { plugin().getLogger().warning("Config '" + fileName() + "' apparently has version '" + installedVer + - "' but the latest is '" + latestVer + "'. Was the file version manually tampered with, or used " + - "on a newer version of the plugin? Please address ASAP as this may cause instability."); + "' but the latest is '" + latestVer + "'. Was the file version manually tampered with, or used " + + "on a newer version of the plugin? Please address ASAP as this may cause instability."); } // lastly, set 'context' metadata if not already present @@ -109,8 +117,8 @@ public final void load() { //noinspection deprecation,UnstableApiUsage final String version = plugin().usePaperFeatures() ? - plugin().getPluginMeta().getVersion() : - plugin().getDescription().getVersion(); + plugin().getPluginMeta().getVersion() : + plugin().getDescription().getVersion(); node.set(plugin().getName() + " " + version); diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/settings/Settings.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/settings/Settings.java index e11ebc0..800f65f 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/settings/Settings.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/settings/Settings.java @@ -21,7 +21,9 @@ import io.github.arcaneplugins.blackwidow.plugin.bukkit.BlackWidow; import io.github.arcaneplugins.blackwidow.plugin.bukkit.cfg.YamlCfg; import io.github.arcaneplugins.blackwidow.plugin.bukkit.util.DebugCategory; +import org.spongepowered.configurate.CommentedConfigurationNode; import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.serialize.SerializationException; import java.util.Collections; import java.util.Objects; @@ -32,17 +34,17 @@ public final class Settings extends YamlCfg { // REMEMBER TO UPDATE THE METHOD BELOW 'upgradeFile' IF THIS IS INCREMENTED. // ALSO REMEMBER TO UPDATE THE FILE ITSELF - BOTH THE 'original' AND 'installed' VALUES SHOULD MATCH THIS. // <<< !!! WARNING !!! NOTICE THIS MESSAGE !!! >>> - private static final int LATEST_FILE_VERSION = 1; + private static final int LATEST_FILE_VERSION = 2; public Settings( - final BlackWidow plugin + final BlackWidow plugin ) { super( - plugin, - "settings.yml", - "settings.yml", - "Settings", - LATEST_FILE_VERSION + plugin, + "settings.yml", + "settings.yml", + "Settings", + LATEST_FILE_VERSION ); } @@ -57,12 +59,12 @@ protected void loadMore() { try { plugin().enabledDebugCategories().clear(); plugin().enabledDebugCategories().addAll( - Objects.requireNonNullElse( - root() - .node("debug-categories") - .getList(DebugCategory.class), - Collections.emptySet() - ) + Objects.requireNonNullElse( + root() + .node("debug-categories") + .getList(DebugCategory.class), + Collections.emptySet() + ) ); } catch (final ConfigurateException ex) { throw new RuntimeException(ex.getMessage(), ex); @@ -81,10 +83,18 @@ public void upgradeFile() { //noinspection SwitchStatementWithTooFewBranches switch (installedVer) { case 1 -> { - // Do nothing. + final CommentedConfigurationNode updChkNode = root().node("update-checker"); + try { + updChkNode.node("enabled").set(true); + updChkNode.node("run-on-startup").set(true); + updChkNode.node("repeat-timer-duration-mins").set(60); + updChkNode.node("log-updates").set(true); + updChkNode.node("notify-players-with-permission").set(true); - //noinspection UnnecessaryBreak - break; + root().node("do-not-touch", "version", "installed").set(2); + } catch (SerializationException e) { + plugin().getLogger().warning(""); + } } default -> throw new IllegalArgumentException("No upgrade logic defined for file version v" + installedVer); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translation.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translation.java index 1ec2ac7..af2ce83 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translation.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translation.java @@ -34,41 +34,41 @@ public enum Translation { COMMAND_BLACKWIDOW_RELOAD_STARTED( - new String[]{"command", "blackwidow", "reload", "started"}, - true, - List.of("%prefix%Reloading BlackWidow.") + new String[]{"command", "blackwidow", "reload", "started"}, + true, + List.of("%prefix%Reloading BlackWidow.") ), COMMAND_BLACKWIDOW_RELOAD_FAILED( - new String[]{"command", "blackwidow", "reload", "failed"}, - true, - List.of("%prefix%Reload failed! Please see your server console for more details.") + new String[]{"command", "blackwidow", "reload", "failed"}, + true, + List.of("%prefix%Reload failed! Please see your server console for more details.") ), COMMAND_BLACKWIDOW_RELOAD_COMPLETE( - new String[]{"command", "blackwidow", "reload", "complete"}, - true, - List.of("%prefix%Reload complete.") + new String[]{"command", "blackwidow", "reload", "complete"}, + true, + List.of("%prefix%Reload complete.") ), COMMAND_BLACKWIDOW_VERSION( - new String[]{"command", "blackwidow", "version"}, - true, - List.of("*---* BlackWidowA security solution for Minecraft.'>BlackWidow v%version% *---*A security solution for Minecraft.From ArcanePlugins by %authors%.") + new String[]{"command", "blackwidow", "version"}, + true, + List.of("*---* BlackWidowA security solution for Minecraft.'>BlackWidow v%version% *---*A security solution for Minecraft.From ArcanePlugins by %authors%.") ), PREFIX( - new String[]{"prefix"}, - false, - "BlackWidow" + - "A security solution for Minecraft.'>BW: " + - "" + new String[]{"prefix"}, + false, + "BlackWidow" + + "A security solution for Minecraft.'>BW: " + + "" ), LIST_DELIMITER( - new String[]{"list-delimeter"}, - false, - ", " + new String[]{"list-delimeter"}, + false, + ", " ); private final String[] nodePath; @@ -76,9 +76,9 @@ public enum Translation { private final Object defValue; Translation( - final String[] nodePath, - final boolean isList, - final Object defValue + final String[] nodePath, + final boolean isList, + final Object defValue ) { this.nodePath = nodePath; this.isList = isList; @@ -98,7 +98,7 @@ public final Object defValue() { } public String strSingle( - final BlackWidow plugin + final BlackWidow plugin ) { if (isList()) { throw new IllegalStateException("Translation is list type, but called `str` instead of `strList`"); @@ -108,9 +108,9 @@ public String strSingle( } public static String placeholerify( - final BlackWidow plugin, - final String msg, - final Map> placeholders + final BlackWidow plugin, + final String msg, + final Map> placeholders ) { // temp var to hold latest placeholderified version of `msg` String formattedMsg = msg; @@ -134,31 +134,31 @@ public static String placeholerify( } public static String joinSeparatedStrings( - final BlackWidow plugin, - final Collection strings + final BlackWidow plugin, + final Collection strings ) { return String.join( - LIST_DELIMITER.strSingle(plugin), - strings + LIST_DELIMITER.strSingle(plugin), + strings ); } public static Component formatify( - final BlackWidow plugin, - final String msg, - final Map> placeholders + final BlackWidow plugin, + final String msg, + final Map> placeholders ) { return plugin.miniMessage().deserialize( - placeholerify( - plugin, - msg, - placeholders - ) + placeholerify( + plugin, + msg, + placeholders + ) ); } public final List strList( - final BlackWidow plugin + final BlackWidow plugin ) { if (!isList()) { throw new IllegalStateException("Translation is not list type, but called `strList` instead of `str`"); @@ -167,8 +167,8 @@ public final List strList( try { //noinspection unchecked return Objects.requireNonNullElse( - plugin.translations().root().node((Object[]) nodePath()).getList(String.class), - (List) defValue() + plugin.translations().root().node((Object[]) nodePath()).getList(String.class), + (List) defValue() ); } catch (final ConfigurateException ex) { throw new RuntimeException(ex.getMessage(), ex); @@ -176,9 +176,9 @@ public final List strList( } public final void sendTo( - final BlackWidow plugin, - final Audience audience, - final Map> placeholders + final BlackWidow plugin, + final Audience audience, + final Map> placeholders ) { Objects.requireNonNull(plugin, "plugin"); Objects.requireNonNull(audience, "audience"); @@ -193,9 +193,9 @@ public final void sendTo( } public final void sendTo( - final BlackWidow plugin, - final CommandSender sender, - final Map> placeholders + final BlackWidow plugin, + final CommandSender sender, + final Map> placeholders ) { Objects.requireNonNull(plugin, "plugin"); Objects.requireNonNull(sender, "sender"); @@ -204,9 +204,9 @@ public final void sendTo( @SuppressWarnings("unused") public final void sendTo( - final BlackWidow plugin, - final Player player, - final Map> placeholders + final BlackWidow plugin, + final Player player, + final Map> placeholders ) { Objects.requireNonNull(plugin, "plugin"); Objects.requireNonNull(player, "player"); diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translations.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translations.java index 0fc961b..8ed63c4 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translations.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/cfg/translations/Translations.java @@ -36,14 +36,14 @@ public final class Translations extends YamlCfg { * @since 1.0.0 */ public Translations( - final BlackWidow plugin + final BlackWidow plugin ) { super( - plugin, - "translations.yml", - "translations.yml", - "Translations", - LATEST_FILE_VERSION + plugin, + "translations.yml", + "translations.yml", + "Translations", + LATEST_FILE_VERSION ); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/CommandManager.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/CommandManager.java index d45c5ab..6aaa740 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/CommandManager.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/CommandManager.java @@ -29,7 +29,7 @@ public final class CommandManager { private final BlackWidow plugin; public CommandManager( - final BlackWidow plugin + final BlackWidow plugin ) { this.plugin = plugin; } @@ -37,10 +37,10 @@ public CommandManager( public void init() { plugin().getLogger().info("Initialising commands."); CommandAPI.onLoad( - new CommandAPIBukkitConfig(plugin()) - .verboseOutput(false) - .silentLogs(true) - .usePluginNamespace() + new CommandAPIBukkitConfig(plugin()) + .verboseOutput(false) + .silentLogs(true) + .usePluginNamespace() ); registerCommands(); } @@ -59,10 +59,10 @@ public void disable() { CommandAPI.unregister("blackwidow", true); CommandAPI.onDisable(); CommandAPI.getRegisteredCommands() - .stream() - .map(RegisteredCommand::commandName) - .iterator() - .forEachRemaining(CommandAPI::unregister); + .stream() + .map(RegisteredCommand::commandName) + .iterator() + .forEachRemaining(CommandAPI::unregister); } private BlackWidow plugin() { diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/BlackWidowCommand.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/BlackWidowCommand.java index bcf818f..04703f2 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/BlackWidowCommand.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/BlackWidowCommand.java @@ -30,7 +30,7 @@ public final class BlackWidowCommand extends CommandAPICommand { private final BlackWidow plugin; public BlackWidowCommand( - final BlackWidow plugin + final BlackWidow plugin ) { super("blackwidow"); this.plugin = plugin; @@ -39,8 +39,8 @@ public BlackWidowCommand( withShortDescription("Base command to view info of and manage the BlackWidow plugin."); withFullDescription("Base command to view and manage the BlackWidow plugin."); withSubcommands( - new ReloadSubcommand(plugin()), - new VersionSubcommand(plugin()) + new ReloadSubcommand(plugin()), + new VersionSubcommand(plugin()) ); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/ReloadSubcommand.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/ReloadSubcommand.java index ed73d65..fd1ddc1 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/ReloadSubcommand.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/ReloadSubcommand.java @@ -30,7 +30,7 @@ public final class ReloadSubcommand extends CommandAPICommand { private final BlackWidow plugin; public ReloadSubcommand( - final BlackWidow plugin + final BlackWidow plugin ) { super("reload"); this.plugin = plugin; diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/VersionSubcommand.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/VersionSubcommand.java index 4b494b8..359db6b 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/VersionSubcommand.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/command/blackwidow/subcommand/VersionSubcommand.java @@ -30,7 +30,7 @@ public final class VersionSubcommand extends CommandAPICommand { private final BlackWidow plugin; public VersionSubcommand( - final BlackWidow plugin + final BlackWidow plugin ) { super("version"); this.plugin = plugin; @@ -41,13 +41,13 @@ public VersionSubcommand( executes((sender, args) -> { //noinspection deprecation Translation.COMMAND_BLACKWIDOW_VERSION.sendTo( - plugin(), - sender, - Map.of( - "version", () -> plugin().getDescription().getVersion(), - "authors", () -> Translation - .joinSeparatedStrings(plugin(), plugin().getDescription().getAuthors()) - ) + plugin(), + sender, + Map.of( + "version", () -> plugin().getDescription().getVersion(), + "authors", () -> Translation + .joinSeparatedStrings(plugin(), plugin().getDescription().getAuthors()) + ) ); }); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/BukkitChain.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/BukkitChain.java index 9593186..81c14d0 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/BukkitChain.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/BukkitChain.java @@ -52,15 +52,15 @@ public final class BukkitChain extends Chain { * @since 1.0.0 */ public BukkitChain( - final BlackWidow plugin, - final String id, - final boolean enabled, - final Policy policy, - final Collection rules, - final boolean isRegex, - final Collection causeFilters, - final Collection requirements, - final Collection actions + final BlackWidow plugin, + final String id, + final boolean enabled, + final Policy policy, + final Collection rules, + final boolean isRegex, + final Collection causeFilters, + final Collection requirements, + final Collection actions ) { super(id, enabled, policy, rules, isRegex, causeFilters); this.plugin = Objects.requireNonNull(plugin, "plugin"); @@ -69,19 +69,19 @@ public BukkitChain( } public BukkitChain( - final BlackWidow plugin, - final CommentedConfigurationNode node + final BlackWidow plugin, + final CommentedConfigurationNode node ) { this( - Objects.requireNonNull(plugin, "plugin"), - node.node("id").getString(), - node.node("enabled").getBoolean(true), - parsePolicyAtNode(node.node("policy")), - parseStringListAtNode(node.node("rules")), - node.node("regex").getBoolean(false), - parseEvalCauseListAtNode(node.node("cause-filters")), - plugin.logicManager().parseRequirementsInChildrenOfNode(node.node("requirements")), - plugin.logicManager().parseActionsInChildrenOfNode(node.node("actions")) + Objects.requireNonNull(plugin, "plugin"), + node.node("id").getString(), + node.node("enabled").getBoolean(true), + parsePolicyAtNode(node.node("policy")), + parseStringListAtNode(node.node("rules")), + node.node("regex").getBoolean(false), + parseEvalCauseListAtNode(node.node("cause-filters")), + plugin.logicManager().parseRequirementsInChildrenOfNode(node.node("requirements")), + plugin.logicManager().parseActionsInChildrenOfNode(node.node("actions")) ); } @@ -91,7 +91,7 @@ private BlackWidow plugin() { } private static Policy parsePolicyAtNode( - final CommentedConfigurationNode node + final CommentedConfigurationNode node ) { try { return node.get(Policy.class, Policy.DENY); @@ -101,7 +101,7 @@ private static Policy parsePolicyAtNode( } private static List parseStringListAtNode( - final CommentedConfigurationNode node + final CommentedConfigurationNode node ) { try { return node.getList(String.class); @@ -119,7 +119,7 @@ public Collection actions() { } public static Collection parseEvalCauseListAtNode( - final CommentedConfigurationNode node + final CommentedConfigurationNode node ) { Objects.requireNonNull(node, "node"); diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/CmdBlocker.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/CmdBlocker.java index de19aab..daef955 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/CmdBlocker.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/component/cmdblocking/CmdBlocker.java @@ -97,7 +97,7 @@ public Evaluation evalAndProcess(final Context context, final String cmd, final final Player player = context.player(false); if (player != null && player.isOp() && operatorsBypassCompletely()) { return new Evaluation(cmd, Policy.ALLOW, null, null, "Operators configured to bypass command blocking") - .withDueToOperatorsBypassCmdBlocking(true); + .withDueToOperatorsBypassCmdBlocking(true); } // copy chains into a new LinkedHashSet, remove chains not applicable to context. diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadActionsReadyEvent.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadActionsReadyEvent.java index d234c80..89fbc24 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadActionsReadyEvent.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadActionsReadyEvent.java @@ -36,8 +36,8 @@ public final class LoadActionsReadyEvent extends Event { private static final HandlerList HANDLERS = new HandlerList(); public LoadActionsReadyEvent( - final BlackWidow plugin, - final Map> actParsers + final BlackWidow plugin, + final Map> actParsers ) { this.plugin = Objects.requireNonNull(plugin, "plugin"); this.actParsers = Objects.requireNonNull(actParsers, "actParsers"); diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadRequirementsReadyEvent.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadRequirementsReadyEvent.java index 3cd5bf0..08ab31e 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadRequirementsReadyEvent.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/event/LoadRequirementsReadyEvent.java @@ -36,8 +36,8 @@ public final class LoadRequirementsReadyEvent extends Event { private static final HandlerList HANDLERS = new HandlerList(); public LoadRequirementsReadyEvent( - final BlackWidow plugin, - final Map> reqParsers + final BlackWidow plugin, + final Map> reqParsers ) { this.plugin = Objects.requireNonNull(plugin, "plugin"); this.reqParsers = Objects.requireNonNull(reqParsers, "reqParsers"); diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/ListenerManager.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/ListenerManager.java index 9196c3c..d971019 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/ListenerManager.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/ListenerManager.java @@ -21,11 +21,13 @@ import io.github.arcaneplugins.blackwidow.plugin.bukkit.BlackWidow; import io.github.arcaneplugins.blackwidow.plugin.bukkit.listener.bukkit.PlayerCommandPreprocessListener; import io.github.arcaneplugins.blackwidow.plugin.bukkit.listener.bukkit.PlayerCommandSendListener; +import io.github.arcaneplugins.blackwidow.plugin.bukkit.listener.bukkit.PlayerJoinListener; import io.github.arcaneplugins.blackwidow.plugin.bukkit.listener.paper.AsyncPlayerCommandSendListener; import org.bukkit.event.Listener; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.List; public final class ListenerManager { @@ -33,20 +35,21 @@ public final class ListenerManager { private final Collection listeners = new LinkedHashSet<>(); public ListenerManager( - final BlackWidow plugin + final BlackWidow plugin ) { this.plugin = plugin; } private void constructListeners() { - listeners().add( - new PlayerCommandPreprocessListener(plugin()) - ); + listeners().addAll(List.of( + new PlayerCommandPreprocessListener(plugin()), + new PlayerJoinListener(plugin()) + )); listeners().add( - plugin().usingPaper() && plugin().usePaperFeatures() ? - new AsyncPlayerCommandSendListener(plugin()) : - new PlayerCommandSendListener(plugin()) + plugin().usingPaper() && plugin().usePaperFeatures() ? + new AsyncPlayerCommandSendListener(plugin()) : + new PlayerCommandSendListener(plugin()) ); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandPreprocessListener.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandPreprocessListener.java index 16b78a0..e80e30d 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandPreprocessListener.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandPreprocessListener.java @@ -35,7 +35,7 @@ public final class PlayerCommandPreprocessListener implements Listener { private final BlackWidow plugin; public PlayerCommandPreprocessListener( - final BlackWidow plugin + final BlackWidow plugin ) { this.plugin = plugin; } @@ -50,13 +50,13 @@ public void handle(final PlayerCommandPreprocessEvent event) { final Player player = event.getPlayer(); final Context context = new Context(plugin()) - .withPlayer(player) - .withCommands(List.of(event.getMessage())); + .withPlayer(player) + .withCommands(List.of(event.getMessage())); if (plugin().cmdBlocker().filterCmdExecution()) { final Evaluation eval = plugin() - .cmdBlocker() - .evalAndProcess(context, event.getMessage(), true, EvalCause.CMD_EXECUTION); + .cmdBlocker() + .evalAndProcess(context, event.getMessage(), true, EvalCause.CMD_EXECUTION); switch (eval.policy()) { case ALLOW: @@ -69,7 +69,7 @@ public void handle(final PlayerCommandPreprocessEvent event) { case null, default: event.setCancelled(true); throw new IllegalStateException("Unexpected policy '" + eval.policy() + "', prevented " + - "command execution for best-practice security purposes."); + "command execution for best-practice security purposes."); } // TODO Debug log the evaluation's description. diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandSendListener.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandSendListener.java index 45bfaee..6496d6f 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandSendListener.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerCommandSendListener.java @@ -33,7 +33,7 @@ public final class PlayerCommandSendListener implements Listener { private final BlackWidow plugin; public PlayerCommandSendListener( - final BlackWidow plugin + final BlackWidow plugin ) { this.plugin = plugin; } @@ -48,19 +48,19 @@ public void handle(final PlayerCommandSendEvent event) { final Player player = event.getPlayer(); final Context context = new Context(plugin()) - .withPlayer(player) - .withCommands(event.getCommands().stream().map(cmd -> "/" + cmd).toList()); + .withPlayer(player) + .withCommands(event.getCommands().stream().map(cmd -> "/" + cmd).toList()); if (plugin().cmdBlocker().filterCmdSuggestion()) { event.getCommands().removeIf(cmd -> { final Evaluation eval = plugin() - .cmdBlocker() - .evalAndProcess(context, "/" + cmd, false, EvalCause.CMD_SUGGESTION); + .cmdBlocker() + .evalAndProcess(context, "/" + cmd, false, EvalCause.CMD_SUGGESTION); if (eval.policy() == null || (eval.policy() != Policy.ALLOW && eval.policy() != Policy.DENY)) { event.getCommands().clear(); throw new IllegalStateException("Unexpected policy '" + eval.policy() + "', prevented " + - "all command suggestions for best-practice security purposes."); + "all command suggestions for best-practice security purposes."); } // TODO Debug log the evaluation's description. diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerJoinListener.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerJoinListener.java new file mode 100644 index 0000000..2c47ca6 --- /dev/null +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/bukkit/PlayerJoinListener.java @@ -0,0 +1,54 @@ +package io.github.arcaneplugins.blackwidow.plugin.bukkit.listener.bukkit; + +import io.github.arcaneplugins.blackwidow.plugin.bukkit.BlackWidow; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * Listens for PlayerJoinEvent events. + * + * @author stumper66 + * @since 1.1.0 + */ +public final class PlayerJoinListener implements Listener { + + private final BlackWidow plugin; + + public PlayerJoinListener( + @NotNull final BlackWidow plugin + ) { + this.plugin = plugin; + } + + @EventHandler(ignoreCancelled = true) + public void handle( + @NotNull final PlayerJoinEvent event + ) { + handleUpdateChecking(event); + } + + private void handleUpdateChecking( + @NotNull final PlayerJoinEvent event + ) { + if (!plugin().bukkitVersionChecker().getNotifyPlayers()) { + return; + } + + final String message = plugin().bukkitVersionChecker().getNotifyMessage(); + + if (message == null) { + return; + } + + event.getPlayer().sendMessage(message); + } + + @NotNull + private BlackWidow plugin() { + return Objects.requireNonNull(plugin, "plugin"); + } +} diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/paper/AsyncPlayerCommandSendListener.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/paper/AsyncPlayerCommandSendListener.java index 5f93d21..f546773 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/paper/AsyncPlayerCommandSendListener.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/listener/paper/AsyncPlayerCommandSendListener.java @@ -33,19 +33,19 @@ public final class AsyncPlayerCommandSendListener implements Listener { private final BlackWidow plugin; public AsyncPlayerCommandSendListener( - final BlackWidow plugin + final BlackWidow plugin ) { this.plugin = plugin; } @EventHandler public void handle( - final AsyncPlayerSendCommandsEvent event + final AsyncPlayerSendCommandsEvent event ) { // Event Javadocs: // https://jd.papermc.io/paper/1.21/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.html - if(!event.isAsynchronous() && !event.hasFiredAsync()) { + if (!event.isAsynchronous() && !event.hasFiredAsync()) { // as per paper API docs, this event can fire twice, once async and once sync. // let's make sure we only handle the async scenario. // OR, if the event will not be fired async, then handle it anyways. @@ -58,9 +58,9 @@ public void handle( //TODO Implement for Paper plugin().debugLog( - DebugCategory.ASYNC_PLAYER_COMMAND_SEND_LISTENER, - () -> "AsyncPlayerCommandSendListener{player=" + player.getName() + ", commandNode.toString=" + - commandNode + "};" + DebugCategory.ASYNC_PLAYER_COMMAND_SEND_LISTENER, + () -> "AsyncPlayerCommandSendListener{player=" + player.getName() + ", commandNode.toString=" + + commandNode + "};" ); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Action.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Action.java index bf6ce7d..0356b43 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Action.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Action.java @@ -28,8 +28,8 @@ public abstract class Action implements LogicUnit { private final String id; public Action( - final BlackWidow plugin, - final String id + final BlackWidow plugin, + final String id ) { this.plugin = Objects.requireNonNull(plugin, "plugin"); this.id = Objects.requireNonNull(id, "id"); diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/BukkitVersionChecker.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/BukkitVersionChecker.java new file mode 100644 index 0000000..3af91a0 --- /dev/null +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/BukkitVersionChecker.java @@ -0,0 +1,178 @@ +package io.github.arcaneplugins.blackwidow.plugin.bukkit.logic; + +import io.github.arcaneplugins.blackwidow.lib.cmdblocking.UpdateChecker; +import io.github.arcaneplugins.blackwidow.plugin.bukkit.BlackWidow; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.spongepowered.configurate.CommentedConfigurationNode; + +import java.io.FileNotFoundException; +import java.io.InvalidObjectException; + +/** + * Handles the update checker for Bukkit implementations + * + * @author stumper66 + * @since 1.1.0 + */ +public class BukkitVersionChecker { + public BukkitVersionChecker(final BlackWidow plugin) { + this.plugin = plugin; + } + + private final BlackWidow plugin; + private BukkitTask notifyTask; + boolean logUpdates; + private boolean notifyPlayers; + private String notifyMessage; + private int lastTimerDuration; + + public void load(final boolean isStartup) { + final CommentedConfigurationNode settings = plugin.settings().root() + .node("update-checker"); + + final boolean enabled = settings.node("enabled").getBoolean(true); + final boolean runOnStartup = settings.node("run-on-startup").getBoolean(true); + final int repeatTimerDuration = settings.node("repeat-timer-duration-mins").getInt(); + logUpdates = settings.node("log-updates").getBoolean(true); + notifyPlayers = settings.node("notify-players-with-permission").getBoolean(true); + + if (!enabled) { + disableChecker(); + return; + } + + startTimerIfNeeded(repeatTimerDuration); + + if (runOnStartup && isStartup) { + getLatestVersion(); + } + } + + public boolean getNotifyPlayers() { + return notifyPlayers; + } + + public String getNotifyMessage() { + return notifyMessage; + } + + private void disableChecker() { + if (notifyTask != null && !notifyTask.isCancelled()) { + notifyTask.cancel(); + notifyTask = null; + } + } + + private void startTimerIfNeeded(final int repeatTimerDuration) { + if (repeatTimerDuration <= 0) { + disableChecker(); + return; + } + + if (notifyTask != null && !notifyTask.isCancelled() + && repeatTimerDuration == lastTimerDuration) { + return; + } + + disableChecker(); + + final BukkitRunnable runnable = new BukkitRunnable() { + @Override + public void run() { + getLatestVersion(); + } + }; + + lastTimerDuration = repeatTimerDuration; + final long delay = repeatTimerDuration * 20L * 60L; + this.notifyTask = runnable.runTaskTimerAsynchronously(plugin, delay, delay); + } + + private void getLatestVersion() { + new BukkitRunnable() { + @Override + public void run() { + checkForLatestVersion(); + } + }.runTaskAsynchronously(plugin); + } + + private void checkForLatestVersion() { + try { + UpdateChecker.getLatestVersion("BlackWidow", latestVersion -> { + //noinspection deprecation + final String currentVersion = plugin.getDescription().getVersion() + .split(" ")[0]; + + if (latestVersion == null) { + if (logUpdates){ + plugin.getLogger().warning("Error check for latest version, string was null"); + } + + return; + } + + final VersionInfo thisVersion; + final VersionInfo hangarVersion; + boolean isOutOfDate; + boolean isNewerVersion = false; + boolean wasUpToDate = false; + + try { + thisVersion = new VersionInfo(currentVersion); + hangarVersion = new VersionInfo(latestVersion); + + isOutOfDate = (thisVersion.compareTo(hangarVersion) < 0); + isNewerVersion = (thisVersion.compareTo(hangarVersion) > 0); + } catch (InvalidObjectException e) { + plugin.getLogger().warning("Got exception creating version objects: " + e.getMessage()); + isOutOfDate = !currentVersion.equals(latestVersion); + } + + if (isNewerVersion) { + notifyMessage = "Your BlackWindow version is a pre-release. Latest release version is " + latestVersion + ". (You're running " + currentVersion + ")"; + } else if (isOutOfDate) { + notifyMessage = "Your BlackWidow version is outdated! Please update to " + latestVersion + " as soon as possible. (You're running " + currentVersion + ")"; + } else { + notifyMessage = "Your BlackWidow version is up to date (You're running " + currentVersion + ")"; + wasUpToDate = true; + } + + if (logUpdates) { + plugin.getLogger().info(notifyMessage); + } + + if (!wasUpToDate) { + notifyPlayers(); + } + }); + } catch (FileNotFoundException e) { + // this exception occurs if the dest URL is incorrect such as a typo in the resource name + plugin.getLogger().warning("Error getting latest version, file not found: " + e.getMessage()); + } catch (Exception e) { + plugin.getLogger().warning("Error getting latest version: " + e.getMessage()); + } + } + + private void notifyPlayers() { + if (!notifyPlayers || notifyMessage == null) { + return; + } + + final String requiredPermission = "blackwidow.notifyupdates"; + + for (final Player player : Bukkit.getOnlinePlayers()) { + if (!player.isValid()) { + continue; + } + if (!player.hasPermission(requiredPermission)) { + continue; + } + + player.sendMessage(notifyMessage); + } + } +} diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Context.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Context.java index 84aa943..0f37bbc 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Context.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Context.java @@ -34,7 +34,7 @@ public final class Context { private CommandSender sender; public Context( - final BlackWidow plugin + final BlackWidow plugin ) { this.plugin = Objects.requireNonNull(plugin, "plugin"); } @@ -77,7 +77,7 @@ private static final class ContextException extends RuntimeException { private final String message; public ContextException( - final String message + final String message ) { this.message = message; } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/LogicManager.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/LogicManager.java index 27bdc67..0b91a97 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/LogicManager.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/LogicManager.java @@ -41,7 +41,7 @@ public final class LogicManager { private final BlackWidow plugin; public LogicManager( - final BlackWidow plugin + final BlackWidow plugin ) { this.plugin = plugin; } @@ -61,8 +61,8 @@ private void loadStdRequirements() { private void loadThirdPartyRequirements() { final LoadRequirementsReadyEvent event = new LoadRequirementsReadyEvent( - plugin(), - requirementParsers() + plugin(), + requirementParsers() ); plugin().getServer().getPluginManager().callEvent(event); } @@ -73,37 +73,37 @@ private void loadStdActions() { private void loadThirdPartyActions() { final LoadActionsReadyEvent event = new LoadActionsReadyEvent( - plugin(), - actionParsers() + plugin(), + actionParsers() ); plugin().getServer().getPluginManager().callEvent(event); } public Action parseActionAtNode( - final CommentedConfigurationNode node + final CommentedConfigurationNode node ) { final String id = Objects.requireNonNull( - node.node("id").getString(), - "Action node at path '" + node.path() + "' must define an ID with 'id'" + node.node("id").getString(), + "Action node at path '" + node.path() + "' must define an ID with 'id'" ); try { return Objects.requireNonNull( - actionParsers().get(id), - "Action node at path '" + node.path() + "' must define the ID ('id') of an Action that" + - "exists (did you make a typo?), but got '" + id + "'" + actionParsers().get(id), + "Action node at path '" + node.path() + "' must define the ID ('id') of an Action that" + + "exists (did you make a typo?), but got '" + id + "'" ).apply(node); } catch (final Exception ex) { throw new RuntimeException( - "Unable to parse Action with ID '" + id + "' at node path '" + node.path() + "': " + - ex.getLocalizedMessage(), - ex + "Unable to parse Action with ID '" + id + "' at node path '" + node.path() + "': " + + ex.getLocalizedMessage(), + ex ); } } public Collection parseActionsInChildrenOfNode( - final CommentedConfigurationNode nodes + final CommentedConfigurationNode nodes ) { final Collection actions = new LinkedHashSet<>(); for (final CommentedConfigurationNode node : nodes.childrenList()) { @@ -113,30 +113,30 @@ public Collection parseActionsInChildrenOfNode( } public Requirement parseRequirementAtNode( - final CommentedConfigurationNode node + final CommentedConfigurationNode node ) { final String id = Objects.requireNonNull( - node.node("id").getString(), - "Requirement node at path '" + node.path() + "' must define an ID with 'id'" + node.node("id").getString(), + "Requirement node at path '" + node.path() + "' must define an ID with 'id'" ); try { return Objects.requireNonNull( - requirementParsers().get(id), - "Requirement node at path '" + node.path() + "' must define the ID ('id') of a Requirement that" + - "exists (did you make a typo?), but got '" + id + "'" + requirementParsers().get(id), + "Requirement node at path '" + node.path() + "' must define the ID ('id') of a Requirement that" + + "exists (did you make a typo?), but got '" + id + "'" ).apply(node); } catch (final Exception ex) { throw new RuntimeException( - "Unable to parse Requirement with ID '" + id + "' at node path '" + node.path() + "': " + - ex.getLocalizedMessage(), - ex + "Unable to parse Requirement with ID '" + id + "' at node path '" + node.path() + "': " + + ex.getLocalizedMessage(), + ex ); } } public Collection parseRequirementsInChildrenOfNode( - final CommentedConfigurationNode nodes + final CommentedConfigurationNode nodes ) { final Collection requirements = new LinkedHashSet<>(); for (final CommentedConfigurationNode node : nodes.childrenList()) { diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Requirement.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Requirement.java index 185fab7..da81393 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Requirement.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/Requirement.java @@ -29,9 +29,9 @@ public abstract class Requirement implements LogicUnit { private final boolean inverted; public Requirement( - final BlackWidow plugin, - final String id, - final boolean inverted + final BlackWidow plugin, + final String id, + final boolean inverted ) { this.plugin = plugin; this.id = id; diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/VersionInfo.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/VersionInfo.java new file mode 100644 index 0000000..c3f6269 --- /dev/null +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/VersionInfo.java @@ -0,0 +1,102 @@ +package io.github.arcaneplugins.blackwidow.plugin.bukkit.logic; + +import io.github.arcaneplugins.blackwidow.plugin.bukkit.util.ClassUtil; +import io.github.arcaneplugins.blackwidow.plugin.bukkit.util.StringUtil; +import org.jetbrains.annotations.NotNull; + +import java.io.InvalidObjectException; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * A custom implementation for comparing program versions + * + * @author stumper66 + * @since 1.1.0 + */ +public final class VersionInfo implements Comparable { + + public VersionInfo( + @NotNull final String verStr + ) throws InvalidObjectException { + Objects.requireNonNull(verStr, "verStr"); + + this.verStr = verStr; + + for (final String numTemp : verStr.split("\\.")) { + if (!StringUtil.isDouble(numTemp)) { + throw new InvalidObjectException("Version can only contain numbers and periods"); + } + final int intD = Integer.parseInt(numTemp); + verSplit().add(intD); + } + + for (int i = 4; i < verSplit().size(); i++) { + verSplit().add(0); + } + } + + private final String verStr; + private final List verSplit = new LinkedList<>(); + + @Override + public boolean equals(final Object o) { + if (o == null) { + return false; + } + if (o == this) { + return true; + } + if (!(o instanceof VersionInfo)) { + return false; + } + + return this.verStr.equals(((VersionInfo) o).verStr()); + } + + @Override + @NotNull + public String toString() { + return verStr(); + } + + @NotNull + private String verStr() { + return Objects.requireNonNull(verStr, "verStr"); + } + + @Override + public int compareTo( + @NotNull final VersionInfo other + ) { + for (int i = 0; i < 4; i++) { + + if (other.verSplit().size() <= i && this.verSplit().size() - 1 <= i) { + break; + } + + // if one has extra digits we'll assume that one is newer + else if (other.verSplit().size() <= i) { + return 1; + } else if (verSplit().size() <= i) { + return -1; + } + + final int compareInt = other.verSplit().get(i); + final int thisInt = this.verSplit().get(i); + + if (thisInt > compareInt) { + return 1; + } else if (thisInt < compareInt) { + return -1; + } + } + + return 0; + } + + private List verSplit() { + return Objects.requireNonNull(verSplit, "verSplit"); + } +} \ No newline at end of file diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/action/SendMessageAction.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/action/SendMessageAction.java index 7bef17c..d3c0926 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/action/SendMessageAction.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/action/SendMessageAction.java @@ -37,26 +37,26 @@ public final class SendMessageAction extends Action { private final Collection message; public SendMessageAction( - final BlackWidow plugin, - final Collection message + final BlackWidow plugin, + final Collection message ) { super(plugin, ID); this.message = Objects.requireNonNull(message, "message"); } public SendMessageAction( - final BlackWidow plugin, - final CommentedConfigurationNode node + final BlackWidow plugin, + final CommentedConfigurationNode node ) { super( - plugin, - Objects.requireNonNull(node.node("id").getString(), "id") + plugin, + Objects.requireNonNull(node.node("id").getString(), "id") ); try { this.message = Objects.requireNonNull( - node.node("msg").getList(String.class), - "msg" + node.node("msg").getList(String.class), + "msg" ); } catch (final SerializationException ex) { throw new RuntimeException("Unable to read msg string list", ex); @@ -76,12 +76,12 @@ public Collection message() { @Override public void run(Context context) { final Audience advPlayer = plugin() - .adventure() - .player(context.player(true)); + .adventure() + .player(context.player(true)); message() - .stream() - .map(msg -> Translation.formatify(plugin(), msg, Collections.emptyMap())) - .forEachOrdered(advPlayer::sendMessage); + .stream() + .map(msg -> Translation.formatify(plugin(), msg, Collections.emptyMap())) + .forEachOrdered(advPlayer::sendMessage); } } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/HasPermissionRequirement.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/HasPermissionRequirement.java index 869f348..02c9433 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/HasPermissionRequirement.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/HasPermissionRequirement.java @@ -32,22 +32,22 @@ public final class HasPermissionRequirement extends Requirement { private final String permission; public HasPermissionRequirement( - final BlackWidow plugin, - final String permission, - final boolean inverted + final BlackWidow plugin, + final String permission, + final boolean inverted ) { super(plugin, ID, inverted); this.permission = Objects.requireNonNull(permission, "permission"); } public HasPermissionRequirement( - final BlackWidow plugin, - final CommentedConfigurationNode node + final BlackWidow plugin, + final CommentedConfigurationNode node ) { this( - plugin, - Objects.requireNonNull(node.node("permission").getString(), "permission"), - node.node("inverted").getBoolean(false) + plugin, + Objects.requireNonNull(node.node("permission").getString(), "permission"), + node.node("inverted").getBoolean(false) ); } @@ -60,8 +60,8 @@ public HasPermissionRequirement( @Override public boolean validateImpl(final Context context) { return Objects.requireNonNullElse( - context.player(false), - context.commandSender(false) + context.player(false), + context.commandSender(false) ).hasPermission(permission()); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/InWorldRequirement.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/InWorldRequirement.java index 1409015..12cbaba 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/InWorldRequirement.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/logic/inbuilt/requirement/InWorldRequirement.java @@ -32,22 +32,22 @@ public final class InWorldRequirement extends Requirement { private final String worldName; public InWorldRequirement( - final BlackWidow plugin, - final String worldName, - final boolean inverted + final BlackWidow plugin, + final String worldName, + final boolean inverted ) { super(plugin, ID, inverted); this.worldName = Objects.requireNonNull(worldName, "worldName"); } public InWorldRequirement( - final BlackWidow plugin, - final CommentedConfigurationNode node + final BlackWidow plugin, + final CommentedConfigurationNode node ) { this( - plugin, - Objects.requireNonNull(node.node("world-name").getString(), "worldName"), - node.node("inverted").getBoolean(false) + plugin, + Objects.requireNonNull(node.node("world-name").getString(), "worldName"), + node.node("inverted").getBoolean(false) ); } diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/util/ExceptionUtil.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/util/ExceptionUtil.java index f9e84fa..1034618 100644 --- a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/util/ExceptionUtil.java +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/util/ExceptionUtil.java @@ -29,9 +29,9 @@ private ExceptionUtil() { } public static void logException( - final BlackWidow plugin, - final Exception ex, - final String msg + final BlackWidow plugin, + final Exception ex, + final String msg ) { final Logger logger = plugin.getLogger(); @@ -42,10 +42,10 @@ public static void logException( logger.severe(""); logger.severe(minidiv + "Warning: Please Read Below Carefully"); logger.severe("\tBlackWidow has detected an issue, please carefully read the below and attempt to " + - "resolve the issue (or otherwise, report it to the maintainers via the details also provided below)."); + "resolve the issue (or otherwise, report it to the maintainers via the details also provided below)."); logger.severe(""); logger.severe("\tSometimes, these are simple errors caused when users misconfigure the plugin, other " + - "times, it can be a bug (issue) with the plugin's code which the maintainers might not know about."); + "times, it can be a bug (issue) with the plugin's code which the maintainers might not know about."); logger.severe(""); logger.severe(bigdiv); logger.severe(""); @@ -60,11 +60,11 @@ public static void logException( logger.severe(""); logger.severe(minidiv + "Contacting Maintainers for Assistance"); logger.severe("If this is an issue you can't fix, or if it is a bug report, please contact the " + - "maintainers by checking for recommended links in the GitHub repository description."); + "maintainers by checking for recommended links in the GitHub repository description."); logger.severe("Please visit: < https://github.com/ArcanePlugins/BlackWidow/ >"); logger.severe(""); logger.severe("Our Discord Server (should be linked within the page above) is usually the best avenue " + - "for users to report issues like these."); + "for users to report issues like these."); logger.severe(""); logger.severe("It's usually worth checking the Frequently Asked Questions page (if applicable)."); logger.severe(""); diff --git a/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/util/StringUtil.java b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/util/StringUtil.java new file mode 100644 index 0000000..7eb601e --- /dev/null +++ b/blackwidowpluginbukkit/src/main/java/io/github/arcaneplugins/blackwidow/plugin/bukkit/util/StringUtil.java @@ -0,0 +1,46 @@ +/* + * BlackWidow: Security modifications for Minecraft servers and proxies + * Copyright (c) 2024 lokka30. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.arcaneplugins.blackwidow.plugin.bukkit.util; + +import org.jetbrains.annotations.Nullable; + +/** + * {@link StringUtil} contains a variety of utilities related to {@link String Java String objects}. + * + * @author lokka30 + * @since 1.1.0 + */ +public final class StringUtil { + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isDouble( + @Nullable final String str + ) { + if (str == null || str.isBlank()) { + return false; + } + + try { + Double.parseDouble(str); + return true; + } catch (final NumberFormatException ex) { + return false; + } + } +} diff --git a/blackwidowpluginbukkit/src/main/resources/settings.yml b/blackwidowpluginbukkit/src/main/resources/settings.yml index 1a3923a..4d65d63 100644 --- a/blackwidowpluginbukkit/src/main/resources/settings.yml +++ b/blackwidowpluginbukkit/src/main/resources/settings.yml @@ -34,8 +34,14 @@ cmd-blocking: execution: true suggestion: true operators-bypass-completely: true +update-checker: + enabled: true + run-on-startup: true + repeat-timer-duration-mins: 60 + log-updates: true + notify-players-with-permission: true do-not-touch: version: context: "" - original: 1 - installed: 1 + original: 2 + installed: 2 diff --git a/settings.xml b/settings.xml index 474cabf..d4dc949 100644 --- a/settings.xml +++ b/settings.xml @@ -16,7 +16,8 @@ ~ along with this program. If not, see . --> - + github