diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 079884ce..2ff59349 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,3 +70,4 @@ jobs: with: name: failed-javadocs-options-file path: build/tmp/javadoc/javadoc.options + diff --git a/.gitignore b/.gitignore index 5459b6f0..2d5ec7c6 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ hs_err_pid* .gradle **/build/ !src/**/build/ +**/resources/generated/ # Ignore Gradle GUI config gradle-app.setting diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99f50ca6..49799c61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ This project is open for all developers for contribution, so it's important we e - No pushing to master without opening a pull request (The only exceptions are small hotfixes, any new features or such MUST go in a new branch) - Must be backwards compatible down to Java 11 -- Javadoc your code according to google's [Javadoc guidlines](https://google.github.io/styleguide/javaguide.html#s7-javadoc) +- Javadoc your code according to Google's [Javadoc guidlines](https://google.github.io/styleguide/javaguide.html#s7-javadoc) ## Pull Requests diff --git a/build.gradle b/build.gradle index 05b250c1..5b22d8a7 100644 --- a/build.gradle +++ b/build.gradle @@ -14,19 +14,21 @@ jacoco { } group = 'com.dumbdogdiner' - -version = '2.1.0' +version = '2.3.0' // License Plugin Options license { header = project.file('LICENSE_HEADER') ext.year = Calendar.getInstance().get(Calendar.YEAR) mapping("java", "SLASHSTAR_STYLE") + exclude("**/*.json") } tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-XDignore.symbol.file" + options.encoding = "UTF-8" } + // Run the license formatter before compiling the source code. tasks.compileJava.dependsOn licenseFormatMain, licenseFormatTest @@ -39,6 +41,7 @@ configurations { repositories { mavenCentral() jcenter() + google() maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } maven { url 'https://papermc.io/repo/repository/maven-public/' } @@ -50,10 +53,14 @@ dependencies { compileOnly 'com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT' compileOnly 'net.md-5:bungeecord-api:1.16-R0.5-SNAPSHOT' + implementation 'org.jetbrains:annotations:20.1.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'io.github.classgraph:classgraph:4.8.100' implementation 'com.github.seancfoley:ipaddress:5.3.3' + implementation 'com.squareup.okhttp3:okhttp:4.9.0' + implementation 'commons-validator:commons-validator:1.7' + implementation 'com.google.guava:guava:30.1-jre' // Tests - JUnit 5 testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0") @@ -61,6 +68,19 @@ dependencies { // Tests - Mocking Suite (eg. mocking Bukkit for tests) testImplementation("org.mockito:mockito-core:3.7.7") + + // Tests - Mocked Bukkit Project (Has some additional features) + testImplementation 'com.github.seeseemelk:MockBukkit-v1.16:0.5.0' + testImplementation 'it.unimi.dsi:fastutil:8.4.4' +} + +task downloadTextures(type: Download) { + sourceUrl = 'https://dumbdogdiner.github.io/mc-heads-resource/textures.json' + target = new File('src/main/resources/generated/textures.json') +} + +task cleanGenerated(type: Delete){ + delete('src/main/resources/generated') } test { @@ -70,6 +90,7 @@ test { // Show System.out for code ran by tests showStandardStreams = true } + //ignoreFailures = true finalizedBy jacocoTestReport // report is always generated after tests run } @@ -86,15 +107,22 @@ task sources(type: Jar, dependsOn: classes) { from sourceSets.main.allSource } +tasks.delombok.shouldRunAfter(sources) +tasks.publish.dependsOn build +tasks.build.shouldRunAfter(clean) +tasks.javadoc.shouldRunAfter(clean) +tasks.build.finalizedBy(sources) +tasks.clean.dependsOn(cleanGenerated) +tasks.processResources.dependsOn(downloadTextures) // Javadoc Fixes // Some environments (such as the builder image) do not use UTF-8 as the default encoding! -// This sets UTF-8 as the encoding for the following tasks: delombok, compileJava, compileTestJava and javadoc. -delombok.encoding = "UTF-8" - -compileJava.options.encoding = "UTF-8" -compileTestJava.options.encoding = "UTF-8" -javadoc.options.encoding = "UTF-8" +delombok { + finalizedBy(javadoc) + print(true) + encoding = "UTF-8" + //verbose(true) +} // Build Info @@ -127,10 +155,35 @@ task processSourceTokens(type: Sync) { } // Use the filter task as the input for compileJava compileJava.source = processSourceTokens.outputs +tasks.publish.dependsOn build, sources + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} +tasks.withType(Test) { + systemProperty "file.encoding", "UTF-8" +} +javadoc { + options.addBooleanOption('XDignore.symbol.file', true) + options.addBooleanOption('-frames', true) + options.addBooleanOption('private', true) + test.ignoreFailures true; + options.encoding = 'UTF-8' + dependsOn delombok +} -tasks.publish.dependsOn build, sources +task browseJavadoc { + dependsOn javadoc + doLast { + java.awt.Desktop.desktop.browse new URI(("file:///" << System.getProperty("user.dir").replace('\\','/') << "/build/docs/javadoc/index.html").toString()) + } +} +task rebuild { + dependsOn clean + finalizedBy build +} @@ -152,3 +205,16 @@ publishing { } } } + +class Download extends DefaultTask { + @Input + String sourceUrl + + @OutputFile + File target + + @TaskAction + void download() { + ant.get(src: sourceUrl, dest: target) + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..b8e93595 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +systemProp.file.encoding=utf-8 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8988d1ba..2a563242 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -compileJava.options.encoding=UTF-8 \ No newline at end of file diff --git a/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java b/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java index dd98ddce..dc9ef778 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/StickyAPI.java @@ -4,6 +4,11 @@ */ package com.dumbdogdiner.stickyapi; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; + +import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -11,9 +16,6 @@ import java.util.concurrent.Executors; import java.util.logging.Logger; -import lombok.Getter; -import lombok.Setter; - /** *

StickyAPI

Utility methods, classes and potentially * code-dupe-annihilating code for DDD plugins. @@ -29,7 +31,16 @@ private StickyAPI() { @Getter @Setter - private static ExecutorService pool = Executors.newCachedThreadPool(); + private static @NotNull ExecutorService pool = Executors.newCachedThreadPool(); + + /** + * Provides a wrapper for {@link java.lang.Class#getResourceAsStream(String)} (String)} + * @param resourceName The resource to get + * @return an {@link InputStream} to that resource + */ + public static InputStream getResourceAsStream(@NotNull String resourceName){ + return StickyAPI.class.getResourceAsStream(resourceName); + } // Build Info Start @@ -38,8 +49,8 @@ private StickyAPI() { * * @since TBA * @return {@link String} version - * */ + @SuppressWarnings("JavaDoc") @Getter private static final String version = "@BUILDINFO_VERSION"; @@ -55,6 +66,7 @@ private StickyAPI() { * @since TBA * @return {@link String} commit id */ + @SuppressWarnings("JavaDoc") @Getter private static final String commit = "@BUILDINFO_COMMIT@"; @@ -64,6 +76,7 @@ private StickyAPI() { * @since TBA * @return {@link String} branch name */ + @SuppressWarnings("JavaDoc") @Getter private static final String branch = "@BUILDINFO_BRANCH@"; @@ -80,8 +93,7 @@ private StickyAPI() { public static Date getTimestamp() { SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); try { - Date date = formatter.parse(timestamp); - return date; + return formatter.parse(timestamp); } catch (ParseException e) { e.printStackTrace(); return null; @@ -106,6 +118,7 @@ public static String getSha() { * @since TBA * @return {@link Boolean} isDirty */ + @SuppressWarnings("ConstantConditions") public static Boolean getIsDirty() { return Boolean.parseBoolean(isDirty); } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/annotation/DoNotCall.java b/src/main/java/com/dumbdogdiner/stickyapi/annotation/DoNotCall.java new file mode 100644 index 00000000..1c5bde49 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/annotation/DoNotCall.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.annotation; + +import java.lang.annotation.Documented; + + +/** + * Do not call a method annotated with this, it will do bad things + */ +@Documented +public @interface DoNotCall { + +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/AsyncCommand.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/AsyncCommand.java index 304a6d72..10d06516 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/AsyncCommand.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/AsyncCommand.java @@ -30,7 +30,7 @@ * * @deprecated Use * {@link com.dumbdogdiner.stickyapi.bukkit.command.BukkitCommandBuilder} - * as this will be removed in the next release + * as this will be removed in a future release */ @Deprecated public abstract class AsyncCommand extends Command implements PluginIdentifiableCommand { @@ -43,7 +43,7 @@ public abstract class AsyncCommand extends Command implements PluginIdentifiable * @param commandName The name of the command the user will execute * @param owner The plugin that owns this command. */ - public AsyncCommand(String commandName, Plugin owner) { + public AsyncCommand(@NotNull String commandName, Plugin owner) { super(commandName); this.owner = owner; } @@ -60,7 +60,7 @@ public AsyncCommand(String commandName, Plugin owner) { // public abstract int executeCommand(Sender sender, String commandLabel, // String[] args); - public abstract ExitCode executeCommand(CommandSender sender, String commandLabel, String[] args); + public abstract @NotNull ExitCode executeCommand(CommandSender sender, String commandLabel, String[] args); /** * This is a vastly simplified command class. We only check if the plugin is @@ -77,17 +77,17 @@ public AsyncCommand(String commandName, Plugin owner) { * @return {@link ExitCode} */ @Override - public final boolean execute(CommandSender sender, String commandLabel, String[] args) { + public final boolean execute(@NotNull CommandSender sender, String commandLabel, String[] args) { if (!this.owner.isEnabled()) throw new CommandException(String.format("Cannot execute command \"%s\" in plugin %s - plugin is disabled.", commandLabel, this.owner.getDescription().getFullName())); - AsyncCommand self = this; - FutureTask t = new FutureTask<>(new Callable() { + @NotNull AsyncCommand self = this; + @NotNull FutureTask t = new FutureTask<>(new Callable() { @Override - public Boolean call() { + public @NotNull Boolean call() { try { - ExitCode resultingExitCode = self.executeCommand(sender, commandLabel, args); + @NotNull ExitCode resultingExitCode = self.executeCommand(sender, commandLabel, args); if (resultingExitCode == null) { throw new IllegalArgumentException("A null exit code was returned"); @@ -116,7 +116,7 @@ public Boolean call() { * @return Plugin that owns this command */ @Override - public Plugin getPlugin() { + public @NotNull Plugin getPlugin() { return this.owner; } @@ -181,16 +181,16 @@ public void setTabCompleter(TabCompleter completer) { */ @Override public java.util.@NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, - String[] args) throws CommandException, IllegalArgumentException { + String @org.jetbrains.annotations.Nullable [] args) throws CommandException, IllegalArgumentException { if (args == null) throw new NullPointerException("arguments to tabComplete cannot be null"); - List completions = null; + @org.jetbrains.annotations.Nullable List completions = null; try { if (completer != null) completions = completer.onTabComplete(sender, this, alias, args); } catch (Throwable ex) { - StringBuilder message = new StringBuilder(); + @NotNull StringBuilder message = new StringBuilder(); message.append("Unhandled exception during tab completion for command '/").append(alias).append(' '); for (String arg : args) message.append(arg).append(' '); @@ -212,8 +212,8 @@ public void setTabCompleter(TabCompleter completer) { * @return the human readable name of the class */ @Override - public String toString() { - StringBuilder stringBuilder = new StringBuilder(super.toString()); + public @NotNull String toString() { + @NotNull StringBuilder stringBuilder = new StringBuilder(super.toString()); stringBuilder.deleteCharAt(stringBuilder.length() - 1); stringBuilder.append(", ").append(owner.getDescription().getFullName()).append(')'); return stringBuilder.toString(); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/BukkitCommandBuilder.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/BukkitCommandBuilder.java index fdfa9524..09abf0d5 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/BukkitCommandBuilder.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/BukkitCommandBuilder.java @@ -4,32 +4,24 @@ */ package com.dumbdogdiner.stickyapi.bukkit.command; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.FutureTask; - import com.dumbdogdiner.stickyapi.StickyAPI; import com.dumbdogdiner.stickyapi.bukkit.util.SoundUtil; +import com.dumbdogdiner.stickyapi.common.ServerVersion; import com.dumbdogdiner.stickyapi.common.arguments.Arguments; import com.dumbdogdiner.stickyapi.common.command.CommandBuilder; import com.dumbdogdiner.stickyapi.common.command.ExitCode; -import com.dumbdogdiner.stickyapi.common.ServerVersion; import com.dumbdogdiner.stickyapi.common.util.NotificationType; import com.dumbdogdiner.stickyapi.common.util.reflection.ReflectionUtil; import com.dumbdogdiner.stickyapi.common.util.StringUtil; import com.google.common.collect.ImmutableList; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandMap; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; +import org.bukkit.command.*; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.FutureTask; /** * CommandBuilder for avoiding bukkit's terrible command API and making creating @@ -40,7 +32,7 @@ public class BukkitCommandBuilder extends CommandBuilder { // Hmm... - HashMap cooldownSenders = new HashMap<>(); + @NotNull HashMap cooldownSenders = new HashMap<>(); Executor executor; TabExecutor tabExecutor; @@ -50,11 +42,11 @@ public class BukkitCommandBuilder extends CommandBuilder { @FunctionalInterface public interface Executor { - public ExitCode apply(CommandSender sender, Arguments args, HashMap vars); + public @NotNull ExitCode apply(CommandSender sender, Arguments args, HashMap vars); } public interface TabExecutor { - public java.util.List apply(CommandSender sender, String commandLabel, Arguments args); + public java.util.@NotNull List apply(CommandSender sender, String commandLabel, Arguments args); } public interface ErrorHandler { @@ -77,8 +69,8 @@ public BukkitCommandBuilder(@NotNull String name, @NotNull Plugin owner) { this.owner = owner; } - private void performAsynchronousExecution(CommandSender sender, org.bukkit.command.Command command, String label, - List args) { + private void performAsynchronousExecution(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command command, String label, + @NotNull List args) { StickyAPI.getPool().execute(new FutureTask(() -> { performExecution(sender, command, label, args); return null; @@ -89,18 +81,18 @@ private void performAsynchronousExecution(CommandSender sender, org.bukkit.comma * Execute this command. Checks for existing sub-commands, and runs the error * handler if anything goes wrong. */ - private void performExecution(CommandSender sender, org.bukkit.command.Command command, String label, - List args) { + private void performExecution(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command command, String label, + @NotNull List args) { // look for subcommands if (args.size() > 0 && getSubCommands().containsKey(args.get(0))) { - BukkitCommandBuilder subCommand = (BukkitCommandBuilder) getSubCommands().get(args.get(0)); + @NotNull BukkitCommandBuilder subCommand = (BukkitCommandBuilder) getSubCommands().get(args.get(0)); if (!getSynchronous() && subCommand.getSynchronous()) { throw new RuntimeException("Attempted to asynchronously execute a synchronous sub-command!"); } // We can't modify List, so we need to make a clone of it, because java is // special. - ArrayList argsClone = new ArrayList(args); + @NotNull ArrayList argsClone = new ArrayList(args); argsClone.remove(0); // spawn async command from sync @@ -114,8 +106,8 @@ private void performExecution(CommandSender sender, org.bukkit.command.Command c } ExitCode exitCode; - Arguments a = new Arguments(args); - var variables = new HashMap(); + @NotNull Arguments a = new Arguments(args); + @NotNull var variables = new HashMap(); variables.put("command", command.getName()); variables.put("sender", sender.getName()); variables.put("player", sender.getName()); @@ -168,7 +160,7 @@ private void performExecution(CommandSender sender, org.bukkit.command.Command c * @param executor to set * @return {@link CommandBuilder} */ - public BukkitCommandBuilder onExecute(@NotNull Executor executor) { + public @NotNull BukkitCommandBuilder onExecute(@NotNull Executor executor) { this.executor = executor; return this; } @@ -179,7 +171,7 @@ public BukkitCommandBuilder onExecute(@NotNull Executor executor) { * @param executor to set * @return {@link CommandBuilder} */ - public BukkitCommandBuilder onTabComplete(@NotNull TabExecutor executor) { + public @NotNull BukkitCommandBuilder onTabComplete(@NotNull TabExecutor executor) { this.tabExecutor = executor; return this; } @@ -190,7 +182,7 @@ public BukkitCommandBuilder onTabComplete(@NotNull TabExecutor executor) { * @param handler to set * @return {@link CommandBuilder} */ - public BukkitCommandBuilder onError(@NotNull ErrorHandler handler) { + public @NotNull BukkitCommandBuilder onError(@NotNull ErrorHandler handler) { this.errorHandler = handler; return this; } @@ -201,8 +193,8 @@ public BukkitCommandBuilder onError(@NotNull ErrorHandler handler) { * @param plugin to build it for * @return {@link org.bukkit.command.Command} */ - public org.bukkit.command.Command build(@NotNull Plugin plugin) { - PluginCommand command = new PluginCommand(this.getName(), plugin); + public org.bukkit.command.@NotNull Command build(@NotNull Plugin plugin) { + @NotNull PluginCommand command = new PluginCommand(this.getName(), plugin); if (this.getSynchronous() == null) { this.synchronous(false); @@ -227,7 +219,7 @@ public boolean onCommand(CommandSender sender, org.bukkit.command.Command comman command.setTabCompleter(new TabCompleter() { @Override - public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + public List onTabComplete(CommandSender sender, Command command, String alias, String @NotNull [] args) { if (tabExecutor == null) { if (args.length == 0) { return ImmutableList.of(); @@ -235,11 +227,11 @@ public List onTabComplete(CommandSender sender, Command command, String String lastWord = args[args.length - 1]; - Player senderPlayer = sender instanceof Player ? (Player) sender : null; + @NotNull Player senderPlayer = sender instanceof Player ? (Player) sender : null; - ArrayList matchedPlayers = new ArrayList(); - for (Player player : sender.getServer().getOnlinePlayers()) { - String name = player.getName(); + @NotNull ArrayList matchedPlayers = new ArrayList(); + for (@NotNull Player player : sender.getServer().getOnlinePlayers()) { + @NotNull String name = player.getName(); if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) { matchedPlayers.add(name); @@ -268,7 +260,7 @@ public List onTabComplete(CommandSender sender, Command command, String * * @return {@link org.bukkit.command.Command} */ - public org.bukkit.command.Command build() throws NullPointerException { + public org.bukkit.command.@NotNull Command build() throws NullPointerException { if (this.owner == null) { throw new NullPointerException("Owning plugin is null, did you construct this object without an owner?"); } @@ -304,7 +296,7 @@ public void register() { this.register(this.owner); } - private void _playSound(CommandSender sender, NotificationType type) { + private void _playSound(@NotNull CommandSender sender, @NotNull NotificationType type) { if (!this.getPlaySound()) return; SoundUtil.send(sender, type); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/ExitCode.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/ExitCode.java index 828533a9..feb740e9 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/ExitCode.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/ExitCode.java @@ -5,13 +5,14 @@ package com.dumbdogdiner.stickyapi.bukkit.command; import lombok.Getter; +import org.jetbrains.annotations.NotNull; /** * Enum based exit codes for StickyAPI command classes. * * @deprecated use {@link com.dumbdogdiner.stickyapi.common.command.ExitCode} as - * this will be removed in the next release - * @see ExitCode {@link com.dumbdogdiner.stickyapi.common.command.ExitCode} + * this will be removed in a future release + * @see com.dumbdogdiner.stickyapi.common.command.ExitCode */ @Deprecated public enum ExitCode { @@ -86,7 +87,7 @@ public enum ExitCode { * @param message * @return {@link ExitCode} */ - public ExitCode setMessage(String message) { + public @NotNull ExitCode setMessage(String message) { this.message = message; return this; } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/PluginCommand.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/PluginCommand.java index 89ad6109..5f213ee3 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/PluginCommand.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/PluginCommand.java @@ -26,9 +26,9 @@ */ // We don't want this class to be used anywhere outside of this package... final class PluginCommand extends Command implements PluginIdentifiableCommand { - private final Plugin owningPlugin; + private final @NotNull Plugin owningPlugin; private CommandExecutor executor; - private TabCompleter completer; + private @Nullable TabCompleter completer; public PluginCommand(@NotNull String name, @NotNull Plugin owner) { super(name); @@ -68,7 +68,7 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLab } if (!success && usageMessage.length() > 0) { - for (String line : usageMessage.replace("", commandLabel).split("\n")) { + for (@NotNull String line : usageMessage.replace("", commandLabel).split("\n")) { sender.sendMessage(line); } } @@ -148,12 +148,12 @@ public Plugin getPlugin() { @NotNull @Override public java.util.List tabComplete(@NotNull CommandSender sender, @NotNull String alias, - @NotNull String[] args) throws CommandException, IllegalArgumentException { + @NotNull String @NotNull [] args) throws CommandException, IllegalArgumentException { Validate.notNull(sender, "Sender cannot be null"); Validate.notNull(args, "Arguments cannot be null"); Validate.notNull(alias, "Alias cannot be null"); - List completions = null; + @Nullable List completions = null; try { if (completer != null) { completions = completer.onTabComplete(sender, this, alias, args); @@ -162,7 +162,7 @@ public java.util.List tabComplete(@NotNull CommandSender sender, @NotNul completions = ((TabCompleter) executor).onTabComplete(sender, this, alias, args); } } catch (Throwable ex) { - StringBuilder message = new StringBuilder(); + @NotNull StringBuilder message = new StringBuilder(); message.append("Unhandled exception during tab completion for command '/").append(alias).append(' '); for (String arg : args) { message.append(arg).append(' '); @@ -179,8 +179,8 @@ public java.util.List tabComplete(@NotNull CommandSender sender, @NotNul } @Override - public String toString() { - StringBuilder stringBuilder = new StringBuilder(super.toString()); + public @NotNull String toString() { + @NotNull StringBuilder stringBuilder = new StringBuilder(super.toString()); stringBuilder.deleteCharAt(stringBuilder.length() - 1); stringBuilder.append(", ").append(owningPlugin.getDescription().getFullName()).append(')'); return stringBuilder.toString(); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/package-info.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/package-info.java index 2eec728e..147bcfbf 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/package-info.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/command/package-info.java @@ -2,7 +2,4 @@ * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. * Licensed under the MIT license, see LICENSE for more information... */ -/** - * Classes dedicated to building Bukkit commands - */ package com.dumbdogdiner.stickyapi.bukkit.command; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/VoidGenerator.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/VoidGenerator.java index 8403386a..101232ef 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/VoidGenerator.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/VoidGenerator.java @@ -4,19 +4,20 @@ */ package com.dumbdogdiner.stickyapi.bukkit.generator; -import java.util.Random; - import org.bukkit.World; import org.bukkit.generator.ChunkGenerator; import org.jetbrains.annotations.NotNull; +import java.util.Random; + /** * A class for generating empty void worlds */ +@SuppressWarnings("unused") public class VoidGenerator extends ChunkGenerator { @Override - public ChunkData generateChunkData(@NotNull World world, Random random, int x, int z, BiomeGrid biome) { + public @NotNull ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome) { return createChunkData(world); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/package-info.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/package-info.java index d9a67388..d3cd312e 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/package-info.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/generator/package-info.java @@ -2,7 +2,4 @@ * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. * Licensed under the MIT license, see LICENSE for more information... */ -/** - * Classes dedicated to Minecraft world generation - */ package com.dumbdogdiner.stickyapi.bukkit.generator; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/ClickableSlot.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/ClickableSlot.java index cfb0629e..f8ef482b 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/ClickableSlot.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/ClickableSlot.java @@ -37,12 +37,12 @@ public class ClickableSlot { * @param y The y position of the item * @param lore (Optional) Add lore to this item */ - public ClickableSlot(@NotNull Material material, int amount, @Nullable String name, int x, int y, String... lore) { + public ClickableSlot(@NotNull Material material, int amount, @Nullable String name, int x, int y, String @NotNull ... lore) { this(makeItem(material, amount, name, lore), x, y); } - private static ItemStack makeItem(Material material, int amount, String name, String[] lore) { - ItemStack item = new ItemStack(material, amount); + private static @NotNull ItemStack makeItem(@NotNull Material material, int amount, @Nullable String name, String @NotNull [] lore) { + @NotNull ItemStack item = new ItemStack(material, amount); ItemMeta meta = item.getItemMeta(); @@ -50,9 +50,9 @@ private static ItemStack makeItem(Material material, int amount, String name, St meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name)); } - ArrayList metaLore = new ArrayList<>(); + @NotNull ArrayList metaLore = new ArrayList<>(); - for (String loreComments : lore) { + for (@NotNull String loreComments : lore) { metaLore.add(ChatColor.translateAlternateColorCodes('&', loreComments)); } @@ -85,7 +85,7 @@ public void setName(@NotNull String s) { item.setItemMeta(isM); } - public String getName() { + public @NotNull String getName() { return item.getItemMeta().getDisplayName(); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/GUI.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/GUI.java index c07a3f9d..5163c829 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/GUI.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/GUI.java @@ -9,7 +9,9 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.inventory.*; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; @@ -138,15 +140,15 @@ public void addSlot(@NotNull ClickableSlot cs, @Nullable String tag, addSlot(cs.getX(), cs.getY(), cs.getItem(), tag, action); } - public void addSlot(ClickableSlot cs, String tag) { + public void addSlot(@NotNull ClickableSlot cs, String tag) { addSlot(cs, tag, null); } - public void addSlot(ClickableSlot cs, BiConsumer action) { + public void addSlot(@NotNull ClickableSlot cs, BiConsumer action) { addSlot(cs, null, action); } - public void addSlot(ClickableSlot cs) { + public void addSlot(@NotNull ClickableSlot cs) { addSlot(cs, null, null); } @@ -180,18 +182,18 @@ public void removeSlot(int x, int y) { * * @param player The player who will see this GUI */ - public void open(Player player) { - var self = this; + public void open(@NotNull Player player) { + @NotNull var self = this; Bukkit.getPluginManager().registerEvents(new Listener() { @EventHandler - public void onInventoryOpen(InventoryOpenEvent event) { + public void onInventoryOpen(@NotNull InventoryOpenEvent event) { if (event.getInventory() == inventory) { self.onInventoryOpen(event); } } @EventHandler - public void onInventoryClick(InventoryClickEvent event) { + public void onInventoryClick(@NotNull InventoryClickEvent event) { if (event.getClickedInventory() == inventory) { event.setCancelled(true); // only allow basic clicks, other clicks might allow players to smuggle gui @@ -205,10 +207,10 @@ public void onInventoryClick(InventoryClickEvent event) { } var slot = slots.get(event.getSlot()); - String tag = null; + @Nullable String tag = null; if (slot != null) { tag = slot.getTag(); - var action = slot.getAction(); + @Nullable var action = slot.getAction(); if (action != null) { action.accept(event, self); } @@ -218,7 +220,7 @@ public void onInventoryClick(InventoryClickEvent event) { } @EventHandler - public void onInventoryClose(InventoryCloseEvent event) { + public void onInventoryClose(@NotNull InventoryCloseEvent event) { if (event.getInventory() == inventory) { self.onInventoryClose(event); InventoryOpenEvent.getHandlerList().unregister(this); @@ -266,11 +268,11 @@ protected void onInventoryClose(@NotNull InventoryCloseEvent event) { // custom setter for inventory name, as inv name normally cannot be changed public void setName(@NotNull String name) { - var viewers = new ArrayList<>(inventory.getViewers()); + @NotNull var viewers = new ArrayList<>(inventory.getViewers()); var contents = inventory.getContents(); inventory = Bukkit.createInventory(null, ROW_LENGTH * rows, name); inventory.setContents(contents); - for (var viewer : viewers) { + for (@NotNull var viewer : viewers) { viewer.openInventory(inventory); } this.name = name; @@ -282,8 +284,8 @@ public void setName(@NotNull String name) { * @return A copy of this GUI with no viewers. */ public @NotNull GUI duplicate() { - var gui = new GUI(rows, name, plugin); - for (var slot : slots.entrySet()) { + @NotNull var gui = new GUI(rows, name, plugin); + for (@NotNull var slot : slots.entrySet()) { int index = slot.getKey(); gui.addSlot(index % 9, index / 9, slot.getValue().duplicate()); } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/package-info.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/package-info.java index 654297d6..6c67dea4 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/package-info.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/gui/package-info.java @@ -2,8 +2,4 @@ * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. * Licensed under the MIT license, see LICENSE for more information... */ -/** - * Classes for building Bukkit inventory GUIs - */ -// TODO: Rewrite the GUI API package com.dumbdogdiner.stickyapi.bukkit.gui; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/item/PlayerHeadBuilder.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/item/PlayerHeadBuilder.java new file mode 100644 index 00000000..e762942b --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/item/PlayerHeadBuilder.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.bukkit.item; + +import com.destroystokyo.paper.profile.PlayerProfile; +import com.destroystokyo.paper.profile.ProfileProperty; +import com.dumbdogdiner.stickyapi.annotation.DoNotCall; +import com.dumbdogdiner.stickyapi.common.webapis.mojang.CachedMojangAPI; +import com.google.common.base.Preconditions; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.UUID; + +/** + * This class provides an easy way to construct Player Heads from existing players, whether online or offline + */ +public class PlayerHeadBuilder extends SkullBuilder { + private @NotNull SkullMeta meta = (SkullMeta) (new ItemStack(Material.PLAYER_HEAD, 1)).getItemMeta(); + private final UUID playerId; + private final @NotNull PlayerProfile ownerProfile; + private boolean frozen; + + + /** + * @param playerId the UUID of the player whose head should be generated + */ + public PlayerHeadBuilder(@NotNull UUID playerId) { + this.meta.setOwningPlayer(Bukkit.getOfflinePlayer(playerId)); + this.ownerProfile = Bukkit.getServer().createProfile(playerId); + Preconditions.checkNotNull(playerId); + this.playerId = playerId; + } + + /** + * @param player The player to use for head generation + */ + public PlayerHeadBuilder(@NotNull Player player) { + this.meta.setOwningPlayer(player); + this.ownerProfile = player.getPlayerProfile(); + playerId = player.getUniqueId(); + } + + /** + * @param player The Offline Player to use for head generation + */ + public PlayerHeadBuilder(@NotNull OfflinePlayer player) { + this.meta.setOwningPlayer(player); + this.ownerProfile = Bukkit.createProfile(player.getUniqueId()); + this.playerId = player.getUniqueId(); + } + + /** + * Creates a {@link PlayerHeadBuilder} from an existing head + * + * @param head an existing playerhead + * @throws IllegalArgumentException if the {@link ItemStack} is not a head, has bad metadata, or the profile attached is invalid + */ + public PlayerHeadBuilder(@NotNull ItemStack head) { + Preconditions.checkArgument(head.getType() != Material.PLAYER_HEAD + && head.getType() != Material.PLAYER_WALL_HEAD, + "Head must be a player head or player wall head"); + meta = (SkullMeta) head.getItemMeta(); + Preconditions.checkNotNull(meta, "Player head must have metadata attached"); + // We check this almost immediately after, so it's OK if it's null temporarily + //noinspection ConstantConditions + ownerProfile = meta.getPlayerProfile(); + name(meta.getDisplayName()); + Preconditions.checkNotNull(ownerProfile, "The player head must have a PlayerProfile attached"); + if (!Objects.requireNonNull(ownerProfile).hasTextures()) { + if (!ownerProfile.complete()) { + throw new IllegalArgumentException("Invalid player profile attached to the head, with no UUID or textures!"); + } + } + this.playerId = null; + } + + /** + * Statically sets the texture of the head so it will not update in the future + */ + public @NotNull PlayerHeadBuilder freeze() { + if (playerId != null) { + String textureString = CachedMojangAPI.getTextureString(playerId); + assert textureString != null; + super.texture(textureString); + frozen = true; + } + return this; + } + + /** + * Constructs a new {@link ItemStack} of type {@link Material#PLAYER_HEAD} based off the specified player + * + * @return A new player head {@link ItemStack} + */ + @Override + public @NotNull ItemStack build() { + meta.setDisplayName(Objects.requireNonNullElseGet(name, () -> CachedMojangAPI.getUsername(playerId) + "'s Head")); + + if (frozen) { + ownerProfile.setProperty(new ProfileProperty("texture", texture)); + } + @NotNull ItemStack head = new ItemStack(Material.PLAYER_HEAD, quantity); + head.setItemMeta(meta); + + return head; + } + + + // Disable methods of superclass: + + /** + * This is unsupported. + * + * @throws UnsupportedOperationException if ran + */ + @DoNotCall + @Override + @Deprecated + public @NotNull SkullBuilder texture(@NotNull String str) { + throw new UnsupportedOperationException(); + } + + /** + * This is unsupported. + * + * @throws UnsupportedOperationException if ran + */ + @DoNotCall + @Override + @Deprecated + public @NotNull SkullBuilder head(@NotNull String str) { + throw new UnsupportedOperationException(); + } + + /** + * This is unsupported. + * + * @throws UnsupportedOperationException if ran + */ + @DoNotCall + @Override + @Deprecated + public @NotNull SkullBuilder texture(java.net.@NotNull URL url) { + throw new UnsupportedOperationException(); + } + + /** + * This is unsupported. + * + * @throws UnsupportedOperationException if ran + */ + @DoNotCall + @Deprecated + @Override + public @NotNull SkullBuilder category(@NotNull String str) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/item/SkullBuilder.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/item/SkullBuilder.java new file mode 100644 index 00000000..3889ac9f --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/item/SkullBuilder.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.bukkit.item; + +import com.destroystokyo.paper.profile.PlayerProfile; +import com.destroystokyo.paper.profile.ProfileProperty; +import com.dumbdogdiner.stickyapi.common.util.StringUtil; +import com.dumbdogdiner.stickyapi.common.util.textures.TextureHelper; +import com.dumbdogdiner.stickyapi.common.util.textures.TextureValidator; +import com.google.common.base.Preconditions; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.VisibleForTesting; + +import java.net.URL; +import java.util.UUID; + +/** + * Utility for constructing ItemStacks of {@link Material#PLAYER_HEAD}. + *

+ * You can specify a group and name from the embedded texture file, or simply set a name and a texture, + * and generate player heads with ease! + *

+ */ + +@SuppressWarnings("UnusedReturnValue") +public class SkullBuilder { + @Getter + @NotNull + @VisibleForTesting + private String category = "*"; + @Getter + @VisibleForTesting + protected int quantity = 1; + @Getter + private String head; + + + @SuppressWarnings("DanglingJavadoc") // For lombok + @Accessors(fluent = true, chain = true) + @Setter + @Getter + /** + * @param name The displayed name of the head + * @return The displayed name of the head + */ + protected String name; + @Getter + protected String texture; + + /** + * Sets the category of the head type (Defaults to *, which is first match) + * + * @param category The category to set + * @throws IllegalArgumentException if the category is not valid + * @see TextureHelper#getCategories() + */ + public @NotNull SkullBuilder category(@NotNull String category) { + Preconditions.checkArgument(TextureHelper.getCategories().contains(category.toUpperCase()), + "The specified group %s is not a valid category", category); + this.category = category.toUpperCase(); + return this; + } + + /** + * Sets the specific head (from textures.yml) + * + * @param head The head to set + * @throws IllegalArgumentException if the head does not exist + * @see TextureHelper#getTexture(String, String) + */ + public @NotNull SkullBuilder head(@NotNull String head) { + head = head.toUpperCase(); + Preconditions.checkNotNull(TextureHelper.getTexture(category, head), + "The specified head %s is not a valid head", head); + this.head = head; + this.texture = TextureHelper.getTexture(category, head); + return this; + } + + + /** + * Sets the quantity of items in the final item stack + * + * @param i the requested quantity + * @throws IllegalArgumentException if the stack size is invalid + */ + public @NotNull SkullBuilder quantity(int i) { + Preconditions.checkArgument(i >= 0 && i <= 64, + "Invalid stack size of %s specified (must be between 0 and 64, inclusive)", i); + this.quantity = i; + return this; + } + + + /** + * @param textureURL A {@link URL} where a valid PNG minecraft texture can be found + * @return The current {@link SkullBuilder} instance + * @throws IllegalArgumentException if the URL is invalid + */ + public @NotNull SkullBuilder texture(@NotNull URL textureURL) { + TextureValidator.validateTextureUrl(textureURL.toExternalForm()); + + texture(TextureHelper.encodeTextureString(textureURL)); + return this; + } + + /** + * Set the texture with a pre-encoded string + * + * @param texture Base64 string of the json of texture location + * @throws IllegalArgumentException if the texture string is invalid + */ + public @NotNull SkullBuilder texture(@NotNull String texture) { + TextureValidator.validateTextureString(texture); + this.texture = texture; + return this; + } + + /** + * Builds the final {@link ItemStack} of {@link Material#PLAYER_HEAD} as long as + * + * @return The constructed head + */ + public @NotNull ItemStack build() { + Preconditions.checkNotNull(texture); + Preconditions.checkArgument(name != null || head != null); + + @NotNull SkullMeta meta = (SkullMeta) (new ItemStack(Material.PLAYER_HEAD, 1)).getItemMeta(); + @NotNull PlayerProfile profile = Bukkit.createProfile(new UUID(0, 0), head); + + profile.setName(TextureHelper.toQualifiedName(category, head == null ? name : head)); + if (name != null) { + meta.setDisplayName(name); + } else { + meta.setDisplayName(StringUtil.capitalize(head)); + } + + profile.setProperty(new ProfileProperty("textures", texture)); + meta.setPlayerProfile(profile); + @NotNull ItemStack head = new ItemStack(Material.PLAYER_HEAD, quantity); + head.setItemMeta(meta); + return head; + } + + public SkullBuilder qualified(String qualifiedName) { + String [] qn = qualifiedName.toUpperCase().split("\\."); + category(qn[0]); + return head(qn[1]); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/ParticleSystem.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/ParticleSystem.java index 28e7180e..36852e52 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/ParticleSystem.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/ParticleSystem.java @@ -166,7 +166,7 @@ public ParticleSystem spawn(@NotNull Particle particle, double x, double y, doub * @param count The number of particles to spawn * @return {@link ParticleSystem} */ - public ParticleSystem spawn(double x, double y, double z, int count) { + public @NotNull ParticleSystem spawn(double x, double y, double z, int count) { this.ensureDefaultParticle(); return this.spawn(this.particle, x, y, z, count, this.data); } @@ -214,7 +214,7 @@ public ParticleSystem spawnAbsolute(@NotNull Particle particle, double x, double * @param count The number of particles to spawn * @return {@link ParticleSystem} */ - public ParticleSystem spawnAbsolute(double x, double y, double z, int count) { + public @NotNull ParticleSystem spawnAbsolute(double x, double y, double z, int count) { this.ensureDefaultParticle(); return this.spawnAbsolute(this.particle, x, y, z, count, this.data); } @@ -527,7 +527,7 @@ public ParticleSystem lineAbsolute(Location a, Location b, double steps, int cou * @param data Data of the particles to spawn * @return {@link ParticleSystem} */ - public ParticleSystem shape(@NotNull Particle particle, @NotNull Shape shape, @Nullable Particle.DustOptions data) { + public @NotNull ParticleSystem shape(@NotNull Particle particle, @NotNull Shape shape, @Nullable Particle.DustOptions data) { this.ensureRelative(); shape.draw(this, particle, data); return this; @@ -540,7 +540,7 @@ public ParticleSystem shape(@NotNull Particle particle, @NotNull Shape shape, @N * @param shape The shape * @return {@link ParticleSystem} */ - public ParticleSystem shape(@NotNull Particle particle, @NotNull Shape shape) { + public @NotNull ParticleSystem shape(@NotNull Particle particle, @NotNull Shape shape) { return this.shape(particle, shape, null); } @@ -550,7 +550,7 @@ public ParticleSystem shape(@NotNull Particle particle, @NotNull Shape shape) { * @param shape The shape * @return {@link ParticleSystem} */ - public ParticleSystem shape(Shape shape) { + public @NotNull ParticleSystem shape(@NotNull Shape shape) { this.ensureDefaultParticle(); return this.shape(this.particle, shape, this.data); } @@ -577,7 +577,7 @@ public ParticleSystem shapeAbsolute(@NotNull Particle particle, @NotNull Shape s * @param shape The shape * @return {@link ParticleSystem} */ - public ParticleSystem shapeAbsolute(@NotNull Particle particle, @NotNull Shape shape) { + public @NotNull ParticleSystem shapeAbsolute(@NotNull Particle particle, @NotNull Shape shape) { return this.shapeAbsolute(particle, shape, null); } @@ -587,7 +587,7 @@ public ParticleSystem shapeAbsolute(@NotNull Particle particle, @NotNull Shape s * @param shape The shape * @return {@link ParticleSystem} */ - public ParticleSystem shapeAbsolute(@NotNull Shape shape) { + public @NotNull ParticleSystem shapeAbsolute(@NotNull Shape shape) { this.ensureDefaultParticle(); return this.shapeAbsolute(this.particle, shape, this.data); } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/shapes/Circle.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/shapes/Circle.java index 9ff214da..f2eec4eb 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/shapes/Circle.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/particle/shapes/Circle.java @@ -11,6 +11,7 @@ import org.bukkit.Particle; import org.bukkit.Particle.DustOptions; +import org.jetbrains.annotations.NotNull; /** * Draws a circle. @@ -37,7 +38,7 @@ public class Circle implements Shape { * @param r Size of the circle's radius. * @param orientation Orientation of the circle. */ - public Circle(double x, double y, double z, double r, Orientation orientation) { + public Circle(double x, double y, double z, double r, @NotNull Orientation orientation) { if (r > MAX_RADIUS) { throw new IllegalArgumentException("Tried to draw circle with absurd radius (>250)!"); } @@ -107,12 +108,12 @@ public double z(double t) { } @Override - public void draw(ParticleSystem system, Particle particle, DustOptions data) { + public void draw(@NotNull ParticleSystem system, @NotNull Particle particle, DustOptions data) { system.parametric(particle, this.parametric, 0, Math.PI * 2, Math.PI / (16 * r), 1, data); } @Override - public void drawAbsolute(ParticleSystem system, Particle particle, DustOptions data) { + public void drawAbsolute(@NotNull ParticleSystem system, @NotNull Particle particle, DustOptions data) { system.parametricAbsolute(particle, this.parametric, 0, Math.PI * 2, Math.PI / (16 * r), 1, data); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/player/PlayerSnapshot.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/player/PlayerSnapshot.java index 7fdc3d6c..7e6be4cb 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/player/PlayerSnapshot.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/player/PlayerSnapshot.java @@ -4,23 +4,29 @@ */ package com.dumbdogdiner.stickyapi.bukkit.player; +import lombok.Getter; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; -public class PlayerSnapshot { +import java.io.Serializable; +import java.time.Instant; - private Location location; - private ItemStack[] armor; - private ItemStack[] items; - private double health; - private int foodLevel; - private float saturation; - private float exhaustion; - private float exp; - private Vector velocity; +public class PlayerSnapshot implements Serializable { + + private final @NotNull Location location; + private final ItemStack[] armor; + private final ItemStack[] items; + private final double health; + private final int foodLevel; + private final float saturation; + private final float exhaustion; + private final float exp; + private final @NotNull Vector velocity; + @Getter + private final Instant savedAt; /** * Create a new {@link PlayerSnapshot}. @@ -34,16 +40,16 @@ public class PlayerSnapshot { * @param player the Player to snapshot */ public PlayerSnapshot(@NotNull Player player) { - location = player.getLocation(); - armor = player.getInventory().getArmorContents(); - items = player.getInventory().getContents(); - health = player.getHealth(); - foodLevel = player.getFoodLevel(); - saturation = player.getSaturation(); - exhaustion = player.getExhaustion(); - exp = player.getExp(); - velocity = player.getVelocity(); - + this.savedAt = Instant.now(); + this.location = player.getLocation(); + this.armor = player.getInventory().getArmorContents(); + this.items = player.getInventory().getContents(); + this.health = player.getHealth(); + this.foodLevel = player.getFoodLevel(); + this.saturation = player.getSaturation(); + this.exhaustion = player.getExhaustion(); + this.exp = player.getExp(); + this.velocity = player.getVelocity(); } /** diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtil.java index a309dc58..5248b822 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtil.java @@ -4,19 +4,17 @@ */ package com.dumbdogdiner.stickyapi.bukkit.util; -import javax.annotation.Nullable; - import com.destroystokyo.paper.Title; - import com.dumbdogdiner.stickyapi.common.translation.Translation; import com.dumbdogdiner.stickyapi.common.util.reflection.ReflectionUtil; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import net.md_5.bungee.api.chat.TextComponent; +import javax.annotation.Nullable; public class ServerUtil { private ServerUtil() { @@ -42,7 +40,7 @@ public static double[] getRecentTps() { * @param args The format arguments, if any */ public static void broadcastMessage(@NotNull String message, @Nullable String... args) { - for (Player player : Bukkit.getServer().getOnlinePlayers()) { + for (@NotNull Player player : Bukkit.getServer().getOnlinePlayers()) { player.sendMessage( new TextComponent(Translation.translateColors("&", String.format(message, (Object) args)))); } @@ -54,7 +52,7 @@ public static void broadcastMessage(@NotNull String message, @Nullable String... * @param title The {@link com.destroystokyo.paper.Title} to send */ public static void broadcastTitle(@NotNull Title title) { - for (Player player : Bukkit.getServer().getOnlinePlayers()) { + for (@NotNull Player player : Bukkit.getServer().getOnlinePlayers()) { player.sendTitle(title); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/SoundUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/SoundUtil.java index 03257dcc..991aeca7 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/SoundUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/SoundUtil.java @@ -25,7 +25,7 @@ private SoundUtil() { * @param sender {@link org.bukkit.command.CommandSender} The sender to validate * @return {@link java.lang.Boolean} */ - private static Boolean validate(CommandSender sender) { + private static @NotNull Boolean validate(CommandSender sender) { return sender instanceof Player; } @@ -100,11 +100,11 @@ public static void sendSuccess(@NotNull Player player) { * @param type {@link NotificationType} The type of sound * @return {@link java.lang.Boolean} */ - public static Boolean send(@NotNull CommandSender sender, @NotNull NotificationType type) { + public static @NotNull Boolean send(@NotNull CommandSender sender, @NotNull NotificationType type) { if (!validate(sender)) { return false; } - var player = (Player) sender; + @NotNull var player = (Player) sender; switch (type) { case ERROR: sendError(player); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/StartupUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/StartupUtil.java index beb5505b..902b8d10 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/StartupUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/StartupUtil.java @@ -52,7 +52,7 @@ public static boolean setupConfig(@NotNull JavaPlugin plugin) { * @param localeProvider The plugin's locale provider * @return False if something went wrong */ - public static LocaleProvider setupLocale(@NotNull JavaPlugin plugin, @Nullable LocaleProvider localeProvider) { + public static @org.jetbrains.annotations.Nullable LocaleProvider setupLocale(@NotNull JavaPlugin plugin, @Nullable LocaleProvider localeProvider) { localeProvider = new LocaleProvider(new File(plugin.getDataFolder(), "locale")); int loadedLocales = localeProvider.loadAllLocales(); boolean localeEnabled = localeProvider.setDefaultLocale("messages.en_us"); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/command/BungeeCommandBuilder.java b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/command/BungeeCommandBuilder.java index 4cbf15dc..16692305 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/command/BungeeCommandBuilder.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/command/BungeeCommandBuilder.java @@ -4,51 +4,45 @@ */ package com.dumbdogdiner.stickyapi.bungeecord.command; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.TreeMap; -import java.util.concurrent.FutureTask; - import com.dumbdogdiner.stickyapi.StickyAPI; import com.dumbdogdiner.stickyapi.bungeecord.packet.PacketRegistration; import com.dumbdogdiner.stickyapi.bungeecord.util.SoundUtil; import com.dumbdogdiner.stickyapi.common.arguments.Arguments; -import com.dumbdogdiner.stickyapi.common.command.ExitCode; import com.dumbdogdiner.stickyapi.common.command.CommandBuilder; +import com.dumbdogdiner.stickyapi.common.command.ExitCode; import com.dumbdogdiner.stickyapi.common.util.NotificationType; import com.dumbdogdiner.stickyapi.common.util.StringUtil; import com.google.common.collect.ImmutableList; - -import org.jetbrains.annotations.NotNull; - import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.FutureTask; @SuppressWarnings("deprecation") // PacketRegistration is deprecated public class BungeeCommandBuilder extends CommandBuilder { // Hmm... - HashMap cooldownSenders = new HashMap<>(); + @NotNull HashMap cooldownSenders = new HashMap<>(); Executor executor; TabExecutor tabExecutor; ErrorHandler errorHandler; - Boolean playSound = false; + @NotNull Boolean playSound = false; @FunctionalInterface public interface Executor { - public ExitCode apply(CommandSender sender, Arguments args, TreeMap vars); + public @NotNull ExitCode apply(CommandSender sender, Arguments args, TreeMap vars); } public interface TabExecutor { - public java.util.List apply(CommandSender sender, String commandLabel, Arguments args); + public java.util.@NotNull List apply(CommandSender sender, String commandLabel, Arguments args); } public interface ErrorHandler { @@ -66,7 +60,7 @@ public interface ErrorHandler { */ @Override @Deprecated - public BungeeCommandBuilder playSound(@NotNull Boolean playSound) { + public @NotNull BungeeCommandBuilder playSound(@NotNull Boolean playSound) { this.playSound = playSound; PacketRegistration.registerSoundPacket(); // Register our sound packet, since this probably hasn't happened // already and if we don't, sounds will not play @@ -83,7 +77,7 @@ public BungeeCommandBuilder playSound(@NotNull Boolean playSound) { */ @Override @Deprecated - public BungeeCommandBuilder playSound() { + public @NotNull BungeeCommandBuilder playSound() { return this.playSound(true); } @@ -91,8 +85,8 @@ public BungeeCommandBuilder(@NotNull String name) { super(name); } - private void performAsynchronousExecution(CommandSender sender, BungeeCommandBuilder builder, String label, - List args) { + private void performAsynchronousExecution(@NotNull CommandSender sender, @NotNull BungeeCommandBuilder builder, String label, + @NotNull List args) { StickyAPI.getPool().execute(new FutureTask(() -> { performExecution(sender, builder, label, args); return null; @@ -103,17 +97,17 @@ private void performAsynchronousExecution(CommandSender sender, BungeeCommandBui * Execute this command. Checks for existing sub-commands, and runs the error * handler if anything goes wrong. */ - private void performExecution(CommandSender sender, BungeeCommandBuilder builder, String label, List args) { + private void performExecution(@NotNull CommandSender sender, @NotNull BungeeCommandBuilder builder, String label, @NotNull List args) { // look for subcommands if (args.size() > 0 && getSubCommands().containsKey(args.get(0))) { - BungeeCommandBuilder subCommand = (BungeeCommandBuilder) getSubCommands().get(args.get(0)); + @NotNull BungeeCommandBuilder subCommand = (BungeeCommandBuilder) getSubCommands().get(args.get(0)); if (!getSynchronous() && subCommand.getSynchronous()) { throw new RuntimeException("Attempted to asynchronously execute a synchronous sub-command!"); } // We can't modify List, so we need to make a clone of it, because java is // special. - ArrayList argsClone = new ArrayList(args); + @NotNull ArrayList argsClone = new ArrayList(args); argsClone.remove(0); // spawn async command from sync @@ -127,8 +121,8 @@ private void performExecution(CommandSender sender, BungeeCommandBuilder builder } ExitCode exitCode; - Arguments a = new Arguments(args); - var variables = new TreeMap(); + @NotNull Arguments a = new Arguments(args); + @NotNull var variables = new TreeMap(); variables.put("command", builder.getName()); variables.put("sender", sender.getName()); variables.put("player", sender.getName()); @@ -181,7 +175,7 @@ private void performExecution(CommandSender sender, BungeeCommandBuilder builder * @param executor to set * @return {@link BungeeCommandBuilder} */ - public BungeeCommandBuilder onExecute(@NotNull Executor executor) { + public @NotNull BungeeCommandBuilder onExecute(@NotNull Executor executor) { this.executor = executor; return this; } @@ -192,7 +186,7 @@ public BungeeCommandBuilder onExecute(@NotNull Executor executor) { * @param executor to set * @return {@link BungeeCommandBuilder} */ - public BungeeCommandBuilder onTabComplete(@NotNull TabExecutor executor) { + public @NotNull BungeeCommandBuilder onTabComplete(@NotNull TabExecutor executor) { this.tabExecutor = executor; return this; } @@ -203,7 +197,7 @@ public BungeeCommandBuilder onTabComplete(@NotNull TabExecutor executor) { * @param handler to set * @return {@link BungeeCommandBuilder} */ - public BungeeCommandBuilder onError(@NotNull ErrorHandler handler) { + public @NotNull BungeeCommandBuilder onError(@NotNull ErrorHandler handler) { this.errorHandler = handler; return this; } @@ -215,7 +209,7 @@ public BungeeCommandBuilder onError(@NotNull ErrorHandler handler) { * * @return {@link Command} */ - public Command build(Plugin plugin) { + public @NotNull Command build(Plugin plugin) { return new TabableCommand(this); } @@ -225,7 +219,7 @@ public Command build(Plugin plugin) { * @param plugin to register with */ public void register(Plugin plugin) { - Command command = build(plugin); + @NotNull Command command = build(plugin); ProxyServer.getInstance().getPluginManager().registerCommand(plugin, command); } @@ -233,12 +227,12 @@ private static class TabableCommand extends net.md_5.bungee.api.plugin.Command implements net.md_5.bungee.api.plugin.TabExecutor { BungeeCommandBuilder builder; - public TabableCommand(BungeeCommandBuilder builder) { + public TabableCommand(@NotNull BungeeCommandBuilder builder) { super(builder.getName(), builder.getPermission(), builder.getAliases().toArray(new String[0])); this.builder = builder; } - public void execute(net.md_5.bungee.api.CommandSender sender, String[] args) { + public void execute(net.md_5.bungee.api.@NotNull CommandSender sender, String[] args) { // CommandSender sender, CommandBuilder builder, String label, List args if (builder.getSynchronous()) { builder.performExecution(sender, builder, builder.getName(), Arrays.asList(args)); @@ -248,7 +242,7 @@ public void execute(net.md_5.bungee.api.CommandSender sender, String[] args) { } @Override - public Iterable onTabComplete(net.md_5.bungee.api.CommandSender sender, String[] args) { + public Iterable onTabComplete(net.md_5.bungee.api.CommandSender sender, String @NotNull [] args) { if (builder.tabExecutor != null) return builder.tabExecutor.apply(sender, builder.getName(), new Arguments(Arrays.asList(args))); else { @@ -258,10 +252,10 @@ public Iterable onTabComplete(net.md_5.bungee.api.CommandSender sender, String lastWord = args[args.length - 1]; - ProxiedPlayer senderPlayer = sender instanceof ProxiedPlayer ? (ProxiedPlayer) sender : null; + @Nullable ProxiedPlayer senderPlayer = sender instanceof ProxiedPlayer ? (ProxiedPlayer) sender : null; - ArrayList matchedPlayers = new ArrayList(); - for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { + @NotNull ArrayList matchedPlayers = new ArrayList(); + for (@NotNull ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { String name = player.getName(); if ((senderPlayer == null) && StringUtil.startsWithIgnoreCase(name, lastWord)) { matchedPlayers.add(name); @@ -274,7 +268,7 @@ public Iterable onTabComplete(net.md_5.bungee.api.CommandSender sender, } } - private void _playSound(CommandSender sender, NotificationType type) { + private void _playSound(@NotNull CommandSender sender, @NotNull NotificationType type) { if (!this.getPlaySound()) return; SoundUtil.send(sender, type); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/PacketRegistration.java b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/PacketRegistration.java index f3cafc46..bee7424e 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/PacketRegistration.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/PacketRegistration.java @@ -4,14 +4,15 @@ */ package com.dumbdogdiner.stickyapi.bungeecord.packet; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.protocol.Protocol; +import org.jetbrains.annotations.NotNull; + import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.protocol.Protocol; - /** * @deprecated This is unsafe. */ @@ -23,12 +24,12 @@ private PacketRegistration() { private static Method map, regPacket; private static Class protocolMapping, protocolMappingArray; private static Object TO_CLIENT; - private static Boolean soundRegistered = false; // If this packet has been registered already + private static @NotNull Boolean soundRegistered = false; // If this packet has been registered already // Process our reflection nonsense private static void processReflection() { try { - Field f = Protocol.class.getDeclaredField("TO_CLIENT"); + @NotNull Field f = Protocol.class.getDeclaredField("TO_CLIENT"); map = Protocol.class.getDeclaredMethod("map", int.class, int.class); f.setAccessible(true); map.setAccessible(true); @@ -37,7 +38,7 @@ private static void processReflection() { TO_CLIENT = f.get(Protocol.GAME); regPacket = TO_CLIENT.getClass().getDeclaredMethod("registerPacket", Class.class, protocolMappingArray); regPacket.setAccessible(true); - } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) { + } catch (@NotNull IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) { e.printStackTrace(); } } @@ -49,11 +50,11 @@ private static void processReflection() { * @param id the protocol ID for the packet (see: https://wiki.vg/Protocol) */ public static void registerPacket(Class clazz, Integer id) { - Object[] array = (Object[]) Array.newInstance(protocolMapping, 1); + Object @NotNull [] array = (Object[]) Array.newInstance(protocolMapping, 1); try { array[0] = map.invoke(null, ProxyServer.getInstance().getProtocolVersion(), id); regPacket.invoke(TO_CLIENT, clazz, array); - } catch (IllegalAccessException | InvocationTargetException e) { + } catch (@NotNull IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/SoundPacket.java b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/SoundPacket.java index a74a83f8..86980cdf 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/SoundPacket.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/packet/SoundPacket.java @@ -8,6 +8,7 @@ import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.ProtocolConstants; +import org.jetbrains.annotations.NotNull; public class SoundPacket extends DefinedPacket { @@ -64,7 +65,7 @@ public void handle(AbstractPacketHandler abstractPacketHandler) throws Exception } @Override - public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + public void read(@NotNull ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { this.sound = readVarInt(buf); this.category = readVarInt(buf); this.x = buf.readInt(); @@ -75,7 +76,7 @@ public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protoco } @Override - public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + public void write(@NotNull ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { writeVarInt(sound, buf); writeVarInt(category, buf); @@ -98,7 +99,7 @@ public int hashCode() { } @Override - public String toString() { + public @NotNull String toString() { return getClass().getName(); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/protocol/Protocol.java b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/protocol/Protocol.java index 17bd2b3a..772c1d32 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/protocol/Protocol.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/protocol/Protocol.java @@ -4,9 +4,10 @@ */ package com.dumbdogdiner.stickyapi.bungeecord.protocol; -import java.util.HashMap; - import net.md_5.bungee.api.ProxyServer; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; @Deprecated /** @@ -16,7 +17,7 @@ public class Protocol { private Protocol() { } - public static HashMap soundEffect = new HashMap<>(); + public static @NotNull HashMap soundEffect = new HashMap<>(); static { soundEffect.put(ProtocolConstants.MINECRAFT_1_8, 0x29); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/SoundUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/SoundUtil.java index 944dc9f2..78dcb943 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/SoundUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/SoundUtil.java @@ -26,7 +26,7 @@ private SoundUtil() { * @param sender {@link org.bukkit.command.CommandSender} The sender to validate * @return {@link java.lang.Boolean} */ - private static Boolean validate(CommandSender sender) { + private static @NotNull Boolean validate(CommandSender sender) { return sender instanceof ProxiedPlayer; } @@ -107,11 +107,11 @@ public static void sendSuccess(@NotNull ProxiedPlayer player) { * @param type {@link NotificationType} The type of sound * @return {@link java.lang.Boolean} */ - public static Boolean send(@NotNull CommandSender sender, @NotNull NotificationType type) { + public static @NotNull Boolean send(@NotNull CommandSender sender, @NotNull NotificationType type) { if (!validate(sender)) { return false; } - var player = (ProxiedPlayer) sender; + @NotNull var player = (ProxiedPlayer) sender; switch (type) { case ERROR: sendError(player); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/StartupUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/StartupUtil.java index 89c50c7e..b448e0fd 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/StartupUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/bungeecord/util/StartupUtil.java @@ -59,7 +59,7 @@ public static boolean setupConfig(@NotNull Plugin plugin) { * @param localeProvider The plugin's locale provider * @return False if something went wrong */ - public static LocaleProvider setupLocale(@NotNull Plugin plugin, @Nullable LocaleProvider localeProvider) { + public static @Nullable LocaleProvider setupLocale(@NotNull Plugin plugin, @Nullable LocaleProvider localeProvider) { localeProvider = new LocaleProvider(new File(plugin.getDataFolder(), "locale")); int loadedLocales = localeProvider.loadAllLocales(); boolean localeEnabled = localeProvider.setDefaultLocale("messages.en_us"); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java b/src/main/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java index 2caf8826..dcd17650 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/ServerVersion.java @@ -4,9 +4,15 @@ */ package com.dumbdogdiner.stickyapi.common; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + /** * Utility class for fetching version data. */ + +// Suppress Constant conditions because they appear to always be true at compile time +@SuppressWarnings("ConstantConditions") public final class ServerVersion { private ServerVersion() { } @@ -20,7 +26,7 @@ public enum ServerType { * * @return The type of the server running when this method is evaluated. */ - public static ServerType getServerType() { + public static @NotNull ServerType getServerType() { if (isBukkit()) { if (isPaper()) { return ServerType.PAPER; @@ -46,7 +52,7 @@ public static ServerType getServerType() { public static boolean isPaper() { try { return Class.forName("com.destroystokyo.paper.VersionHistoryManager$VersionData") != null; - } catch (NoClassDefFoundError | ClassNotFoundException e) { + } catch (@NotNull NoClassDefFoundError | ClassNotFoundException e) { return false; } } @@ -73,7 +79,7 @@ public static boolean isSpigot() { public static boolean isBukkit() { try { return Class.forName("org.bukkit.Server") != null; - } catch (NoClassDefFoundError | ClassNotFoundException e) { + } catch (@NotNull NoClassDefFoundError | ClassNotFoundException e) { return false; } } @@ -86,7 +92,7 @@ public static boolean isBukkit() { public static boolean isWaterfall() { try { return Class.forName("io.github.waterfallmc.waterfall.QueryResult") != null; - } catch (NoClassDefFoundError | ClassNotFoundException e) { + } catch (@NotNull NoClassDefFoundError | ClassNotFoundException e) { return false; } } @@ -99,7 +105,7 @@ public static boolean isWaterfall() { public static boolean isBungee() { try { return Class.forName("net.md_5.bungee.api.ProxyServer") != null; - } catch (NoClassDefFoundError | ClassNotFoundException e) { + } catch (@NotNull NoClassDefFoundError | ClassNotFoundException e) { return false; } } @@ -110,7 +116,7 @@ public static boolean isBungee() { * * @return The current version of bukkit */ - public static String getBukkitVersion() { + public static @Nullable String getBukkitVersion() { try { return org.bukkit.Bukkit.getVersion(); } catch (NoClassDefFoundError e) { @@ -123,7 +129,7 @@ public static String getBukkitVersion() { * * @return The current version of bungee */ - public static String getBungeeVersion() { + public static @Nullable String getBungeeVersion() { try { return net.md_5.bungee.api.ProxyServer.getInstance().getVersion(); } catch (NoClassDefFoundError e) { diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java b/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java index e442bd55..81082126 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java @@ -4,21 +4,16 @@ */ package com.dumbdogdiner.stickyapi.common.arguments; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - import com.dumbdogdiner.stickyapi.common.util.Debugger; import com.dumbdogdiner.stickyapi.common.util.NumberUtil; import com.dumbdogdiner.stickyapi.common.util.TimeUtil; - import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.sql.Timestamp; +import java.util.*; + /** * Utility class for handling command arguments. */ @@ -28,7 +23,7 @@ public class Arguments { @Getter private ArrayList unparsedArgs; - private HashMap parsedArgs = new HashMap<>(); + private @NotNull HashMap parsedArgs = new HashMap<>(); @Getter private String invalidatedBy; @@ -70,7 +65,7 @@ public Arguments(@NotNull List args) { * @param flag The name of this flag, and flag to register * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalFlag(@NotNull String flag) { + public @NotNull Arguments optionalFlag(@NotNull String flag) { return optionalFlag(flag, flag); } @@ -81,7 +76,7 @@ public Arguments optionalFlag(@NotNull String flag) { * @param flag The flag to register * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalFlag(@NotNull String name, @NotNull String flag) { + public @NotNull Arguments optionalFlag(@NotNull String name, @NotNull String flag) { debug.print("Looking for optional flag " + name + "..."); int index = unparsedArgs.indexOf(flag); if (index == -1) { @@ -104,7 +99,7 @@ public Arguments optionalFlag(@NotNull String name, @NotNull String flag) { * @param flag The flag to register * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments requiredFlag(@NotNull String name, @NotNull String flag) { + public @NotNull Arguments requiredFlag(@NotNull String name, @NotNull String flag) { debug.print("Looking for required flag " + name + "..."); int index = unparsedArgs.indexOf(flag); if (index == -1) { @@ -121,7 +116,7 @@ public Arguments requiredFlag(@NotNull String name, @NotNull String flag) { return this; } - private Arguments optionalStringImplementation(String name, String fallback) { + private @NotNull Arguments optionalStringImplementation(String name, String fallback) { debug.print("Looking for optional string " + name + "..."); if (unparsedArgs.size() > position) { parsedArgs.put(name, unparsedArgs.get(position)); @@ -143,7 +138,7 @@ private Arguments optionalStringImplementation(String name, String fallback) { * @param fallback the default value you want for the argument * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalString(@NotNull String name, @NotNull String fallback) { + public @NotNull Arguments optionalString(@NotNull String name, @NotNull String fallback) { // noinspection ConstantConditions return optionalStringImplementation(name, fallback); } @@ -154,7 +149,7 @@ public Arguments optionalString(@NotNull String name, @NotNull String fallback) * @param name The name of this string * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalString(String name) { + public @NotNull Arguments optionalString(String name) { return optionalStringImplementation(name, null); } @@ -164,7 +159,7 @@ public Arguments optionalString(String name) { * @param name The name of this string * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments requiredString(String name) { + public @NotNull Arguments requiredString(@NotNull String name) { debug.print("Looking for required string " + name + "..."); if (unparsedArgs.size() > position) { @@ -179,7 +174,7 @@ public Arguments requiredString(String name) { return this; } - private Arguments optionalSentenceImplementation(String name, String fallback, int length) { + private @NotNull Arguments optionalSentenceImplementation(String name, String fallback, int length) { int end = position + length; debug.print("Looking for optional sentence - start = " + String.valueOf(position) + ", end = " + String.valueOf(end) + ", length = " + String.valueOf(length)); @@ -197,7 +192,7 @@ private Arguments optionalSentenceImplementation(String name, String fallback, i return this; } - String concatenated = String.join(" ", + @NotNull String concatenated = String.join(" ", Arrays.copyOfRange(unparsedArgs.toArray(new String[unparsedArgs.size()]), position, end)); parsedArgs.put(name, concatenated); @@ -218,7 +213,7 @@ private Arguments optionalSentenceImplementation(String name, String fallback, i * @param name The name of this sentence * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalSentence(@NotNull String name) { + public @NotNull Arguments optionalSentence(@NotNull String name) { debug.print("Using default length: " + String.valueOf(unparsedArgs.size() - position)); return optionalSentence(name, unparsedArgs.size() - position); } @@ -231,7 +226,7 @@ public Arguments optionalSentence(@NotNull String name) { * @param fallback The fallback sentence to use if one is not provided * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalSentence(@NotNull String name, @Nullable String fallback) { + public @NotNull Arguments optionalSentence(@NotNull String name, @Nullable String fallback) { debug.print("Using default length: " + String.valueOf(unparsedArgs.size() - position)); return optionalSentence(name, fallback, unparsedArgs.size() - position); } @@ -244,7 +239,7 @@ public Arguments optionalSentence(@NotNull String name, @Nullable String fallbac * @param length The length of the sentence * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalSentence(@NotNull String name, @Nullable String fallback, @NotNull int length) { + public @NotNull Arguments optionalSentence(@NotNull String name, @Nullable String fallback, @NotNull int length) { if (fallback != null) { return optionalSentenceImplementation(name, fallback, length); } else { @@ -260,7 +255,7 @@ public Arguments optionalSentence(@NotNull String name, @Nullable String fallbac * @param length The length of the sentence * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalSentence(@NotNull String name, @NotNull int length) { + public @NotNull Arguments optionalSentence(@NotNull String name, @NotNull int length) { optionalSentenceImplementation(name, null, length); return this; } @@ -272,7 +267,7 @@ public Arguments optionalSentence(@NotNull String name, @NotNull int length) { * @param name Name of the argument * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments requiredSentence(@NotNull String name) { + public @NotNull Arguments requiredSentence(@NotNull String name) { debug.print("Using default length: " + String.valueOf(unparsedArgs.size() - position)); return requiredSentence(name, unparsedArgs.size() - position); } @@ -284,7 +279,7 @@ public Arguments requiredSentence(@NotNull String name) { * @param length Maximum length in words of the sentence * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments requiredSentence(@NotNull String name, @NotNull int length) { + public @NotNull Arguments requiredSentence(@NotNull String name, @NotNull int length) { int end = position + length; debug.print("Looking for required sentence - start = " + String.valueOf(position) + ", end = " + String.valueOf(end) + ", length = " + String.valueOf(length)); @@ -303,7 +298,7 @@ public Arguments requiredSentence(@NotNull String name, @NotNull int length) { return this; } - String concatenated = String.join(" ", + @NotNull String concatenated = String.join(" ", Arrays.copyOfRange(unparsedArgs.toArray(new String[unparsedArgs.size()]), position, end)); parsedArgs.put(name, concatenated); @@ -320,7 +315,7 @@ public Arguments requiredSentence(@NotNull String name, @NotNull int length) { * @param name Name of the argument * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalTimeString(@NotNull String name) { + public @NotNull Arguments optionalTimeString(@NotNull String name) { debug.print("Looking for optional timestamp " + name + "..."); if (unparsedArgs.size() > position && TimeUtil.toTimestamp(unparsedArgs.get(position)) != null) { parsedArgs.put(name, String.valueOf(TimeUtil.toTimestamp(unparsedArgs.get(position)).getTime())); @@ -339,7 +334,7 @@ public Arguments optionalTimeString(@NotNull String name) { * @param name Name of the argument * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments requiredTimeString(@NotNull String name) { + public @NotNull Arguments requiredTimeString(@NotNull String name) { debug.print("Looking for required timestamp " + name + "..."); if (unparsedArgs.size() > position && TimeUtil.toTimestamp(unparsedArgs.get(position)) != null) { parsedArgs.put(name, String.valueOf(TimeUtil.toTimestamp(unparsedArgs.get(position)).getTime())); @@ -354,7 +349,7 @@ public Arguments requiredTimeString(@NotNull String name) { return this; } - private Arguments optionalIntImplementation(@NotNull String name, @NotNull Integer fallback) { + private @NotNull Arguments optionalIntImplementation(@NotNull String name, @NotNull Integer fallback) { debug.print("Looking for optional integer " + name + "..."); if (unparsedArgs.size() > position && NumberUtil.isNumeric(unparsedArgs.get(position))) { parsedArgs.put(name, unparsedArgs.get(position)); @@ -375,7 +370,7 @@ private Arguments optionalIntImplementation(@NotNull String name, @NotNull Integ * @param name Name of the argument * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalInt(@NotNull String name) { + public @NotNull Arguments optionalInt(@NotNull String name) { optionalIntImplementation(name, null); return this; } @@ -387,7 +382,7 @@ public Arguments optionalInt(@NotNull String name) { * @param fallback The fallback integer to use if one is not provided * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalInt(@NotNull String name, @NotNull Integer fallback) { + public @NotNull Arguments optionalInt(@NotNull String name, @NotNull Integer fallback) { if (fallback != null) { return optionalIntImplementation(name, fallback); } else { @@ -402,7 +397,7 @@ public Arguments optionalInt(@NotNull String name, @NotNull Integer fallback) { * @param name The name of the integer to create * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments requiredInt(@NotNull String name) { + public @NotNull Arguments requiredInt(@NotNull String name) { debug.print("Looking for optional required " + name + "..."); if (unparsedArgs.size() > position && NumberUtil.isNumeric(unparsedArgs.get(position))) { @@ -424,7 +419,7 @@ public Arguments requiredInt(@NotNull String name) { * @param name The name of the duration to create * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments optionalDuration(@NotNull String name) { + public @NotNull Arguments optionalDuration(@NotNull String name) { debug.print("Looking for optional duration " + name + "..."); if (unparsedArgs.size() > position && TimeUtil.duration(unparsedArgs.get(position)).isPresent()) { @@ -444,7 +439,7 @@ public Arguments optionalDuration(@NotNull String name) { * @param name The name of the duration to create * @return {@link com.dumbdogdiner.stickyapi.common.arguments.Arguments} */ - public Arguments requiredDuration(@NotNull String name) { + public @NotNull Arguments requiredDuration(@NotNull String name) { debug.print("Looking for required duration " + name + "..."); if (unparsedArgs.size() > position && TimeUtil.duration(unparsedArgs.get(position)).isPresent()) { @@ -483,7 +478,7 @@ public String getString(@NotNull String name) { * @deprecated Renamed in 2.0, use {@link #getString(String)} instead. */ @Deprecated - public String get(String name) { + public String get(@NotNull String name) { return getString(name); } @@ -495,7 +490,7 @@ public String get(String name) { * @param name The name of the timestamp to fetch * @return {@link java.sql.Timestamp} */ - public Timestamp getTimestamp(@NotNull String name) { + public @Nullable Timestamp getTimestamp(@NotNull String name) { if (parsedArgs.get(name) == null) { return null; } @@ -510,7 +505,7 @@ public Timestamp getTimestamp(@NotNull String name) { * @param name The name of the integer to fetch * @return {@link java.lang.Integer} */ - public Integer getInt(@NotNull String name) { + public @Nullable Integer getInt(@NotNull String name) { try { return Integer.parseInt(parsedArgs.get(name)); } catch (NumberFormatException e) { @@ -526,7 +521,7 @@ public Integer getInt(@NotNull String name) { * @param name The name of the double to fetch * @return {@link java.lang.Double} */ - public Double getDouble(@NotNull String name) { + public @Nullable Double getDouble(@NotNull String name) { try { return Double.parseDouble(parsedArgs.get(name)); } catch (NumberFormatException e) { @@ -542,7 +537,7 @@ public Double getDouble(@NotNull String name) { * @param name The name of the long to fetch * @return {@link java.lang.Long} */ - public Long getLong(@NotNull String name) { + public @Nullable Long getLong(@NotNull String name) { try { return Long.parseLong(parsedArgs.get(name)); } catch (NumberFormatException e) { @@ -553,12 +548,10 @@ public Long getLong(@NotNull String name) { /** * Return whether an argument exists. *

- * Returns the argument, if it exists * * @param name The name of the argument to check for - * @return {@link java.lang.Boolean} */ - public Boolean exists(@NotNull String name) { + public boolean exists(@NotNull String name) { return parsedArgs.get(name) != null; } @@ -570,7 +563,7 @@ public Boolean exists(@NotNull String name) { * @param name The name of the flag to fetch * @return {@link java.lang.Boolean} */ - public Boolean getFlag(@NotNull String name) { + public @Nullable Boolean getFlag(@NotNull String name) { return exists(name); } @@ -582,7 +575,7 @@ public Boolean getFlag(@NotNull String name) { * @param name The name of the boolean to fetch * @return {@link java.lang.Boolean} */ - public Boolean getBoolean(@NotNull String name) { + public @NotNull Boolean getBoolean(@NotNull String name) { return Boolean.valueOf(parsedArgs.get(name)); } @@ -594,7 +587,7 @@ public Boolean getBoolean(@NotNull String name) { * @param name The name of the duration to fetch * @return {@link java.lang.Long} */ - public Long getDuration(@NotNull String name) { + public @Nullable Long getDuration(@NotNull String name) { return TimeUtil.duration(parsedArgs.get(name)).isPresent() ? TimeUtil.duration(parsedArgs.get(name)).get() : null; } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cache.java b/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cache.java index 0e95d9d2..b9c7d024 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cache.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cache.java @@ -15,6 +15,7 @@ import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.Nullable; /** * General purpose cache for caching things that should be cached. @@ -31,12 +32,12 @@ public interface Predicate { boolean match(T object); } - private ConcurrentHashMap objects = new ConcurrentHashMap(); - private ConcurrentHashMap objectInsertionTimestamps = new ConcurrentHashMap(); + private @NotNull ConcurrentHashMap objects = new ConcurrentHashMap(); + private @NotNull ConcurrentHashMap objectInsertionTimestamps = new ConcurrentHashMap(); @Getter @Setter - private Long ttl = (long) (30 * 60e3); + private @NotNull Long ttl = (long) (30 * 60e3); // private int memoryUsage = 0; // @Getter @Setter @@ -47,9 +48,9 @@ public interface Predicate { private int maxSize = 0; @Getter - private FutureTask objectExpiryTask = new FutureTask<>(new Callable() { + private @NotNull FutureTask objectExpiryTask = new FutureTask<>(new Callable() { @Override - public Boolean call() { + public @NotNull Boolean call() { if (ttl <= 0) { return false; } @@ -70,7 +71,7 @@ public Cache(Class clazz) { this.clazz = clazz; } - private Debugger debug = new Debugger(getClass()); + private @NotNull Debugger debug = new Debugger(getClass()); /** * Return the size of this cache. @@ -120,7 +121,7 @@ public T get(@NotNull String key) { * * @return All values in the cache */ - public Collection getAll() { + public @NotNull Collection getAll() { return objects.values(); } @@ -131,9 +132,9 @@ public Collection getAll() { * looking for * @return The first object that evaluates the tester to true, if there is one */ - public T find(@NotNull Predicate tester) { + public @Nullable T find(@NotNull Predicate tester) { debug.reset(); - for (T object : objects.values()) { + for (@NotNull T object : objects.values()) { if (tester.match(object)) { debug.print("Found cached entry for " + clazz.getSimpleName() + " with key " + object.getKey()); return object; @@ -207,7 +208,7 @@ public void update(@NotNull T object) { * @param object The object to remove * @return The removed object, if it exists */ - public T remove(@NotNull T object) { + public @Nullable T remove(@NotNull T object) { debug.reset(); T didRemove = objects.remove(object.getKey()); @@ -231,7 +232,7 @@ public T remove(@NotNull T object) { * @param key The key to remove * @return The removed object, if it exists */ - public T removeKey(@NotNull String key) { + public @Nullable T removeKey(@NotNull String key) { T object = objects.get(key); if (object == null) { return null; @@ -246,7 +247,7 @@ public T removeKey(@NotNull String key) { * @return The oldest entry in the cache, if it exists */ public T getOldestEntry() { - String oldest = null; + @Nullable String oldest = null; Long oldestTimestamp = Long.MAX_VALUE; for (String k : objectInsertionTimestamps.keySet()) { @@ -264,7 +265,7 @@ public T getOldestEntry() { * * @return The oldest entry in the cache, if it exists */ - public T removeOldestEntry() { + public @Nullable T removeOldestEntry() { return remove(getOldestEntry()); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cacheable.java b/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cacheable.java index 7a43bbf1..c6022303 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cacheable.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/cache/Cacheable.java @@ -4,10 +4,12 @@ */ package com.dumbdogdiner.stickyapi.common.cache; +import org.jetbrains.annotations.NotNull; + /** * Mark a class as being cacheable - must provide the getKey() * method to allow for key retrieval. */ public interface Cacheable { - String getKey(); + @NotNull String getKey(); } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/chat/ChatMessage.java b/src/main/java/com/dumbdogdiner/stickyapi/common/chat/ChatMessage.java index b75f2588..cf37bf8d 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/chat/ChatMessage.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/chat/ChatMessage.java @@ -57,7 +57,7 @@ public ChatMessage(@NotNull String content) { * @param content The new content this ChatMessage object should contain * @return {@link ChatMessage} */ - public ChatMessage setContent(@NotNull String content) { + public @NotNull ChatMessage setContent(@NotNull String content) { this.component = new TextComponent(content); this.rawContent = content; return this; @@ -70,7 +70,7 @@ public ChatMessage setContent(@NotNull String content) { * @param apply Whether URLs should be formatted. * @return {@link ChatMessage} */ - public ChatMessage applyURLs(@NotNull boolean apply) { + public @NotNull ChatMessage applyURLs(@NotNull boolean apply) { if (apply) { String original = this.component.getText(); component = URLUtil.convertURLs(original); @@ -86,7 +86,7 @@ public ChatMessage applyURLs(@NotNull boolean apply) { * @param chatMessage The ChatMessage that should be added. * @return {@link ChatMessage} */ - public ChatMessage appendMessage(@NotNull ChatMessage chatMessage) { + public @NotNull ChatMessage appendMessage(@NotNull ChatMessage chatMessage) { if (chatMessage != null) { this.component.addExtra(chatMessage.getComponent()); this.rawContent = rawContent + chatMessage.getRawContent(); @@ -103,8 +103,8 @@ public ChatMessage appendMessage(@NotNull ChatMessage chatMessage) { * * @return {@link ChatMessage} */ - public ChatMessage setHoverMessage(@NotNull String... text) { - StringBuilder tooltip = new StringBuilder(); + public @NotNull ChatMessage setHoverMessage(@NotNull String @NotNull ... text) { + @NotNull StringBuilder tooltip = new StringBuilder(); int i = 0; for (String s : text) { tooltip.append(s); @@ -127,7 +127,7 @@ public ChatMessage setHoverMessage(@NotNull String... text) { * @param url The URL this message should suggest when clicked. * @return {@link ChatMessage} */ - public ChatMessage setLink(@NotNull String url) { + public @NotNull ChatMessage setLink(@NotNull String url) { this.component.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)); return this; } @@ -139,7 +139,7 @@ public ChatMessage setLink(@NotNull String url) { * @param command The command that should run when message is clicked. * @return {@link ChatMessage} */ - public ChatMessage setCommand(@NotNull String command) { + public @NotNull ChatMessage setCommand(@NotNull String command) { this.component.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command)); return this; } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/command/CommandBuilder.java b/src/main/java/com/dumbdogdiner/stickyapi/common/command/CommandBuilder.java index ae4104a5..0ae573dd 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/command/CommandBuilder.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/command/CommandBuilder.java @@ -4,6 +4,9 @@ */ package com.dumbdogdiner.stickyapi.common.command; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -18,6 +21,7 @@ public abstract class CommandBuilder> { @Getter Boolean synchronous = false; @Getter + @NotNull Boolean requiresPlayer = false; @Getter String name; @@ -26,12 +30,16 @@ public abstract class CommandBuilder> { @Getter String description; @Getter + @NotNull Boolean playSound = false; @Getter + @NotNull List aliases = new ArrayList<>(); @Getter + @NotNull Long cooldown = 0L; @Getter + @NotNull HashMap subCommands = new HashMap<>(); /** @@ -51,7 +59,7 @@ public CommandBuilder(@NotNull String name) { * @param synchronous if this command should run synchronously * @return {@link CommandBuilder} */ - public T synchronous(@NotNull Boolean synchronous) { + public @NotNull T synchronous(@NotNull Boolean synchronous) { this.synchronous = synchronous; return (T) this; } @@ -61,7 +69,7 @@ public T synchronous(@NotNull Boolean synchronous) { * * @return {@link CommandBuilder} */ - public T synchronous() { + public @NotNull T synchronous() { return this.synchronous(true); } @@ -71,7 +79,7 @@ public T synchronous() { * @param cooldown in milliseconds * @return {@link CommandBuilder} */ - public T cooldown(@NotNull Long cooldown) { + public @NotNull T cooldown(@NotNull Long cooldown) { this.cooldown = cooldown; return (T) this; } @@ -83,7 +91,7 @@ public T cooldown(@NotNull Long cooldown) { * @param requiresPlayer If this command should require a player as the executor * @return {@link CommandBuilder} */ - public T requiresPlayer(@NotNull Boolean requiresPlayer) { + public @NotNull T requiresPlayer(@NotNull Boolean requiresPlayer) { this.requiresPlayer = requiresPlayer; return (T) this; } @@ -94,7 +102,7 @@ public T requiresPlayer(@NotNull Boolean requiresPlayer) { * * @return {@link CommandBuilder} */ - public T requiresPlayer() { + public @NotNull T requiresPlayer() { return this.requiresPlayer(true); } @@ -124,7 +132,7 @@ public T playSound() { * @param permission to set * @return {@link CommandBuilder} */ - public T permission(@NotNull String permission) { + public @NotNull T permission(@NotNull String permission) { this.permission = permission; return (T) this; } @@ -135,7 +143,7 @@ public T permission(@NotNull String permission) { * @param description to set * @return {@link CommandBuilder} */ - public T description(@NotNull String description) { + public @NotNull T description(@NotNull String description) { this.description = description; return (T) this; } @@ -146,7 +154,7 @@ public T description(@NotNull String description) { * @param alias to add * @return {@link CommandBuilder} */ - public T alias(@NotNull String... alias) { + public @NotNull T alias(@NotNull String @NotNull ... alias) { for (var a : alias) { this.aliases.add(a); } @@ -159,7 +167,7 @@ public T alias(@NotNull String... alias) { * @param aliases to set * @return {@link CommandBuilder} */ - public T aliases(@NotNull List aliases) { + public @NotNull T aliases(@NotNull List aliases) { this.aliases = aliases; return (T) this; } @@ -170,7 +178,7 @@ public T aliases(@NotNull List aliases) { * @param builder the sub command * @return {@link CommandBuilder} */ - public T subCommand(@NotNull T builder) { + public @NotNull T subCommand(@NotNull T builder) { builder.synchronous = this.synchronous; this.subCommands.put(builder.name, builder); return (T) this; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/ConfigurationOptions.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/ConfigurationOptions.java index f29e6471..899b5957 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/ConfigurationOptions.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/ConfigurationOptions.java @@ -13,7 +13,7 @@ public class ConfigurationOptions { private char pathSeparator = '.'; private boolean copyDefaults = false; - private final Configuration configuration; + private final @NotNull Configuration configuration; protected ConfigurationOptions(@NotNull Configuration configuration) { this.configuration = configuration; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemoryConfiguration.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemoryConfiguration.java index 88dff822..0f7c7d1a 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemoryConfiguration.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemoryConfiguration.java @@ -15,7 +15,7 @@ * temporary Configurations for providing defaults. */ public class MemoryConfiguration extends MemorySection implements Configuration { - protected Configuration defaults; + protected @Nullable Configuration defaults; protected MemoryConfigurationOptions options; /** @@ -50,7 +50,7 @@ public void addDefault(@NotNull String path, @Nullable Object value) { public void addDefaults(@NotNull Map defaults) { Validate.notNull(defaults, "Defaults may not be null"); - for (Map.Entry entry : defaults.entrySet()) { + for (Map.@NotNull Entry entry : defaults.entrySet()) { addDefault(entry.getKey(), entry.getValue()); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemorySection.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemorySection.java index 523d1911..a85b4da4 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemorySection.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/MemorySection.java @@ -26,10 +26,10 @@ */ public class MemorySection implements ConfigurationSection { protected final Map map = new LinkedHashMap(); - private final Configuration root; - private final ConfigurationSection parent; - private final String path; - private final String fullPath; + private final @Nullable Configuration root; + private final @Nullable ConfigurationSection parent; + private final @NotNull String path; + private final @NotNull String fullPath; /** * Creates an empty MemorySection for use as a root {@link Configuration} @@ -77,11 +77,11 @@ protected MemorySection(@NotNull ConfigurationSection parent, @NotNull String pa @Override @NotNull public Set getKeys(boolean deep) { - Set result = new LinkedHashSet(); + @NotNull Set result = new LinkedHashSet(); - Configuration root = getRoot(); + @Nullable Configuration root = getRoot(); if (root != null && root.options().copyDefaults()) { - ConfigurationSection defaults = getDefaultSection(); + @Nullable ConfigurationSection defaults = getDefaultSection(); if (defaults != null) { result.addAll(defaults.getKeys(deep)); @@ -96,11 +96,11 @@ public Set getKeys(boolean deep) { @Override @NotNull public Map getValues(boolean deep) { - Map result = new LinkedHashMap(); + @NotNull Map result = new LinkedHashMap(); - Configuration root = getRoot(); + @Nullable Configuration root = getRoot(); if (root != null && root.options().copyDefaults()) { - ConfigurationSection defaults = getDefaultSection(); + @Nullable ConfigurationSection defaults = getDefaultSection(); if (defaults != null) { result.putAll(defaults.getValues(deep)); @@ -124,7 +124,7 @@ public boolean contains(@NotNull String path, boolean ignoreDefault) { @Override public boolean isSet(@NotNull String path) { - Configuration root = getRoot(); + @Nullable Configuration root = getRoot(); if (root == null) { return false; } @@ -162,7 +162,7 @@ public ConfigurationSection getParent() { public void addDefault(@NotNull String path, @Nullable Object value) { Validate.notNull(path, "Path cannot be null"); - Configuration root = getRoot(); + @Nullable Configuration root = getRoot(); if (root == null) { throw new IllegalStateException("Cannot add default without root"); } @@ -175,8 +175,8 @@ public void addDefault(@NotNull String path, @Nullable Object value) { @Override @Nullable public ConfigurationSection getDefaultSection() { - Configuration root = getRoot(); - Configuration defaults = root == null ? null : root.getDefaults(); + @Nullable Configuration root = getRoot(); + @Nullable Configuration defaults = root == null ? null : root.getDefaults(); if (defaults != null) { if (defaults.isConfigurationSection(getCurrentPath())) { @@ -191,7 +191,7 @@ public ConfigurationSection getDefaultSection() { public void set(@NotNull String path, @Nullable Object value) { Validate.notEmpty(path, "Cannot set to an empty path"); - Configuration root = getRoot(); + @Nullable Configuration root = getRoot(); if (root == null) { throw new IllegalStateException("Cannot use section without a root"); } @@ -200,10 +200,10 @@ public void set(@NotNull String path, @Nullable Object value) { // i1 is the leading (higher) index // i2 is the trailing (lower) index int i1 = -1, i2; - ConfigurationSection section = this; + @Nullable ConfigurationSection section = this; while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { - String node = path.substring(i2, i1); - ConfigurationSection subSection = section.getConfigurationSection(node); + @NotNull String node = path.substring(i2, i1); + @Nullable ConfigurationSection subSection = section.getConfigurationSection(node); if (subSection == null) { if (value == null) { // no need to create missing sub-sections if we want to remove the value: @@ -215,7 +215,7 @@ public void set(@NotNull String path, @Nullable Object value) { } } - String key = path.substring(i2); + @NotNull String key = path.substring(i2); if (section == this) { if (value == null) { map.remove(key); @@ -242,7 +242,7 @@ public Object get(@NotNull String path, @Nullable Object def) { return this; } - Configuration root = getRoot(); + @Nullable Configuration root = getRoot(); if (root == null) { throw new IllegalStateException("Cannot access section without a root"); } @@ -251,7 +251,7 @@ public Object get(@NotNull String path, @Nullable Object def) { // i1 is the leading (higher) index // i2 is the trailing (lower) index int i1 = -1, i2; - ConfigurationSection section = this; + @Nullable ConfigurationSection section = this; while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { section = section.getConfigurationSection(path.substring(i2, i1)); if (section == null) { @@ -259,7 +259,7 @@ public Object get(@NotNull String path, @Nullable Object def) { } } - String key = path.substring(i2); + @NotNull String key = path.substring(i2); if (section == this) { Object result = map.get(key); return (result == null) ? def : result; @@ -271,7 +271,7 @@ public Object get(@NotNull String path, @Nullable Object def) { @NotNull public ConfigurationSection createSection(@NotNull String path) { Validate.notEmpty(path, "Cannot create section at empty path"); - Configuration root = getRoot(); + @Nullable Configuration root = getRoot(); if (root == null) { throw new IllegalStateException("Cannot create section without a root"); } @@ -280,10 +280,10 @@ public ConfigurationSection createSection(@NotNull String path) { // i1 is the leading (higher) index // i2 is the trailing (lower) index int i1 = -1, i2; - ConfigurationSection section = this; + @Nullable ConfigurationSection section = this; while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { - String node = path.substring(i2, i1); - ConfigurationSection subSection = section.getConfigurationSection(node); + @NotNull String node = path.substring(i2, i1); + @Nullable ConfigurationSection subSection = section.getConfigurationSection(node); if (subSection == null) { section = section.createSection(node); } else { @@ -291,9 +291,9 @@ public ConfigurationSection createSection(@NotNull String path) { } } - String key = path.substring(i2); + @NotNull String key = path.substring(i2); if (section == this) { - ConfigurationSection result = new MemorySection(this, key); + @NotNull ConfigurationSection result = new MemorySection(this, key); map.put(key, result); return result; } @@ -303,9 +303,9 @@ public ConfigurationSection createSection(@NotNull String path) { @Override @NotNull public ConfigurationSection createSection(@NotNull String path, @NotNull Map map) { - ConfigurationSection section = createSection(path); + @NotNull ConfigurationSection section = createSection(path); - for (Map.Entry entry : map.entrySet()) { + for (Map.@NotNull Entry entry : map.entrySet()) { if (entry.getValue() instanceof Map) { section.createSection(entry.getKey().toString(), (Map) entry.getValue()); } else { @@ -320,92 +320,92 @@ public ConfigurationSection createSection(@NotNull String path, @NotNull Map getList(@NotNull String path) { - Object def = getDefault(path); + @Nullable Object def = getDefault(path); return getList(path, (def instanceof List) ? (List) def : null); } @Override @Nullable public List getList(@NotNull String path, @Nullable List def) { - Object val = get(path, def); + @Nullable Object val = get(path, def); return (List) ((val instanceof List) ? val : def); } @Override public boolean isList(@NotNull String path) { - Object val = get(path); + @Nullable Object val = get(path); return val instanceof List; } @Override @NotNull public List getStringList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if ((object instanceof String) || (isPrimitiveWrapper(object))) { @@ -453,13 +453,13 @@ public List getStringList(@NotNull String path) { @Override @NotNull public List getIntegerList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Integer) { @@ -482,13 +482,13 @@ public List getIntegerList(@NotNull String path) { @Override @NotNull public List getBooleanList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Boolean) { @@ -508,13 +508,13 @@ public List getBooleanList(@NotNull String path) { @Override @NotNull public List getDoubleList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Double) { @@ -537,13 +537,13 @@ public List getDoubleList(@NotNull String path) { @Override @NotNull public List getFloatList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Float) { @@ -566,13 +566,13 @@ public List getFloatList(@NotNull String path) { @Override @NotNull public List getLongList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Long) { @@ -595,13 +595,13 @@ public List getLongList(@NotNull String path) { @Override @NotNull public List getByteList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Byte) { @@ -624,19 +624,19 @@ public List getByteList(@NotNull String path) { @Override @NotNull public List getCharacterList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Character) { result.add((Character) object); } else if (object instanceof String) { - String str = (String) object; + @NotNull String str = (String) object; if (str.length() == 1) { result.add(str.charAt(0)); @@ -652,13 +652,13 @@ public List getCharacterList(@NotNull String path) { @Override @NotNull public List getShortList(@NotNull String path) { - List list = getList(path); + @Nullable List list = getList(path); if (list == null) { return new ArrayList(0); } - List result = new ArrayList(); + @NotNull List result = new ArrayList(); for (Object object : list) { if (object instanceof Short) { @@ -681,8 +681,8 @@ public List getShortList(@NotNull String path) { @Override @NotNull public List> getMapList(@NotNull String path) { - List list = getList(path); - List> result = new ArrayList>(); + @Nullable List list = getList(path); + @NotNull List> result = new ArrayList>(); if (list == null) { return result; @@ -702,7 +702,7 @@ public List getShortList(@NotNull String path) { @Override public T getObject(@NotNull String path, @NotNull Class clazz) { Validate.notNull(clazz, "Class cannot be null"); - Object def = getDefault(path); + @Nullable Object def = getDefault(path); return getObject(path, clazz, (def != null && clazz.isInstance(def)) ? clazz.cast(def) : null); } @@ -710,7 +710,7 @@ public T getObject(@NotNull String path, @NotNull Class cl @Override public T getObject(@NotNull String path, @NotNull Class clazz, @Nullable T def) { Validate.notNull(clazz, "Class cannot be null"); - Object val = get(path, def); + @Nullable Object val = get(path, def); return (val != null && clazz.isInstance(val)) ? clazz.cast(val) : def; } @@ -730,7 +730,7 @@ public T getSerializable(@NotNull String p @Override @Nullable public ConfigurationSection getConfigurationSection(@NotNull String path) { - Object val = get(path, null); + @Nullable Object val = get(path, null); if (val != null) { return (val instanceof ConfigurationSection) ? (ConfigurationSection) val : null; } @@ -741,7 +741,7 @@ public ConfigurationSection getConfigurationSection(@NotNull String path) { @Override public boolean isConfigurationSection(@NotNull String path) { - Object val = get(path); + @Nullable Object val = get(path); return val instanceof ConfigurationSection; } @@ -755,25 +755,25 @@ protected boolean isPrimitiveWrapper(@Nullable Object input) { protected Object getDefault(@NotNull String path) { Validate.notNull(path, "Path cannot be null"); - Configuration root = getRoot(); - Configuration defaults = root == null ? null : root.getDefaults(); + @Nullable Configuration root = getRoot(); + @Nullable Configuration defaults = root == null ? null : root.getDefaults(); return (defaults == null) ? null : defaults.get(createPath(this, path)); } protected void mapChildrenKeys(@NotNull Set output, @NotNull ConfigurationSection section, boolean deep) { if (section instanceof MemorySection) { - MemorySection sec = (MemorySection) section; + @NotNull MemorySection sec = (MemorySection) section; - for (Map.Entry entry : sec.map.entrySet()) { + for (Map.@NotNull Entry entry : sec.map.entrySet()) { output.add(createPath(section, entry.getKey(), this)); if ((deep) && (entry.getValue() instanceof ConfigurationSection)) { - ConfigurationSection subsection = (ConfigurationSection) entry.getValue(); + @NotNull ConfigurationSection subsection = (ConfigurationSection) entry.getValue(); mapChildrenKeys(output, subsection, deep); } } } else { - Set keys = section.getKeys(deep); + @NotNull Set keys = section.getKeys(deep); for (String key : keys) { output.add(createPath(section, key, this)); @@ -784,15 +784,15 @@ protected void mapChildrenKeys(@NotNull Set output, @NotNull Configurati protected void mapChildrenValues(@NotNull Map output, @NotNull ConfigurationSection section, boolean deep) { if (section instanceof MemorySection) { - MemorySection sec = (MemorySection) section; + @NotNull MemorySection sec = (MemorySection) section; - for (Map.Entry entry : sec.map.entrySet()) { + for (Map.@NotNull Entry entry : sec.map.entrySet()) { // Because of the copyDefaults call potentially copying out of order, we must // remove and then add in our saved order // This means that default values we haven't set end up getting placed first // See SPIGOT-4558 for an example using spigot.yml - watch subsections move // around to default order - String childPath = createPath(section, entry.getKey(), this); + @NotNull String childPath = createPath(section, entry.getKey(), this); output.remove(childPath); output.put(childPath, entry.getValue()); @@ -803,9 +803,9 @@ protected void mapChildrenValues(@NotNull Map output, @NotNull C } } } else { - Map values = section.getValues(deep); + @NotNull Map values = section.getValues(deep); - for (Map.Entry entry : values.entrySet()) { + for (Map.@NotNull Entry entry : values.entrySet()) { output.put(createPath(section, entry.getKey(), this), entry.getValue()); } } @@ -843,15 +843,15 @@ public static String createPath(@NotNull ConfigurationSection section, @Nullable public static String createPath(@NotNull ConfigurationSection section, @Nullable String key, @Nullable ConfigurationSection relativeTo) { Validate.notNull(section, "Cannot create path without a section"); - Configuration root = section.getRoot(); + @Nullable Configuration root = section.getRoot(); if (root == null) { throw new IllegalStateException("Cannot create path without a root"); } char separator = root.options().pathSeparator(); - StringBuilder builder = new StringBuilder(); + @NotNull StringBuilder builder = new StringBuilder(); if (section != null) { - for (ConfigurationSection parent = section; (parent != null) + for (@Nullable ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) { if (builder.length() > 0) { builder.insert(0, separator); @@ -873,8 +873,8 @@ public static String createPath(@NotNull ConfigurationSection section, @Nullable } @Override - public String toString() { - Configuration root = getRoot(); + public @NotNull String toString() { + @Nullable Configuration root = getRoot(); return new StringBuilder().append(getClass().getSimpleName()).append("[path='").append(getCurrentPath()) .append("', root='").append(root == null ? null : root.getClass().getSimpleName()).append("']") .toString(); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfiguration.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfiguration.java index 07cf5928..f31e2ff9 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfiguration.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfiguration.java @@ -66,9 +66,9 @@ public void save(@NotNull File file) throws IOException { Files.createParentDirs(file); - String data = saveToString(); + @NotNull String data = saveToString(); - Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8); + @NotNull Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8); try { writer.write(data); @@ -127,7 +127,7 @@ public void save(@NotNull String file) throws IOException { public void load(@NotNull File file) throws FileNotFoundException, IOException, InvalidConfigurationException { Validate.notNull(file, "File cannot be null"); - final FileInputStream stream = new FileInputStream(file); + final @NotNull FileInputStream stream = new FileInputStream(file); load(new InputStreamReader(stream, Charsets.UTF_8)); } @@ -147,9 +147,9 @@ public void load(@NotNull File file) throws FileNotFoundException, IOException, * @throws IllegalArgumentException thrown when reader is null */ public void load(@NotNull Reader reader) throws IOException, InvalidConfigurationException { - BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + @NotNull BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); - StringBuilder builder = new StringBuilder(); + @NotNull StringBuilder builder = new StringBuilder(); try { String line; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfigurationOptions.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfigurationOptions.java index 48e126ea..14064b12 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfigurationOptions.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/FileConfigurationOptions.java @@ -14,7 +14,7 @@ * {@link FileConfiguration} */ public class FileConfigurationOptions extends MemoryConfigurationOptions { - private String header = null; + private @Nullable String header = null; private boolean copyHeader = true; protected FileConfigurationOptions(@NotNull MemoryConfiguration configuration) { diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConfiguration.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConfiguration.java index 5d5cfe27..d79b74fb 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConfiguration.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConfiguration.java @@ -16,6 +16,7 @@ import com.dumbdogdiner.stickyapi.common.configuration.ConfigurationSection; import com.dumbdogdiner.stickyapi.common.configuration.InvalidConfigurationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.error.YAMLException; @@ -39,7 +40,7 @@ public String saveToString() { yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - String header = buildHeader(); + @NotNull String header = buildHeader(); String dump = yaml.dump(getValues(false)); if (dump.equals(BLANK_CONFIG)) { @@ -62,7 +63,7 @@ public void loadFromString(@NotNull String contents) throws InvalidConfiguration throw new InvalidConfigurationException("Top level is not a Map."); } - String header = parseHeader(contents); + @NotNull String header = parseHeader(contents); if (header.length() > 0) { options().header(header); } @@ -73,7 +74,7 @@ public void loadFromString(@NotNull String contents) throws InvalidConfiguration } protected void convertMapsToSections(@NotNull Map input, @NotNull ConfigurationSection section) { - for (Map.Entry entry : input.entrySet()) { + for (Map.@NotNull Entry entry : input.entrySet()) { String key = entry.getKey().toString(); Object value = entry.getValue(); @@ -88,7 +89,7 @@ protected void convertMapsToSections(@NotNull Map input, @NotNull Configur @NotNull protected String parseHeader(@NotNull String input) { String[] lines = input.split("\r?\n", -1); - StringBuilder result = new StringBuilder(); + @NotNull StringBuilder result = new StringBuilder(); boolean readingHeader = true; boolean foundHeader = false; @@ -118,14 +119,14 @@ protected String parseHeader(@NotNull String input) { @NotNull @Override protected String buildHeader() { - String header = options().header(); + @Nullable String header = options().header(); if (options().copyHeader()) { - Configuration def = getDefaults(); + @Nullable Configuration def = getDefaults(); if ((def != null) && (def instanceof FileConfiguration)) { - FileConfiguration filedefaults = (FileConfiguration) def; - String defaultsHeader = filedefaults.buildHeader(); + @NotNull FileConfiguration filedefaults = (FileConfiguration) def; + @NotNull String defaultsHeader = filedefaults.buildHeader(); if ((defaultsHeader != null) && (defaultsHeader.length() > 0)) { return defaultsHeader; @@ -137,7 +138,7 @@ protected String buildHeader() { return ""; } - StringBuilder builder = new StringBuilder(); + @NotNull StringBuilder builder = new StringBuilder(); String[] lines = header.split("\r?\n", -1); boolean startedHeader = false; @@ -180,7 +181,7 @@ public YamlConfigurationOptions options() { public static YamlConfiguration loadConfiguration(@NotNull File file) { Validate.notNull(file, "File cannot be null"); - YamlConfiguration config = new YamlConfiguration(); + @NotNull YamlConfiguration config = new YamlConfiguration(); try { config.load(file); @@ -208,7 +209,7 @@ public static YamlConfiguration loadConfiguration(@NotNull File file) { public static YamlConfiguration loadConfiguration(@NotNull Reader reader) { Validate.notNull(reader, "Stream cannot be null"); - YamlConfiguration config = new YamlConfiguration(); + @NotNull YamlConfiguration config = new YamlConfiguration(); try { config.load(reader); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConstructor.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConstructor.java index 76c5e60e..5ed8db3c 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConstructor.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlConstructor.java @@ -27,7 +27,7 @@ public Object construct(@NotNull Node node) { throw new YAMLException("Unexpected referential mapping structure. Node: " + node); } - Map raw = (Map) super.construct(node); + @NotNull Map raw = (Map) super.construct(node); return raw; } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlRepresenter.java b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlRepresenter.java index b4faf714..460c911f 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlRepresenter.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/configuration/file/YamlRepresenter.java @@ -33,8 +33,8 @@ private class RepresentConfigurationSerializable extends RepresentMap { @NotNull @Override public Node representData(@NotNull Object data) { - ConfigurationSerializable serializable = (ConfigurationSerializable) data; - Map values = new LinkedHashMap(); + @NotNull ConfigurationSerializable serializable = (ConfigurationSerializable) data; + @NotNull Map values = new LinkedHashMap(); values.putAll(serializable.serialize()); return super.representData(values); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/scheduler/Scheduler.java b/src/main/java/com/dumbdogdiner/stickyapi/common/scheduler/Scheduler.java index e3985a81..3986d8e8 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/scheduler/Scheduler.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/scheduler/Scheduler.java @@ -17,6 +17,8 @@ import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Schedule async or sync runnables @@ -27,12 +29,12 @@ public class Scheduler { * Array of tasks to be run on the main thread or synchronously */ @Getter - protected Queue> synchronous = new ArrayDeque>(); + protected @NotNull Queue> synchronous = new ArrayDeque>(); /** * Array of tasks to be run as part of a thread pool. */ @Getter - protected Queue> asynchronous = new ArrayDeque>(); + protected @NotNull Queue> asynchronous = new ArrayDeque>(); /** * The thread pool to use for executing tasks. @@ -49,8 +51,8 @@ public Scheduler(int poolsz) { * * @param task to execute */ - public Future scheduleThreaded(Callable task) { - RunnableFuture t = new FutureTask(task); + public @NotNull Future scheduleThreaded(@NotNull Callable task) { + @NotNull RunnableFuture t = new FutureTask(task); this.pool.execute(t); return (FutureTask) t; } @@ -61,7 +63,7 @@ public Future scheduleThreaded(Callable task) { * @param task to run * @param time to execute the task */ - public Future scheduleThreaded(Callable task, Date time) { + public @NotNull Future scheduleThreaded(Callable task, @NotNull Date time) { long future = time.getTime(); long now = System.currentTimeMillis(); if (future <= now) @@ -77,8 +79,8 @@ public Future scheduleThreaded(Callable task, Date time) { * * @param task to run */ - public Future scheduleSynchronous(Callable task) { - FutureTask t = new FutureTask(task); + public @NotNull Future scheduleSynchronous(@NotNull Callable task) { + @NotNull FutureTask t = new FutureTask(task); this.synchronous.add(t); return t; } @@ -89,7 +91,7 @@ public Future scheduleSynchronous(Callable task) { * @param task to run * @param time to execute the task */ - public Future scheduleSynchronous(Callable task, Date time) { + public @NotNull Future scheduleSynchronous(@NotNull Callable task, Date time) { // TODO: Make synchronous version of this? return new FutureTask(task); } @@ -99,7 +101,7 @@ public Future scheduleSynchronous(Callable task, Date time) { * be called in the application's eventloop or in a single thread. */ public void schedule() { - RunnableFuture task = null; + @Nullable RunnableFuture task = null; while ((task = this.synchronous.poll()) != null) task.run(); } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Locale.java b/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Locale.java index 38e5ff40..499862d3 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Locale.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Locale.java @@ -4,29 +4,30 @@ */ package com.dumbdogdiner.stickyapi.common.translation; -import java.io.File; - import com.dumbdogdiner.stickyapi.common.configuration.file.FileConfiguration; import com.dumbdogdiner.stickyapi.common.configuration.file.YamlConfiguration; import com.dumbdogdiner.stickyapi.common.util.Debugger; - +import lombok.Getter; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import lombok.Getter; +import java.io.File; /** * Represents a wrapper around a locale configuration file. */ public class Locale { - private Debugger debug = new Debugger(getClass()); + private final Debugger debug = new Debugger(getClass()); @Getter + @NotNull Boolean isValid = false; @Getter File localeFile; @Getter + @NotNull FileConfiguration localeConfig = new YamlConfiguration(); /** @@ -55,7 +56,7 @@ public Locale(@NotNull File localeFile) { * @param node The node to get * @return {@link java.lang.String} */ - public String get(@NotNull String node) { + public @Nullable String get(@NotNull String node) { debug.reset().print("fetching node " + node); debug.print("node value: " + localeConfig.getString(node)); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/translation/LocaleProvider.java b/src/main/java/com/dumbdogdiner/stickyapi/common/translation/LocaleProvider.java index 22d062ab..40606f04 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/translation/LocaleProvider.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/translation/LocaleProvider.java @@ -4,39 +4,35 @@ */ package com.dumbdogdiner.stickyapi.common.translation; -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.Map; -import java.util.TreeMap; - +import com.dumbdogdiner.stickyapi.StickyAPI; import com.dumbdogdiner.stickyapi.common.util.Debugger; - -import org.jetbrains.annotations.NotNull; - import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; /** * Provides an interface between locale files and your plugin. */ public class LocaleProvider { - Debugger debug = new Debugger(getClass()); + @NotNull Debugger debug = new Debugger(getClass()); @Getter @Setter private File localeFolder; @Getter - private ConcurrentHashMap loadedLocales = new ConcurrentHashMap<>(); + private final ConcurrentHashMap loadedLocales = new ConcurrentHashMap<>(); /** * The default locale to use when * - * @return {@link Locale} + * @return the Default locale */ @Getter private Locale defaultLocale; @@ -50,7 +46,9 @@ public class LocaleProvider { public LocaleProvider(@NotNull File localeFolder) { this.localeFolder = localeFolder; if (!localeFolder.exists()) - localeFolder.mkdir(); + if(!localeFolder.mkdir()){ + StickyAPI.getLogger().severe("Directory " + localeFolder.getPath() + " did not exist and could not be created!"); + } } /** @@ -90,7 +88,7 @@ public boolean loadLocale(@NotNull File file) { return false; } - Locale locale = new Locale(file); + @NotNull Locale locale = new Locale(file); if (!locale.getIsValid()) { debug.print("Encountered an error while loading the locale configuration - skipping load"); return false; @@ -112,7 +110,7 @@ public boolean loadLocale(@NotNull File file) { public int loadAllLocales() { int accumulator = 0; - for (File file : this.localeFolder.listFiles()) { + for (@NotNull File file : this.localeFolder.listFiles()) { if (!file.getName().endsWith(".yml")) continue; @@ -133,7 +131,7 @@ public int loadAllLocales() { * @param vars A map of variables to interpolate into the configured node value * @return {@link java.lang.String} */ - public String translate(@NotNull String node, @NotNull Map vars) { + public @Nullable String translate(@NotNull String node, @NotNull Map vars) { debug.reset(); if (node == null || node.equals("")) { @@ -141,7 +139,7 @@ public String translate(@NotNull String node, @NotNull Map vars) return null; } - String message = get(node); + @Nullable String message = get(node); if (message == null) { debug.print("node does not exist"); return null; @@ -160,11 +158,11 @@ public String translate(@NotNull String node, @NotNull Map vars) * @param vars A map of variables to interpolate into the configured node value * @return {@link java.lang.String} */ - public String translateNoColor(@NotNull String node, @NotNull Map vars) { + public @Nullable String translateNoColor(@NotNull String node, @NotNull Map vars) { if (node == null || node.equals("")) return null; - String message = get(node); + @Nullable String message = get(node); if (message == null) return null; @@ -180,7 +178,7 @@ public String translateNoColor(@NotNull String node, @NotNull Map node) { + public @Nullable String get(@NotNull Enum node) { return get(node.name().toLowerCase().replace("_", "-")); } @@ -207,7 +205,7 @@ public String get(@NotNull Enum node) { * @param node The configuration node to retrieve * @return {@link java.lang.String} */ - public String get(@NotNull String name, @NotNull String node) throws IllegalArgumentException { + public @Nullable String get(@NotNull String name, @NotNull String node) throws IllegalArgumentException { checkForLoadedLocale(name); return loadedLocales.get(name).get(node); } @@ -222,7 +220,7 @@ public String get(@NotNull String name, @NotNull String node) throws IllegalArgu * @param defaultValue The default value to use * @return {@link java.lang.String} */ - public String getDefault(@NotNull String node, @NotNull String defaultValue) { + public @Nullable String getDefault(@NotNull String node, @NotNull String defaultValue) { return get(node) == null ? defaultValue : get(node); } @@ -278,9 +276,9 @@ public void writeLocaleStream(@NotNull InputStream in, @NotNull String resourceP throw new IllegalArgumentException("The embedded resource '" + resourcePath + "' cannot be found"); } - File outFile = new File(localeFolder, resourcePath); + @NotNull File outFile = new File(localeFolder, resourcePath); int lastIndex = resourcePath.lastIndexOf('/'); - File outDir = new File(localeFolder, resourcePath.substring(0, lastIndex >= 0 ? lastIndex : 0)); + @NotNull File outDir = new File(localeFolder, resourcePath.substring(0, lastIndex >= 0 ? lastIndex : 0)); if (!outDir.exists()) { outDir.mkdirs(); @@ -291,8 +289,8 @@ public void writeLocaleStream(@NotNull InputStream in, @NotNull String resourceP return; } - OutputStream out = new FileOutputStream(outFile); - byte[] buf = new byte[1024]; + @NotNull OutputStream out = new FileOutputStream(outFile); + byte @NotNull [] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); @@ -316,7 +314,7 @@ private void checkForLoadedLocale(@NotNull String name) throws IllegalArgumentEx * * @return {@link java.util.TreeMap} */ - public TreeMap newVariables() { + public @NotNull TreeMap newVariables() { return new TreeMap(String.CASE_INSENSITIVE_ORDER); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Translation.java b/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Translation.java index b04ae45f..3290528e 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Translation.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/translation/Translation.java @@ -12,6 +12,10 @@ import java.util.TreeMap; import com.dumbdogdiner.stickyapi.common.util.TimeUtil; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * A class for parsing configurations @@ -19,7 +23,7 @@ public class Translation { // {VARIABLE|pluralize:"y,ies"} - private static String pluralize(String lvalue, String arg) { + private static String pluralize(String lvalue, @NotNull String arg) { String singular = "", plural = (arg.isEmpty() ? "s" : arg); if (arg.contains(",")) { String values[] = arg.trim().split(","); @@ -33,7 +37,7 @@ private static String pluralize(String lvalue, String arg) { return plural; } - private static String yesno(String lvalue, String arg) { + private static String yesno(String lvalue, @NotNull String arg) { if (arg.isEmpty()) return Boolean.valueOf(lvalue) ? "yes" : "no"; else if (arg.contains(",")) @@ -58,7 +62,7 @@ else if (arg.contains(",")) * function in the map below as: * {@code datetime(Variables.get("TimeBanned"), "HH:MM:SS")} */ - public static TreeMap> functions = new TreeMap>( + public static @NotNull TreeMap> functions = new TreeMap>( String.CASE_INSENSITIVE_ORDER) { { put("pluralize", (String lvalue, String arg) -> { @@ -152,7 +156,7 @@ public static boolean isxdigit(char ch) { * @param message Message containing sequences of `chars` in it * @return {@link java.lang.String}. */ - public static String translateColors(String chars, String message) { + public static @Nullable String translateColors(@Nullable String chars, @Nullable String message) { if (message == null) return null; @@ -163,7 +167,7 @@ public static String translateColors(String chars, String message) { if (!message.contains(chars)) return message; - StringBuilder retstr = new StringBuilder(message); + @NotNull StringBuilder retstr = new StringBuilder(message); for (int pos = message.indexOf(chars); pos != -1; pos = message.indexOf(chars, pos)) { if (pos + 1 > message.length()) break; @@ -196,12 +200,12 @@ public static String translateColors(String chars, String message) { * placeholders and their functions * @return {@link java.lang.String} */ - public static String translateVariables(LocaleProvider locale, String message, Map Variables) { + public static @NotNull String translateVariables(@NotNull LocaleProvider locale, @NotNull String message, @Nullable Map Variables) { // If it doesn't have the starting char for variables, skip it. if (!message.contains("{") || Variables == null) return message; - String retstr = message; + @NotNull String retstr = message; // Try and iterate over all our variables. for (int pos = retstr.indexOf("{"), pos2 = retstr.indexOf("}", pos); pos != -1 && pos2 != -1; pos = retstr.indexOf("{", pos + 1), pos2 = retstr.indexOf("}", pos + 1)) { @@ -210,8 +214,8 @@ public static String translateVariables(LocaleProvider locale, String message, M break; // Substring. - String variable = retstr.substring(pos + 1, pos2); - String replacement = null; + @NotNull String variable = retstr.substring(pos + 1, pos2); + @Nullable String replacement = null; // If the variable contains a | (verticle bar), then we tokenize on `|` and // treat the lvalue as a variable and the rvalue as a function name. The @@ -220,7 +224,7 @@ public static String translateVariables(LocaleProvider locale, String message, M // like conditionally pluralize words and such in the config. if (variable.contains("|")) { String values[] = variable.split("\\|"); - String rvalue = values[1], lvalue = values[0].trim(); + @NotNull String rvalue = values[1], lvalue = values[0].trim(); // Allow recursive locale nodes. String value = Variables.get(lvalue); @@ -230,7 +234,7 @@ public static String translateVariables(LocaleProvider locale, String message, M if (rvalue.contains(":")) { int nextsplit = rvalue.indexOf(":"); rvalue = rvalue.substring(0, nextsplit); - String argument = values[1].substring(nextsplit + 2, values[1].length() - 1); + @NotNull String argument = values[1].substring(nextsplit + 2, values[1].length() - 1); replacement = functions.get(rvalue.trim()).apply(value, argument); } else // (Functions.containsKey(rvalue.trim()) && @@ -265,9 +269,9 @@ public static String translateVariables(LocaleProvider locale, String message, M * @param Variables A list of variables to be parsed by the placeholder * @return {@link java.lang.String} */ - public static String translate(LocaleProvider locale, String message, String ColorChars, - Map Variables) { - String retstr = Translation.translateVariables(locale, message, Variables); + public static @Nullable String translate(@NotNull LocaleProvider locale, @NotNull String message, String ColorChars, + Map Variables) { + @Nullable String retstr = Translation.translateVariables(locale, message, Variables); retstr = Translation.translateColors(ColorChars, retstr); return retstr; } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/user/StickyUser.java b/src/main/java/com/dumbdogdiner/stickyapi/common/user/StickyUser.java new file mode 100644 index 00000000..5841a7ce --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/user/StickyUser.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.user; + +import com.dumbdogdiner.stickyapi.common.cache.Cacheable; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +/** + * A base user class to eliminate some code duplication + */ +public abstract class StickyUser implements Cacheable { + @Getter + protected UUID uniqueId; + + @Getter + protected @Nullable String name; + + /** + * The normal/default constructor for a {@link StickyUser} + * @param uniqueId the {@link UUID} for the user + */ + public StickyUser(@NotNull UUID uniqueId) { + this.uniqueId = uniqueId; + this.name = null; + } + + /** + * Create a {@link StickyUser} from another {@link StickyUser} + * @param stickyUser the {@link StickyUser} to convert to this type of {@link StickyUser} + */ + public StickyUser(@NotNull StickyUser stickyUser){ + uniqueId = stickyUser.getUniqueId(); + name = stickyUser.getName(); + } + + /** + * For use by extending classes only + * @param uniqueId the {@link UUID} of the user + * @param userName the name of the user + */ + protected StickyUser(@NotNull UUID uniqueId, @NotNull String userName) { + this.uniqueId = uniqueId; + this.name = userName; + } + + @Override + public @NotNull String getKey() { + return uniqueId.toString(); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/Debugger.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/Debugger.java index b865be47..a3b31669 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/Debugger.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/Debugger.java @@ -4,15 +4,13 @@ */ package com.dumbdogdiner.stickyapi.common.util; -import java.util.Random; -import java.util.logging.Logger; - -import javax.annotation.Nullable; - +import lombok.Getter; +import lombok.Setter; import org.jetbrains.annotations.NotNull; -import lombok.Setter; -import lombok.Getter; +import javax.annotation.Nullable; +import java.util.Random; +import java.util.logging.Logger; /** * Utility class for debugging. @@ -45,9 +43,9 @@ public class Debugger { @Getter private int logCount = 0; - private final Class clazz; + private final @NotNull Class clazz; - private Random r = new Random(); + private @NotNull Random r = new Random(); private final String ALPHABET = "3569abcde"; private final String COLOR = "\u00A7" + ALPHABET.charAt(r.nextInt(ALPHABET.length())); @@ -82,7 +80,7 @@ public void print(@Nullable Object object, @Nullable Object... args) { * * @return {@link Debugger} */ - public Debugger reset() { + public @NotNull Debugger reset() { startTime = System.nanoTime(); logCount = 0; return this; @@ -103,8 +101,8 @@ private int dddGetThisLineOfWhereverThisThingIsCalleduwu() { boolean thisOne = false; int thisOneCountDown = 1; StackTraceElement[] elements = Thread.currentThread().getStackTrace(); - for (StackTraceElement element : elements) { - String methodName = element.getMethodName(); + for (@NotNull StackTraceElement element : elements) { + @NotNull String methodName = element.getMethodName(); int lineNum = element.getLineNumber(); if (thisOne && (thisOneCountDown == 0)) { return lineNum; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/MemoryUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/MemoryUtil.java index 33379947..e6c12fd3 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/MemoryUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/MemoryUtil.java @@ -4,6 +4,9 @@ */ package com.dumbdogdiner.stickyapi.common.util; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -30,7 +33,7 @@ public enum Unit { * @param units The unit to return the size in * @return {@link Double} */ - public static Double getSizeOf(Object object, Unit units) { + public static @NotNull Double getSizeOf(Object object, @NotNull Unit units) { int bits = getSizeOf(object); return formatBits(bits, units); } @@ -42,7 +45,7 @@ public static Double getSizeOf(Object object, Unit units) { * @param units The unit to format `bits` with * @return {@link Double} */ - public static Double formatBits(int bits, Unit units) { + public static @NotNull Double formatBits(int bits, @NotNull Unit units) { switch (units) { case BITS: return (double) bits; @@ -63,7 +66,7 @@ public static Double formatBits(int bits, Unit units) { * @param object The object to get the size of * @return {@link Integer} */ - public static int getSizeOf(Object object) { + public static int getSizeOf(@Nullable Object object) { if (object == null) { return 0; } @@ -76,7 +79,7 @@ public static int getSizeOf(Object object) { return size; } - for (Field field : clazz.getDeclaredFields()) { + for (@NotNull Field field : clazz.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers())) { continue; } @@ -89,7 +92,7 @@ public static int getSizeOf(Object object) { /** * Get the size of a field on a given object. */ - private static int getFieldSize(Object object, Field field) { + private static int getFieldSize(Object object, @NotNull Field field) { try { // Java complains about illegal reflective access... Too bad! return getSizeOf(ReflectionUtil.getProtectedValue(object, field.getName())); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java index 9f64139f..ba24dbf1 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java @@ -45,27 +45,29 @@ private NumberUtil() { public static boolean isNumeric(@NotNull String string) { int size = string.length(); for (int i = 0; i < size; i++) { - if (Character.isDigit(string.charAt(i)) == false) { + if (!Character.isDigit(string.charAt(i))) { return false; } } return true; } + // TODO the percent stuff can be rewritten in a more compact manor + /** * Get a number as the percentage of another. * * @param x The number who's percentage of the total this method will * return * @param total The total - * @param string If this should return as a string with `%` appended to the end + * @param withSymbol If this should return as a string with `%` appended to the end * @return {@link Double} */ - public static String getPercentage(@NotNull double x, @NotNull double total, @NotNull Boolean string) { - var percent = (x / total); - StringBuilder sb = new StringBuilder(); - if (string) { - sb.append((percent * 100) + "%"); + public static @NotNull String getPercentage(@NotNull double x, @NotNull double total, boolean withSymbol) { + double percent = (x / total); + @NotNull StringBuilder sb = new StringBuilder(); + if (withSymbol) { + sb.append(percent * 100).append("%"); } else { sb.append((percent)); } @@ -79,7 +81,7 @@ public static String getPercentage(@NotNull double x, @NotNull double total, @No * @param total The total * @return {@link Double} */ - public static Double getPercentage(@NotNull int x, @NotNull int total) { + public static @NotNull Double getPercentage(@NotNull int x, @NotNull int total) { return Double.valueOf(getPercentage(x, total, false)); } @@ -90,7 +92,7 @@ public static Double getPercentage(@NotNull int x, @NotNull int total) { * @param total The total * @return {@link String} */ - public static String getPercentageString(@NotNull int x, @NotNull int total) { + public static @NotNull String getPercentageString(int x, int total) { return getPercentage(x, total, true); } @@ -106,10 +108,9 @@ public static String getPercentageString(@NotNull int x, @NotNull int total) { * @return {@link Integer} * @throws IllegalArgumentException when min is greater than max */ - public static int getRandomNumber(@NotNull int min, @NotNull int max) { - if (min >= max) - throw new IllegalArgumentException("Min may not be greater than max!"); - return (int) ((Math.random() * (max - min)) + min); + @Deprecated + public static int getRandomNumber(int min, int max) { + return MathUtil.randomInt(min, max); } /** diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/Paginator.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/Paginator.java index 4108b8e6..087c73e7 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/Paginator.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/Paginator.java @@ -4,12 +4,10 @@ */ package com.dumbdogdiner.stickyapi.common.util; -import java.lang.Math; -import java.util.List; - import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.List; @SuppressWarnings("unchecked") public class Paginator { @@ -24,7 +22,7 @@ public class Paginator { * @param objects array of objects to paginate * @param max maximum number of objects per page */ - public Paginator(@NotNull T[] objects, @NotNull Integer max) { + public Paginator(@NotNull T @NotNull [] objects, @NotNull Integer max) { this.objects = objects; this.pagSize = Double.valueOf(max); this.amountOfPages = (int) Math.ceil(objects.length / pagSize); @@ -110,8 +108,8 @@ public int getTotalPages() { * @param pageNum the page number. * @return List of objects that make up this page */ - public List getPage(@NotNull Integer pageNum) { - List page = new ArrayList<>(); + public @NotNull List getPage(@NotNull Integer pageNum) { + @NotNull List page = new ArrayList<>(); double total = objects.length / pagSize; amountOfPages = (int) Math.ceil(total); currentPage = pageNum; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/ShortID.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/ShortID.java index 154a130f..1192cf55 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/ShortID.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/ShortID.java @@ -4,13 +4,16 @@ */ package com.dumbdogdiner.stickyapi.common.util; +import org.apache.commons.lang.Validate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.util.Random; import java.util.UUID; import java.util.zip.CRC32; -import org.apache.commons.lang.Validate; -import org.jetbrains.annotations.NotNull; +// TODO: consider https://commons.apache.org/proper/commons-validator/apidocs/org/apache/commons/validator/routines/checkdigit/LuhnCheckDigit.html instead?? final class Luhn { private Luhn() { } @@ -21,12 +24,12 @@ private Luhn() { * @param card {@link String} card number * @return result {@link boolean} true of false */ - public static boolean luhnCheck(String card) { + public static boolean luhnCheck(@Nullable String card) { if (card == null || card.length() == 0) return false; char checkDigit = card.charAt(card.length() - 1); - String digit = calculateCheckDigit(card.substring(0, card.length() - 1)); + @Nullable String digit = calculateCheckDigit(card.substring(0, card.length() - 1)); return checkDigit == digit.charAt(0); } @@ -36,13 +39,13 @@ public static boolean luhnCheck(String card) { * @param card {@link String} number * @return {@link String} the check digit */ - public static String calculateCheckDigit(String card) { + public static @Nullable String calculateCheckDigit(@Nullable String card) { if (card == null) return null; String digit; /* convert to array of int for simplicity */ - int[] digits = new int[card.length()]; + int @NotNull [] digits = new int[card.length()]; for (int i = 0; i < card.length(); i++) digits[i] = Character.getNumericValue(card.charAt(i)); @@ -77,7 +80,7 @@ public class ShortID { private String id; public ShortID() { - Random r = new Random(); + @NotNull Random r = new Random(); this.id = generate(r.nextInt()).toString(); } @@ -97,22 +100,22 @@ public String toString() { * representation as described in toString * @return {@link ShortID} */ - public static ShortID fromString(String name) throws IllegalArgumentException { + public static @NotNull ShortID fromString(@NotNull String name) throws IllegalArgumentException { if (!validateID(name)) - throw new IllegalArgumentException("The provided String is not a valid Luhn-parseable ShortID"); + throw new IllegalArgumentException("The provided String is not a valid Luhn-parsable ShortID"); return new ShortID((name)); } - private static ShortID generateBase(int key) { + private static @NotNull ShortID generateBase(int key) { Validate.notNull(key, "key cannot be null"); - CRC32 crc = new CRC32(); + @NotNull CRC32 crc = new CRC32(); crc.update((int) System.nanoTime()); // Some kind of semi-unique identifier (like last database row insertion) crc.update(key); // Get the hash - String crc32hash = Long.toHexString(crc.getValue()); + @Nullable String crc32hash = Long.toHexString(crc.getValue()); // Calculate the Luhn check digit crc32hash += Luhn.calculateCheckDigit(crc32hash); // return. @@ -126,7 +129,7 @@ private static ShortID generateBase(int key) { * table) * @return a Luhn-passable identifier */ - public static ShortID generate(@NotNull int key) { + public static @NotNull ShortID generate(@NotNull int key) { return generateBase(key); } @@ -135,7 +138,7 @@ public static ShortID generate(@NotNull int key) { * * @return a Luhn-passable identifier */ - public static ShortID generate() { + public static @NotNull ShortID generate() { return generateBase(Integer.valueOf(UUID.randomUUID().toString().replace("[A-z\\-]", ""))); } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java index 516852d0..6776b3db 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java @@ -4,25 +4,27 @@ */ package com.dumbdogdiner.stickyapi.common.util; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.nio.charset.StandardCharsets; import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; import org.jetbrains.annotations.NotNull; /** * Operations on {@link java.lang.String} */ -public final class StringUtil { +public final class StringUtil extends StringUtils { private StringUtil() { } - private static HashMap leetReplace = new HashMap<>(); + private static final HashMap leetReplace = new HashMap<>(); // createProgressBar - format double percentage to no decimal places to avoid it // showing as '100.0000%' or something - private static DecimalFormat percentageFormatter = new DecimalFormat("#"); + private static final DecimalFormat percentageFormatter = new DecimalFormat("#"); static { leetReplace.put("0", "o"); @@ -53,10 +55,10 @@ private StringUtil() { * snuggly brackets * @return {@link String} */ - public static String createProgressBar(@NotNull double size, @NotNull double percentage, @NotNull boolean monospace, - @NotNull boolean includePercentage, @NotNull boolean includeBrackets) { + public static @NotNull String createProgressBar(double size, double percentage, boolean monospace, + boolean includePercentage, boolean includeBrackets) { double barCount = ((percentage / 100) * size); - StringBuilder barBuilder = new StringBuilder(); + @NotNull StringBuilder barBuilder = new StringBuilder(); for (double i = 0; i < size; i++) { if (i < barCount) { if (!monospace) @@ -88,7 +90,7 @@ public static String createProgressBar(@NotNull double size, @NotNull double per * @param percentage The percentage to fill the bar to * @return {@link String} */ - public static String createProgressBar(@NotNull double size, @NotNull double percentage) { + public static @NotNull String createProgressBar(double size, double percentage) { return createProgressBar(size, percentage, false, false, true); } @@ -110,8 +112,8 @@ public static String createProgressBar(@NotNull double size, @NotNull double per * @param keepCase Whether or not to keep the uppercase characters * @return {@link String} */ - public static String capitaliseSentence(@NotNull String string, @NotNull Boolean keepCase) { - StringBuilder sb = new StringBuilder(); + public static @NotNull String capitaliseSentence(@NotNull String string, @NotNull Boolean keepCase) { + @NotNull StringBuilder sb = new StringBuilder(); boolean cnl = true; for (char c : string.toCharArray()) { if (cnl && Character.isLetter(c)) { @@ -144,7 +146,7 @@ public static String capitaliseSentence(@NotNull String string, @NotNull Boolean * @param string The string to capitalise * @return {@link String} */ - public static String capitaliseSentence(@NotNull String string) { + public static @NotNull String capitaliseSentence(@NotNull String string) { return capitaliseSentence(string, false); } @@ -167,7 +169,7 @@ public static String capitaliseSentence(@NotNull String string) { * @param string The string to capitalise * @return {@link String} */ - public static String capitaliseSentenceKeepUpperCase(@NotNull String string) { + public static @NotNull String capitaliseSentenceKeepUpperCase(@NotNull String string) { return capitaliseSentence(string, true); } @@ -180,8 +182,8 @@ public static String capitaliseSentenceKeepUpperCase(@NotNull String string) { * @param regex The characters to not censor * @return {@link String} */ - public static String censorWord(@NotNull String word, @NotNull String regex) { - StringBuilder asterisks = new StringBuilder(); + public static @NotNull String censorWord(@NotNull String word, @NotNull String regex) { + @NotNull StringBuilder asterisks = new StringBuilder(); for (int i = 0; i < word.length(); i++) { if (String.valueOf(word.charAt(i)).matches(regex)) { @@ -203,7 +205,7 @@ public static String censorWord(@NotNull String word, @NotNull String regex) { * @param word The word to censor * @return {@link String} */ - public static String censorWord(@NotNull String word) { + public static @NotNull String censorWord(@NotNull String word) { return censorWord(word, "[ -/:-@\\[-`{-~¡-¿]"); } @@ -222,11 +224,11 @@ public static String censorWord(@NotNull String word) { * @param message The message to filter * @return {@link String} */ - public static String replaceLeet(@NotNull String message) { + public static @NotNull String replaceLeet(@NotNull String message) { if (message.trim().isEmpty()) return message; - for (Map.Entry entry : leetReplace.entrySet()) + for (Map.@NotNull Entry entry : leetReplace.entrySet()) message = message.replaceAll(entry.getKey(), entry.getValue()); return message; @@ -242,7 +244,7 @@ public static String replaceLeet(@NotNull String message) { * @param needles things that may match the comparison string * @return {@link Boolean} */ - public static boolean compareMany(@NotNull String haystack, @NotNull String[] needles) { + public static boolean compareMany(@NotNull String haystack, @NotNull String @NotNull [] needles) { for (String needle : needles) { if (haystack.equalsIgnoreCase(needle)) return true; @@ -278,7 +280,7 @@ public static boolean startsWithIgnoreCase(@NotNull final String string, @NotNul * @throws NullPointerException if uuid string is null * @throws IllegalArgumentException if uuid is not 32 characters and is invalid */ - public static UUID hyphenateUUID(@NotNull String uuid) { + public static @NotNull UUID hyphenateUUID(@NotNull String uuid) { if (uuid.length() == 32) { return UUID.fromString(uuid.replaceFirst( // https://stackoverflow.com/a/19399768 "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", @@ -287,4 +289,32 @@ public static UUID hyphenateUUID(@NotNull String uuid) { return UUID.fromString(uuid); } } + + public static @NotNull String unhyphenateUUID(@NotNull UUID uuid){ + return unhyphenate(uuid.toString()); + } + + public static @NotNull String unhyphenate(@NotNull String str){ + return str.replace("-",""); + } + + + /** + * Utility method to ensure Base64 encoding is done consistently. + * @param str The {@link String} to be encoded + * @return The encoded {@link String} + */ + public static @NotNull String encodeBase64(@NotNull String str) { + return new String(Base64.getEncoder().encode(str.getBytes(StandardCharsets.UTF_8))); + } + + /** + * Utility method to ensure Base64 decoding is done consistently. + * @param str A {@link String} containing the Base64-encoded data + * @return A string of UTF-8 Text decoded from the input + * @throws IllegalArgumentException if the Base64 decoding fails + */ + public static @NotNull String decodeBase64(String str) throws IllegalArgumentException { + return new String(Base64.getDecoder().decode(str), StandardCharsets.UTF_8); + } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java index 9bdee829..9b08944c 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/TextUtil.java @@ -4,12 +4,13 @@ */ package com.dumbdogdiner.stickyapi.common.util; -import java.util.HashMap; - import org.jetbrains.annotations.NotNull; +import java.util.HashMap; + public class TextUtil { - static HashMap characterWidths = new HashMap<>() { + @NotNull + private static final HashMap characterWidths = new HashMap<>() { { put('!', 1); put(',', 1); @@ -19,7 +20,6 @@ public class TextUtil { put(';', 1); put('i', 1); put('|', 1); - put('!', 1); put('`', 2); put('l', 2); @@ -46,13 +46,14 @@ public class TextUtil { }; // Uses info from: + /** * Get the width of a character (https://minecraft.gamepedia.com/Language#Font) - * - * @param c - * @return + * + * @param c The character + * @return the width in pixels */ - public static int getCharacterWidth(@NotNull char c) { + public static int getCharacterWidth(char c) { if (c < 32 || c > 126) { // Not presently implemented, would require rendering TTF return -1; diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java index b729daa8..ea935729 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java @@ -49,8 +49,8 @@ private TimeUtil() { * @param time in milliseconds (e.x. 1000 == 0.02/m) * @return {@link java.lang.String} */ - public static String significantDurationString(@NotNull final long time) { - StringBuilder message = new StringBuilder(); + public static @NotNull String significantDurationString(@NotNull final long time) { + @NotNull StringBuilder message = new StringBuilder(); double timeSince = (double) (System.currentTimeMillis() / 1000L) - ((double) (System.currentTimeMillis() / 1000L) - (time / 1000L) + 0.0); if ((timeSince /= 60.0) < 60.0) { @@ -76,7 +76,7 @@ public static String significantDurationString(@NotNull final long time) { * @param time in milliseconds (e.x. 1000 == 1 second) * @return {@link java.lang.String} */ - public static String durationString(@NotNull final long time) { + public static @NotNull String durationString(@NotNull final long time) { var t = time / 1000L; long years = t / 31449600; long weeks = (t / 604800) % 52; @@ -85,7 +85,7 @@ public static String durationString(@NotNull final long time) { long minutes = (t / 60) % 60; long seconds = t % 60; - List components = new ArrayList<>(); + @NotNull List components = new ArrayList<>(); if (years != 0) { components.add(String.format("%d %s", years, years != 1 ? "years" : "year")); @@ -105,7 +105,7 @@ public static String durationString(@NotNull final long time) { if (seconds != 0) { components.add(String.format("%d %s", seconds, seconds != 1 ? "seconds" : "second")); } - StringBuilder sb = new StringBuilder(); + @NotNull StringBuilder sb = new StringBuilder(); for (String str : components) { if (components.get(components.size() - 1) == str) { if (components.size() == 1) { @@ -128,7 +128,7 @@ public static String durationString(@NotNull final long time) { * @param timestamp to convert to a duration string * @return {@link java.lang.String} */ - public static String durationString(@NotNull Timestamp timestamp) { + public static @NotNull String durationString(@NotNull Timestamp timestamp) { return durationString(timestamp.getTime()); } @@ -140,7 +140,7 @@ public static String durationString(@NotNull Timestamp timestamp) { * @param timestamp to convert to a significant duration string * @return {@link java.lang.String} */ - public static String significantDurationString(@NotNull Timestamp timestamp) { + public static @NotNull String significantDurationString(@NotNull Timestamp timestamp) { return significantDurationString(timestamp.getTime()); } @@ -254,7 +254,7 @@ public static Optional duration(@NotNull String string) { * {@link java.text.SimpleDateFormat} */ @Deprecated - public static String timeString(@Nullable long t) { + public static @NotNull String timeString(@Nullable long t) { return TimeUtil.timeString(new Timestamp(t)); } @@ -269,7 +269,7 @@ public static String timeString(@Nullable long t) { * {@link java.text.SimpleDateFormat} */ @Deprecated - public static String timeString(@Nullable Timestamp ts) { + public static @NotNull String timeString(@Nullable Timestamp ts) { if (ts == null) return ""; return sdf.format(ts); @@ -281,7 +281,7 @@ public static String timeString(@Nullable Timestamp ts) { * @param timePeriod A duration string (eg, "2y1w10d40m6s") * @return {@link java.sql.Timestamp} */ - public static Timestamp toTimestamp(@NotNull String timePeriod) { + public static @org.jetbrains.annotations.Nullable Timestamp toTimestamp(@NotNull String timePeriod) { boolean compare = StringUtil.compareMany(timePeriod, new String[] { "*", "0" }); if (compare) return null; @@ -300,7 +300,7 @@ public static Timestamp toTimestamp(@NotNull String timePeriod) { return null; } - private static Timestamp _toTimestamp(String timePeriod) { + private static @org.jetbrains.annotations.Nullable Timestamp _toTimestamp(@NotNull String timePeriod) { Optional dur = TimeUtil.duration(timePeriod); if (dur.isPresent()) return ((TimeUtil.getUnixTime() + dur.get()) * 1000L) >= (253402261199L * 1000L) ? null @@ -322,7 +322,7 @@ public static long getUnixTime() { * * @return {@link java.sql.Timestamp} */ - public static Timestamp now() { + public static @NotNull Timestamp now() { return new Timestamp(TimeUtil.getUnixTime() * 1000L); } } diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/FieldUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/FieldUtil.java index 3ca24678..846648ca 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/FieldUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/FieldUtil.java @@ -11,7 +11,7 @@ /** * A class for manipulating {@link java.lang.reflect.Field Fields}. - * + * * @since 2.0 */ public final class FieldUtil { diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtil.java index 99866b42..2feabaae 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtil.java @@ -4,6 +4,9 @@ */ package com.dumbdogdiner.stickyapi.common.util.reflection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -18,17 +21,17 @@ public final class ReflectionUtil { private ReflectionUtil() { } - public static void setProtectedValue(Object o, String field, Object newValue) { + public static void setProtectedValue(@NotNull Object o, @NotNull String field, Object newValue) { setProtectedValue(o.getClass(), o, field, newValue); } - public static void setProtectedValue(Class c, String field, Object newValue) { + public static void setProtectedValue(@NotNull Class c, @NotNull String field, Object newValue) { setProtectedValue(c, null, field, newValue); } - public static void setProtectedValue(Class c, Object o, String field, Object newValue) { + public static void setProtectedValue(@NotNull Class c, Object o, @NotNull String field, Object newValue) { try { - Field f = c.getDeclaredField(field); + @NotNull Field f = c.getDeclaredField(field); f.setAccessible(true); if (Modifier.isFinal(f.getModifiers())) { @@ -45,17 +48,17 @@ public static void setProtectedValue(Class c, Object o, String field, Object } f.set(o, newValue); - } catch (NoSuchFieldException | IllegalAccessException ex) { + } catch (@NotNull NoSuchFieldException | IllegalAccessException ex) { System.out.println("*** " + c.getName() + ":" + ex); } } - public static T getProtectedValue(Object obj, String fieldName) { + public static @Nullable T getProtectedValue(@NotNull Object obj, String fieldName) { try { Class c = obj.getClass(); while (c != Object.class) { Field[] fields = c.getDeclaredFields(); - for (Field f : fields) { + for (@NotNull Field f : fields) { if (f.getName() == fieldName) { f.setAccessible(true); return (T) f.get(obj); @@ -71,9 +74,9 @@ public static T getProtectedValue(Object obj, String fieldName) { } } - public static T getProtectedValue(Class c, String field) { + public static @Nullable T getProtectedValue(@NotNull Class c, @NotNull String field) { try { - Field f = c.getDeclaredField(field); + @NotNull Field f = c.getDeclaredField(field); f.setAccessible(true); return (T) f.get(c); } catch (Exception ex) { @@ -82,20 +85,20 @@ public static T getProtectedValue(Class c, String field) { } } - public static Object invokeProtectedMethod(Class c, String method, Object... args) { + public static @Nullable Object invokeProtectedMethod(@NotNull Class c, @NotNull String method, Object... args) { return invokeProtectedMethod(c, null, method, args); } - public static Object invokeProtectedMethod(Object o, String method, Object... args) { + public static @Nullable Object invokeProtectedMethod(@NotNull Object o, @NotNull String method, Object... args) { return invokeProtectedMethod(o.getClass(), o, method, args); } - public static Constructor getProtectedConstructor(Class clazz, Class... params) { + public static @Nullable Constructor getProtectedConstructor(@NotNull Class clazz, Class... params) { Constructor c; try { c = clazz.getDeclaredConstructor(params); - } catch (NoSuchMethodException | SecurityException e) { + } catch (@NotNull NoSuchMethodException | SecurityException e) { e.printStackTrace(); return null; } @@ -110,9 +113,9 @@ public static Constructor getProtectedConstructor(Class clazz, Class... return c; } - public static Object invokeProtectedMethod(Class c, Object o, String method, Object... args) { + public static @Nullable Object invokeProtectedMethod(@NotNull Class c, Object o, @NotNull String method, Object @NotNull ... args) { try { - Class[] pTypes = new Class[args.length]; + Class @NotNull [] pTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Integer) pTypes[i] = int.class; @@ -120,7 +123,7 @@ public static Object invokeProtectedMethod(Class c, Object o, String method, pTypes[i] = args[i].getClass(); } - Method m = c.getDeclaredMethod(method, pTypes); + @NotNull Method m = c.getDeclaredMethod(method, pTypes); m.setAccessible(true); return m.invoke(o, args); } catch (Exception ex) { diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/UnsafeUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/UnsafeUtil.java index af460e8d..269a19ec 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/UnsafeUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/reflection/UnsafeUtil.java @@ -14,7 +14,7 @@ * A utility class for the Java internal {@link sun.misc.Unsafe Unsafe} class - * a class with low-level mechanisms designed only to be used by the core Java * library. - * + * * @since 2.0 */ public class UnsafeUtil { @@ -23,9 +23,9 @@ private UnsafeUtil() { /** * Get an instance of the Unsafe class. - * + * * This method uses reflection to avoid a SecurityException. - * + * * @return {@link sun.misc.Unsafe Unsafe} - an instance of the Unsafe class. * @throws NoSuchFieldException * @throws SecurityException diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/InvalidTextureException.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/InvalidTextureException.java new file mode 100644 index 00000000..ddba54c3 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/InvalidTextureException.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.util.textures; + +import java.text.MessageFormat; + +/* + * Exception + */ +public class InvalidTextureException extends RuntimeException { + public static final String EXCEPTION_MESSAGE = "The specified texture {0} was invalid"; + public InvalidTextureException(String textureString){ + super(MessageFormat.format(EXCEPTION_MESSAGE, textureString)); + } + + public InvalidTextureException(String textureString, Exception e){ + super(MessageFormat.format(EXCEPTION_MESSAGE, textureString), e); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureHelper.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureHelper.java new file mode 100644 index 00000000..7fc84cb4 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureHelper.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.util.textures; + +import com.dumbdogdiner.stickyapi.StickyAPI; +import com.dumbdogdiner.stickyapi.common.util.StringUtil; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import lombok.experimental.UtilityClass; +import okhttp3.OkHttpClient; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.net.URL; +import java.util.*; + +/** + * Contains various helper methods related to Textures, Texture Strings, and texture validation, as well as providing + * access to the bundled texture resources + */ +//TODO write javadoc +@UtilityClass +public class TextureHelper { + // Package-local visibility + static final OkHttpClient httpClient = new OkHttpClient(); + private static final Gson GSON = new Gson(); + private static final Yaml YAML = new Yaml(); + private static Map> TextureMap = generateTextureMap(); + + + + private static Map> generateTextureMap(){ + try { + try (InputStream textures = StickyAPI.getResourceAsStream("/generated/textures.json")) { + return GSON.fromJson(new InputStreamReader(textures),new TypeToken>>() {}.getType()); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("An unknown error occurred while accessing the builtin resource 'textures.json'.", e); + } catch (ClassCastException e) { + throw new RuntimeException("The integrated textures.json resource was invalid. Please check the format at compile-time, ensure that the resource is generated correctly. If you are a server owner, contact the developers of StickyAPI", e); + } + + } catch (RuntimeException e) { + Bukkit.getLogger().severe(e.getMessage()); + throw e; + } + } + + + /** + * Gets the {@link Set} of categories of textures in the Heads file + * + * @return A {@link Set} of all the categories of texture + */ + public static @NotNull Set getCategories() { + return TextureMap.keySet(); + } + + /** + * Get a {@link Set} of textures + * + * @param cat The {@link #getCategories() category}of textures to get the {@link Map} + * @return A {@link Map} of texture names to texture strings for the given category + * @see TextureHelper#getCategories() + */ + public static Map getTextureMapCategory(@NotNull String cat) { + return TextureMap.get(cat.toUpperCase()); + } + + + /** + * Get a list of all the textures for a given Category + * + * @param cat a Category of textures + * @return a {@link List} of the textures in a given Category + * @see #getCategories() + */ + public static @NotNull List getTextures(@NotNull String cat) { + return Collections.unmodifiableList(new ArrayList<>(getTextureMapCategory(cat).keySet())); + } + + /** + * Gets a texture string given a category and texture name + * + * @param cat The texture category + * @param name The texture name + * @return The texture string matching + * @throws NoSuchElementException if the specified texture is not found + */ + public static String getTexture(@NotNull String cat, @NotNull String name) throws NoSuchElementException { + return TextureMap.get(cat.toUpperCase()).get(name.toUpperCase()); + } + + /** + * Gets a texture string given a qualified name + * + * @param qualifiedName A given texture as a qualified name + * @return the texture string for the given texture + * @throws NoSuchElementException if the specified texture is not found + * @see #toQualifiedName(String, String) + */ + public static @Nullable String getTexture(@NotNull String qualifiedName) throws NoSuchElementException { + String[] splits = qualifiedName.split("\\."); + if (splits.length != 2 && splits.length != 1) + throw new RuntimeException("Invalid qualified name: " + qualifiedName); + if (splits[0].equals("*")) { + @Nullable String texture = null; + for (@NotNull String cat : getCategories()) { + try { + texture = getTexture(cat, qualifiedName); + } catch (NoSuchElementException e) { + continue; + } + return texture; + } + return texture; + } else { + return getTexture(splits[0], splits[1]); + } + } + + /** + * Returns a {@link List} of Qualified Names, of the format of "{CATEGORY}.{TEXTURE}" + * + * @return a {@link List} of Qualified Names of all textures + */ + public static @NotNull List getQualifiedNames() { + @NotNull ArrayList qualifiedNames = new ArrayList<>(); + for (@NotNull String cat : getCategories()) { + for (String texture : getTextureMapCategory(cat).keySet()) { + qualifiedNames.add(toQualifiedName(cat, texture)); + } + } + return Collections.unmodifiableList(qualifiedNames); + } + + /** + * @see TextureHelper#toTextureJson(URL) + */ + public static @NotNull JsonObject toTextureJson(@NotNull URL url) { + return toTextureJson(url.toExternalForm()); + } + + /** + * Constructs a JsonObject that represents a Mojang Textures JSON object. + * Note that it DOES NOT make sure the URL is safe or valid, as this is checked later. + * + *

A texture string is a Base64 encoded version of a JSON Object + *

+     * {
+     *     "textures": {
+     *        "SKIN": {
+     *            "url": <TEXTURE URL>
+     *        }
+     *    }
+     * }
+     * 
+ * + * @param url The URL of the texture file + * @return A {@link JsonObject} representing a JSON object with structure similar to the following example: + *
+     * {
+     *     "textures": {
+     *        "SKIN": {
+     *            "url": "http://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032"
+     *        }
+     *    }
+     * }
+     * 
+ */ + public static @NotNull JsonObject toTextureJson(String url) { + @NotNull JsonObject SKIN = new JsonObject(); + SKIN.addProperty("url", url); + + @NotNull JsonObject textures = new JsonObject(); + textures.add("SKIN", SKIN); + + @NotNull JsonObject root = new JsonObject(); + root.add("textures", textures); + + return root; + } + + /** + * Creates a Base64-Encoded String containing the URL where the texture can be located. + * + * @param url The URL of the texture + * @return A Base-64 encoded version of the JSON provided by {@link TextureHelper#toTextureJson(String)} + * @see TextureHelper#encodeJson(JsonObject) + */ + public static @NotNull String encodeTextureString(@NotNull URL url) throws InvalidTextureException { + TextureValidator.validateTextureUrl(url.toExternalForm()); + return encodeJson(toTextureJson(url)); + } + + /** + * Encodes JSON wrapping a texture in Base64 + * + * @param texture JSON that wraps the texture URL + * @return a base-64 encoded JSON that wraps a texture URL + * @throws InvalidTextureException if the JSON is invalid + */ + public static @NotNull String encodeJson(@NotNull JsonObject texture) throws InvalidTextureException { + + TextureValidator.validateTextureJson(texture); + return StringUtil.encodeBase64(GSON.toJson(texture)); + } + + /** + * Converts a texture string to a {@link JsonObject} + * + * @param texture The texture string + * @return a decoded {@link JsonObject} + */ + public static JsonObject decodeTextureStringToJson(@NotNull String texture) { + return JsonParser.parseString(texture).getAsJsonObject(); + } + + /** + * Converts a category and head to a qualified name (in the form of {category}.{name} + * + * @param category The category of head + * @param name The specified name + * @return a qualified name of the category and head + */ + public static @NotNull String toQualifiedName(String category, String name) { + return String.join(".", category, name).toUpperCase(); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureValidator.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureValidator.java new file mode 100644 index 00000000..79d9092d --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureValidator.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.util.textures; + +import com.dumbdogdiner.stickyapi.StickyAPI; +import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import lombok.experimental.UtilityClass; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.commons.lang.NullArgumentException; +import org.apache.commons.validator.routines.UrlValidator; +import org.jetbrains.annotations.NotNull; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.logging.Level; + +@UtilityClass +public class TextureValidator { + /** + * Byte array containing the file signature of a PNG image + */ + private static final byte[] PNG_SIGNATURE = new byte[] { + (byte) 0x89, + 'P', 'N', 'G', + (byte) 0x0D, (byte) 0x0A, (byte) 0x1A, (byte) 0x0A + }; + + /** + * Validates a URL to make sure it is a valid, loadable Minecraft texture + * @param textureURL the URL to test + * @return if the texture is valid + */ + public static boolean isValidTextureUrl(@NotNull String textureURL) { + try { + validateTextureUrl(textureURL); + return true; + } catch (InvalidTextureException e) { + StickyAPI.getLogger().log(Level.INFO, new InvalidTextureException(textureURL, e).getMessage()); + return false; + } + } + + /** + * Validates a URL to make sure it is a valid, loadable Minecraft texture + * @param textureURL the URL to test + * @throws InvalidTextureException if the texture is not valid + */ + public static void validateTextureUrl(@NotNull String textureURL) throws InvalidTextureException{ + try { + // Make sure URL scheme is valid + if (!UrlValidator.getInstance().isValid(textureURL)) { + throw new MalformedURLException("The texture URL " + textureURL + " is not a valid URL"); + } + + // Needs to be from minecraft.net or mojang.com + @NotNull String host = HttpUrl.get(textureURL).host().toLowerCase(); + if (!(host.endsWith("mojang.com") || host.endsWith("minecraft.net"))) { + throw new MalformedURLException("The texture URL " + textureURL + " specified a host other than minecraft.net or mojang.com"); + } + + // Needs to be resolvable + @NotNull Response resp = TextureHelper.httpClient.newCall(new Request.Builder().url(textureURL).build()).execute(); + if (resp.code() != 200) { + resp.close(); + throw new Exception("Non 200 OK Response of " + resp.code() + " received from " + textureURL); + } + + // Needs to have a content type of PNG + if (!"image/png".equalsIgnoreCase(resp.header("Content-Type"))) { + resp.close(); + throw new Exception("Unexpected format of " + resp.header("Content-Type") + " was received from " + textureURL); + } + + // Verify that the image is, in fact, an actual PNG, and a valid PNG + BufferedImage img; + try { + byte[] response = resp.body().bytes(); + + if ((img = ImageIO.read(new ByteArrayInputStream(response))) != null) { + for (int i = 0; i < PNG_SIGNATURE.length; i++) { + if (PNG_SIGNATURE[i] != response[i]) { + int j = i + 1; + throw new Exception("Byte " + j + " of the response (" + response[i] + ") did not match byte " + j + " of the PNG signature (" + PNG_SIGNATURE[i] + ")"); + } + } + + } else { + throw new NullArgumentException("The image retrieved from " + textureURL + " was decoded to null and" /* must not be null */); + } + } catch (IOException | NullArgumentException | NullPointerException e) { + throw new Exception("The content retrieved from " + textureURL + " was not a recognized image, was null, or was decoded to null", e); + } + + // Needs to be 64x32 or 64x64 + Preconditions.checkArgument(img.getWidth() == 64 && (img.getHeight() == 64 || img.getHeight() == 32), + "The texture must be either 64x64 (for a new texture) or 64x32 (for a classic texture)"); + + + } catch (Exception e) { + throw new InvalidTextureException(textureURL, e); + } + } + + + /** + * Checks a texture string as exhaustively as possible to validate a given String of a Base-64 encoded texture + * + * @param texture The string that represents the texture + * @throws InvalidTextureException if the texture is invalid + */ + public static void validateTextureString(String texture) throws InvalidTextureException { + @NotNull String decodedTexture = new String(Base64.getDecoder().decode(texture), StandardCharsets.UTF_8); + validateTextureJson(JsonParser.parseString(decodedTexture)); + + } + + /** + * Checks a json representation of a texture to make sure it is valid + * + * @param json JSON representing the texture + * @throws InvalidTextureException if the texture is invalid + */ + public static void validateTextureJson(@NotNull JsonElement json) throws InvalidTextureException{ + String textureURL = json + .getAsJsonObject().get("textures") + .getAsJsonObject().get("SKIN") + .getAsJsonObject().get("url") + .getAsString(); + + validateTextureUrl(textureURL); + } + + /** + * Checks a json representation of a texture to make sure it is valid + * + * @param json JSON representing the texture + * @return if the json is valid + */ + public static boolean isValidTextureJson(@NotNull JsonElement json) { + try { + validateTextureJson(json); + return true; + } catch (InvalidTextureException e) { + StickyAPI.getLogger().log(Level.INFO, new InvalidTextureException(json.getAsString(), e).getMessage()); + return false; + } + } + + /** + * Attempts to (as exhaustively as possible) validate a given String of a Base-64 encoded texture + * + * @param texture The string that represents the texture + * @return if the texture is valid or not + */ + public static boolean isValidTextureString(String texture) { + try { + validateTextureString(texture); + return true; + } catch (Exception e) { + StickyAPI.getLogger().log(Level.INFO, new InvalidTextureException(texture, e).getMessage()); + return false; + } + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/util/url/URLUtil.java b/src/main/java/com/dumbdogdiner/stickyapi/common/util/url/URLUtil.java index dde8d1c4..1e33b8ff 100644 --- a/src/main/java/com/dumbdogdiner/stickyapi/common/util/url/URLUtil.java +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/util/url/URLUtil.java @@ -4,15 +4,16 @@ */ package com.dumbdogdiner.stickyapi.common.util.url; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.hover.content.Text; +import org.bukkit.ChatColor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.TextComponent; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class URLUtil { private URLUtil() { @@ -30,8 +31,8 @@ private URLUtil() { * @param text The text that should be checked for URLs * @return {@link URLPair} */ - public static URLPair findURL(@NotNull String text) { - Matcher matcher = urlPattern.matcher(text); + public static @Nullable URLPair findURL(@NotNull String text) { + @NotNull Matcher matcher = urlPattern.matcher(text); if (matcher.find()) { return new URLPair(matcher.group(0), matcher.group(2)); @@ -49,9 +50,9 @@ public static URLPair findURL(@NotNull String text) { * @return {@link TextComponent} */ - public static TextComponent convertURLs(@NotNull String text) { - TextComponent finalComp = new TextComponent(); - TextComponent tmp = new TextComponent(); + public static @NotNull TextComponent convertURLs(@NotNull String text) { + @NotNull TextComponent finalComp = new TextComponent(); + @NotNull TextComponent tmp = new TextComponent(); String[] split = text.split(" "); int i = 0; @@ -65,7 +66,7 @@ public static TextComponent convertURLs(@NotNull String text) { finalComp.addExtra(tmp); tmp = new TextComponent(); - TextComponent urlComponent = new TextComponent(url.getShortened() + " "); + @NotNull TextComponent urlComponent = new TextComponent(url.getShortened() + " "); urlComponent.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url.getFullPath())); urlComponent.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("§7Click to open URL"), new Text("\n§8" + url.getFullPath()))); diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpConnectionException.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpConnectionException.java new file mode 100644 index 00000000..e0b70b77 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpConnectionException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis; + +import okhttp3.HttpUrl; +import okhttp3.Request; + +import java.io.IOException; +import java.net.URL; + +/** + * Runetime exception to wrap IOExceptions from HTTP requests + */ +public class HttpConnectionException extends HttpException { + public HttpConnectionException(URL url, IOException e){ + this(url.toExternalForm(), e); + } + + public HttpConnectionException(String url, IOException e) { + super("The request to " + url + " failed", e); + } + + public HttpConnectionException(Request request, IOException e) { + this(request.url().toString(), e); + } + + public HttpConnectionException(HttpUrl url, IOException e){ + this(url.toString(), e); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpException.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpException.java new file mode 100644 index 00000000..190598be --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpException.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis; + +import java.net.URL; + +public class HttpException extends RuntimeException { + public HttpException(URL url) { + this(url.toExternalForm()); + } + + public HttpException(String url) { + super("An error occurred while accessing " + url); + } + + protected HttpException(String str, Exception e) { + super(str, e); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpStatusException.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpStatusException.java new file mode 100644 index 00000000..12ada504 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/HttpStatusException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis; + +import okhttp3.Response; + +import java.net.URL; + +public class HttpStatusException extends HttpException { + public HttpStatusException(URL url, int responseCode) { + this(url.toExternalForm(), responseCode); + } + + public HttpStatusException(String url, int responseCode) { + super("An error occurred while accessing " + url + "; Response code of " + responseCode + " received."); + } + + public HttpStatusException(URL url, int responseCode, int expectedCode) { + this(url.toExternalForm(), responseCode, expectedCode); + } + + public HttpStatusException(String url, int responseCode, int expectedCode) { + super("An error occurred while accessing " + url + "; Response code of " + responseCode + " received, expected " + expectedCode); + } + + public HttpStatusException(Response resp) { + this(resp.request().url().toString(), resp.code()); + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/AshconResponse.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/AshconResponse.java new file mode 100644 index 00000000..cd7f9e5a --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/AshconResponse.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis.mojang; + +import lombok.Getter; +import lombok.SneakyThrows; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.*; + +/** + * Package-local utility class to easily convert the received object from the Cached API to a java object. + */ + +class AshconResponse { + protected String uuid; + + + @Getter + protected String username; + + + protected final @NotNull List username_history = new ArrayList<>(); + + public String getTextureString() { + return textures.raw.value; + } + + private static class Username { + @Getter + private String username; + private String changed_at; + + private @NotNull Instant getChangedAt() { + if(changed_at == null) { + // Happens for the very very first username, just set to 0 I guess? + return Instant.ofEpochMilli(0L); + } else { + return Instant.parse(changed_at); + } + } + + + @Override + public String toString(){ + return username; + } + } + + @Getter + private Texture textures; + static class Texture { + @Getter + private boolean custom; + @Getter + private boolean slim; + @Getter + private Skin skin; + + static class Skin { + private String url; + @SneakyThrows + public URL getUrl(){ + // The URL should always be valid! + return new URL(url); + } + private String data; + + public byte [] getData(){ + return Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8)); + } + } + + @Getter + Raw raw; + + static class Raw{ + @Getter + private String value; + @Getter + private String signature; + } + } + + private String created_at; + public @Nullable Instant getCreated(){ + if(created_at == null) + return null; + return Instant.parse(created_at); + } + + public @NotNull UUID getUniqueId(){ + return UUID.fromString(uuid); + } + + public @NotNull SortedMap getUsernameHistory(){ + TreeMap usernameHistory = new TreeMap<>(Instant::compareTo); + for(Username username : username_history) { + usernameHistory.put(username.getChangedAt(), username.getUsername()); + } + return usernameHistory; + } + +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/CachedMojangAPI.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/CachedMojangAPI.java new file mode 100644 index 00000000..a0ae05fc --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/CachedMojangAPI.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis.mojang; + +import com.dumbdogdiner.stickyapi.common.util.StringUtil; +import com.dumbdogdiner.stickyapi.common.webapis.HttpConnectionException; +import com.dumbdogdiner.stickyapi.common.webapis.HttpException; +import com.dumbdogdiner.stickyapi.common.webapis.HttpStatusException; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.UUID; + +//TODO: Better error handeling in case of 404 + +// FIXME POSSIBLE MAJOR BUG IN ASHCON: Sometimes the raw.value and the skin.data are inverted! +// Double check the occurances of this and whatnot, try to fix, etc. +public class CachedMojangAPI { + /** + * When possible, use the cached, faster api at https://api.ashcon.app/mojang/v2/user, otherwise use mojang + * API URLs + */ + + private static final @NotNull Gson G = new Gson(); + private static final @NotNull OkHttpClient HTTP_CLIENT = new OkHttpClient(); + + protected static final @NotNull HttpUrl COMBINED_API_URL = new HttpUrl.Builder() + .scheme("https") + .host("api.ashcon.app") + .addPathSegments("mojang/v2/user/") + .build(); + + + private static @NotNull Request buildAshconRequest(@NotNull UUID uniqueId) { + return new Request.Builder().url( + COMBINED_API_URL.newBuilder() + .addPathSegment(StringUtil.unhyphenateUUID(uniqueId)) + .build()) + .build(); + } + + private static @NotNull Response getResponse(UUID uniqueId) throws HttpException { + Request ashconRequest = buildAshconRequest(uniqueId); + try { + Response resp = HTTP_CLIENT.newCall(ashconRequest).execute(); + if (resp.code() == 200) + return resp; + throw new HttpStatusException(resp); + } catch (IOException e) { + throw new HttpConnectionException(ashconRequest, e); + } + } + + @Deprecated + public static @NotNull JsonObject getJsonResponse(UUID uniqueId) throws HttpException { + return JsonParser.parseReader(getResponse(uniqueId).body().charStream()).getAsJsonObject(); + } + + + private static @NotNull AshconResponse getAshconResponse(UUID uniqueId) throws HttpException { + return G.fromJson(getResponse(uniqueId).body().charStream(), AshconResponse.class); + } + + /** + * Gets the history of usernames that a given {@link UUID} has had, by time + * @param uniqueId The {@link UUID} of the user to check + * @return A {@link SortedMap} of the time of each change (With 1970-01-01T00:00:00Z meaning that it was the first Username) + * @throws HttpException if there is an error with the HTTP Request/Response + */ + public static @NotNull SortedMap getUsernameHistory(UUID uniqueId) throws HttpException { + return getAshconResponse(uniqueId).getUsernameHistory(); + + } + + + public static byte[] getTexture(@NotNull UUID uniqueId) throws HttpException { + return getAshconResponse(uniqueId).getTextures().getSkin().getData(); + } + + public static @Nullable String getTextureString(@NotNull UUID uniqueId) throws HttpException { + return getAshconResponse(uniqueId).getTextureString(); + } + + + public static @NotNull String getUsername(UUID uniqueId) throws HttpException { + return getAshconResponse(uniqueId).getUsername(); + } + + public static @Nullable UUID getUniqueId(String username) { + throw new UnsupportedOperationException(); + } + + public static @Nullable UUID getUniqueId(String username, Instant when) { + throw new UnsupportedOperationException(); + } + + + /** + * When possible, use the cached, faster api at https://api.ashcon.app/mojang/v2/user, otherwise use mojang + * API URLs + */ + + + private static final HttpUrl MOJANG_STATUS_BASE_URL = new HttpUrl.Builder() + .scheme("https") + .host("status.mojang.com") + .addPathSegment("check") + .build(); + @NotNull + protected static final HttpUrl MOJANG_API_BASE_URL = new HttpUrl.Builder() + .scheme("https") + .host("api.mojang.com") + .build(); + + protected static final String MOJANG_SESSION_URL = "https://sessionserver.mojang.com"; + + + public static @NotNull Map getMojangAPIStatus() throws HttpConnectionException { + @NotNull Map status = new HashMap<>(); + Request request = new Request.Builder().url(MOJANG_STATUS_BASE_URL).build(); + try { + Response resp = HTTP_CLIENT.newCall(request).execute(); + for (@NotNull JsonElement obj : JsonParser.parseString(resp.body().string()).getAsJsonArray()) { + for (Map.@NotNull Entry entry : obj.getAsJsonObject().entrySet()) { + status.put(entry.getKey(), MojangStatus.valueOf(entry.getValue().getAsString().toUpperCase())); + } + } + } catch (IOException e) { + throw new HttpConnectionException(request, e); + } + return status; + } + + public static @NotNull UUID uuidAtTime(@NotNull String username, @NotNull Instant timestamp) throws HttpException { + @NotNull HttpUrl url = MOJANG_API_BASE_URL.newBuilder() + .addPathSegments("users/profile/minecraft/") + .addPathSegment(username) + .addQueryParameter("at", Long.toString(timestamp.getEpochSecond())) + .build(); + try { + @NotNull Response resp = HTTP_CLIENT.newCall(new Request.Builder().url(url).build()).execute(); + if (resp.code() == HttpURLConnection.HTTP_OK) { + JsonObject responseJson = JsonParser.parseReader(resp.body().charStream()).getAsJsonObject(); + return StringUtil.hyphenateUUID(responseJson.get("id").getAsString()); + } else { + throw new HttpStatusException(resp); + } + } catch (IOException e) { + throw new HttpConnectionException(url, e); + } + } +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/MojangStatus.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/MojangStatus.java new file mode 100644 index 00000000..b355be67 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/MojangStatus.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis.mojang; + +public enum MojangStatus { + GREEN, + YELLOW, + RED +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/OldAPI.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/OldAPI.java new file mode 100644 index 00000000..297b291d --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/mojang/OldAPI.java @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis.mojang; + +//TODO: Better error handeling in case of 404 + +public class OldAPI { + + +} diff --git a/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/package-info.java b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/package-info.java new file mode 100644 index 00000000..b2a96a1d --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/common/webapis/package-info.java @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +/** + * This package contains code that is used for obtaining information from Mojang's web API or cached versions. Other APIs may be supported in the future. + */ +package com.dumbdogdiner.stickyapi.common.webapis; \ No newline at end of file diff --git a/src/main/java/com/dumbdogdiner/stickyapi/package-info.java b/src/main/java/com/dumbdogdiner/stickyapi/package-info.java new file mode 100644 index 00000000..729dc0a1 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickyapi/package-info.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +/** + *

StickyAPI

Utility methods, classes and potentially + * code-dupe-annihilating code for DDD plugins. + * + * Copyright (c) 2020 DumbDogDiner <dumbdogdiner.com> All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + * + * @author DumbDogDiner (dumbdogdiner.com) + * @version 2.0.0 + */ +package com.dumbdogdiner.stickyapi; \ No newline at end of file diff --git a/src/test/java/com/dumbdogdiner/stickyapi/bukkit/item/generator/SkullBuilderTest.java b/src/test/java/com/dumbdogdiner/stickyapi/bukkit/item/generator/SkullBuilderTest.java new file mode 100644 index 00000000..13bd0006 --- /dev/null +++ b/src/test/java/com/dumbdogdiner/stickyapi/bukkit/item/generator/SkullBuilderTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.bukkit.item.generator; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import com.destroystokyo.paper.profile.ProfileProperty; +import com.dumbdogdiner.stickyapi.bukkit.item.SkullBuilder; +import com.dumbdogdiner.stickyapi.common.util.textures.TextureHelper; +import com.dumbdogdiner.stickyapi.common.util.textures.TextureValidator; +import com.dumbdogdiner.stickyapi.mockedplugin.StickyAPIPlugin; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.junit.jupiter.api.*; + +import java.net.MalformedURLException; +import java.net.URL; + +class SkullBuilderTest { + static ServerMock svr; + + @BeforeAll + static void setup() { + svr = MockBukkit.mock(); + MockBukkit.createMockPlugin(); + svr.addPlayer(); + MockBukkit.load(StickyAPIPlugin.class); + //Bukkit.setServer(svr); + } + + @AfterAll + static void tearDown() { + MockBukkit.unmock(); + } + + /** + * For now, we can only test the property setting + */ + @Test + void buildSetup() { + SkullBuilder sb = new SkullBuilder(); + + Assertions.assertEquals(sb, sb.category("MHF")); + Assertions.assertEquals("MHF", sb.getCategory()); +// Assertions.assertEquals(sb, sb.filter("MHFAA")); +// Assertions.assertEquals("MHF", sb.getFilter()); + + Assertions.assertEquals(sb, sb.head("Alex")); + Assertions.assertEquals("ALEX", sb.getHead()); + +// Assertions.assertEquals(sb, sb.head("lmaono")); +// Assertions.assertEquals("MHF_ALEX", sb.getHead()); + + Assertions.assertEquals(sb, sb.quantity(5)); + Assertions.assertEquals(5, sb.getQuantity()); +// Assertions.assertEquals(sb, sb.quantity(99)); +// Assertions.assertEquals(5, sb.getQuantity()); +// Assertions.assertEquals(sb, sb.quantity(-1)); +// Assertions.assertEquals(5, sb.getQuantity()); + + Assertions.assertEquals(sb, sb.name("alexa")); + Assertions.assertEquals("alexa", sb.name()); + + try { + sb.texture(new URL("http://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032")); + } catch (MalformedURLException e) { + Assertions.fail(e.getMessage()); + e.printStackTrace(); + } + } + + + + + /** + * If mocked paper is ever available, this can be used, and thusly should not be ignored + */ + @Disabled("Mocked paper isn't a thing yet") + @Test + void build() { + + ItemStack head = new SkullBuilder().category("MHF").head("MHF_Alex").name("alexa").quantity(5).build(); + Assertions.assertEquals(5, head.getAmount()); + SkullMeta meta = (SkullMeta) head.getItemMeta(); + Assertions.assertEquals(Material.PLAYER_HEAD, head.getType()); + Assertions.assertTrue(meta.getPlayerProfile().hasTextures()); + for (ProfileProperty pp : meta.getPlayerProfile().getProperties()) { + if (pp.getName().equals("texture")) { + Assertions.assertEquals(TextureHelper.getTexture("MHF.MHF_Alex"), pp.getValue()); + Assertions.assertTrue(TextureValidator.isValidTextureString(pp.getValue())); + } + } + Assertions.assertEquals("alexa", meta.getDisplayName()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtilTest.java b/src/test/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtilTest.java index 0cb0d2f4..f0531a36 100644 --- a/src/test/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtilTest.java +++ b/src/test/java/com/dumbdogdiner/stickyapi/bukkit/util/ServerUtilTest.java @@ -2,59 +2,28 @@ * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. * Licensed under the MIT license, see LICENSE for more information... */ -package com.dumbdogdiner.stickyapi.bukkit.util; - -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import static org.mockito.Mockito.*; +package com.dumbdogdiner.stickyapi.bukkit.util; import com.destroystokyo.paper.Title; - -import org.bukkit.Bukkit; -import org.bukkit.Server; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.function.Consumer; +import com.dumbdogdiner.stickyapi_tests_common.BukkitCommon; +import org.junit.jupiter.api.Test; @SuppressWarnings({"rawtypes", "unchecked"}) public class ServerUtilTest { - private void getMockedBukkit(Consumer func) { - // Mock the Bukkit server - Server server = mock(Server.class); - - // Mock the Bukkit player - Player p1 = mock(Player.class); - //doNothing().when(p1).sendMessage(any(TextComponent.class)); - - // Create a new online player Collection - ArrayList c2 = new ArrayList(); - c2.add(p1); - try (MockedStatic mocked = mockStatic(Bukkit.class)) { - // When Bukkit.getServer().getOnlinePlayers() is called return the new collection - mocked.when(Bukkit::getServer).thenReturn(server); - when(server.getOnlinePlayers()).thenReturn(c2); - - func.accept(null); - - mocked.verify(Bukkit::getServer); - verify(server).getOnlinePlayers(); - } - } @Test public void testBroadcastMessage() { - getMockedBukkit(i -> { + BukkitCommon.getMockedBukkit(i -> { ServerUtil.broadcastMessage("hi"); }); } @Test public void testBroadcastTitle() { - getMockedBukkit(i -> { + BukkitCommon.getMockedBukkit(i -> { ServerUtil.broadcastTitle(Title.builder().title("Title").build()); }); } diff --git a/src/test/java/com/dumbdogdiner/stickyapi/common/util/StringUtilTest.java b/src/test/java/com/dumbdogdiner/stickyapi/common/util/StringUtilTest.java index 2e01cb0b..a6e111c2 100644 --- a/src/test/java/com/dumbdogdiner/stickyapi/common/util/StringUtilTest.java +++ b/src/test/java/com/dumbdogdiner/stickyapi/common/util/StringUtilTest.java @@ -9,10 +9,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; public class StringUtilTest { @Test diff --git a/src/test/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtilNoSuchFieldTest.java b/src/test/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtilNoSuchFieldTest.java index c2c97e18..87a25b98 100644 --- a/src/test/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtilNoSuchFieldTest.java +++ b/src/test/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtilNoSuchFieldTest.java @@ -8,11 +8,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; - import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class ReflectionUtilNoSuchFieldTest { private class ExampleClass { public ExampleClass() { @@ -38,6 +38,11 @@ public void restoreStreams() { System.setOut(originalOut); System.setErr(originalErr); + System.out.println("Restoring PrintStreams"); + + System.out.println(outContent.toString()); + System.err.println(errContent.toString()); + System.out.println("Restored PrintStreams."); } diff --git a/src/test/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtilTest.java b/src/test/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtilTest.java deleted file mode 100644 index a31d93b3..00000000 --- a/src/test/java/com/dumbdogdiner/stickyapi/common/util/reflection/ReflectionUtilTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. - * Licensed under the MIT license, see LICENSE for more information... - */ -package com.dumbdogdiner.stickyapi.common.util.reflection; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ReflectionUtilTest { - private class ExampleClass { - - private String privateString = "default_value"; - - protected String protectedString = "default_value"; - - private final String privateFinalString = "default_value"; - - private static final String privateStaticFinalString = "default_value"; - - public ExampleClass() { - }; - } - - @Test - public void testProtectedValuePrivate() { - ExampleClass instance = new ExampleClass(); - - assertEquals(instance.privateString, "default_value"); - - ReflectionUtil.setProtectedValue(instance, "privateString", "edited_value"); - - // assertEquals(ReflectionUtil.getProtectedValue(instance, "privateString"), - // "edited_value"); - assertEquals(instance.privateString, "edited_value"); - } - - @Test - public void testProtectedValuePrivateFinal() { - ExampleClass instance = new ExampleClass(); - - assertEquals(instance.privateFinalString, "default_value"); - - ReflectionUtil.setProtectedValue(instance, "privateFinalString", "edited_value"); - - assertEquals(ReflectionUtil.getProtectedValue(instance, "privateFinalString"), "edited_value"); - // Doesn't work here - have to use ReflectionUtil's get! - // assertEquals(instance.privateFinalString, "edited_value"); - } - - @Test - public void testProtectedValuePrivateStaticFinal() { - Class c = ExampleClass.class; - - assertEquals(ExampleClass.privateStaticFinalString, "default_value"); - - ReflectionUtil.setProtectedValue(c, "privateStaticFinalString", "edited_value"); - - assertEquals(ReflectionUtil.getProtectedValue(c, "privateStaticFinalString"), "edited_value"); - - assertEquals(ReflectionUtil.getProtectedValue(c, "privateStaticFinalString"), "edited_value"); - // Doesn't work here - have to use ReflectionUtil's get! - // assertEquals(c.privateStaticFinalString, "edited_value"); - } - - @Test - public void testProtectedValueProtected() { - ExampleClass instance = new ExampleClass(); - - assertEquals(instance.protectedString, "default_value"); - - ReflectionUtil.setProtectedValue(instance, "protectedString", "edited_value"); - - assertEquals(ReflectionUtil.getProtectedValue((Object) instance, "protectedString"), "edited_value"); - assertEquals(instance.protectedString, "edited_value"); - } - - @Test - public void testGetProtectedValueObjectNoSuchField() { - ExampleClass instance = new ExampleClass(); - - Object o = ReflectionUtil.getProtectedValue((Object) instance, "nonexistent_field"); - - assertEquals(o, null); - } -} diff --git a/src/test/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureValidatorTest.java b/src/test/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureValidatorTest.java new file mode 100644 index 00000000..b54b84ce --- /dev/null +++ b/src/test/java/com/dumbdogdiner/stickyapi/common/util/textures/TextureValidatorTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.util.textures; + +import com.dumbdogdiner.stickyapi_tests_common.TestsCommon; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class TextureValidatorTest { + @BeforeAll + static void setUp() { + TestsCommon.disableHandlers(); + //TestsCommon.addMaskedHandler(); + +// // Uncomment the following two lines to see if there are any leaks from OkHTTP +// Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE); +// Logger.getLogger(OkHttpClient.class.getName()).addHandler(new StreamHandler(System.err, new SimpleFormatter())); + } + + @AfterAll + static void tearDown() { + //TestsCommon.removeMaskedHandler(); + TestsCommon.enableHandlers(); + } + + @Test + void validateTexture() { + assertTrue(TextureValidator.isValidTextureString("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDViM2Y4Y2E0YjNhNTU1Y2NiM2QxOTQ0NDk4MDhiNGM5ZDc4MzMyNzE5NzgwMGQ0ZDY1OTc0Y2M2ODVhZjJlYSJ9fX0=")); + } + + @Test + void validateTextureBadBase64() { + String[] tests = { + // Base64 errors + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDViM2Y4Y2E0YjNhNTU1Y2NiM2QxOTQ0NDk4MDhiNGM5ZDc4MzMyNzE5NzgwMGQ0ZDY1OTc0Y2M2ODVhZjJlYSJ9fX0===", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh$dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDViM2Y4Y2E0YjNhNTU1Y2NiM2QxOTQ0NDk4MDhiNGM5ZDc4MzMyNzE5NzgwMGQ0ZDY1OTc0Y2M2ODVhZjJlYSJ9fX0=", + }; + for (String invalidTest : tests) { + System.out.println(invalidTest); + assertFalse(TextureValidator.isValidTextureString(invalidTest)); + } + } + + @Test + void validateTextureBadHTTP() { + String[] tests = { + // HTTP Errors + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly9jbGllbnRzMy5nb29nbGUuY29tL2dlbmVyYXRlXzIwNCJ9fX0=", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly9jbGllbnRzMy5nb29nbGUuY29tL2dlbmVyYXRlXzQwNCJ9fX0=", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDViM2Y4Y2E0YjNhNTU1Y2NiM2QxOTQ0NDk4MDhiNGM5ZDc4MzMyNzE5NzgwMGQ0ZDY1OTc0Y2M2ODVhZjJbYSJ9fX0=", + }; + testMultiFail(tests); + } + + @Test + void validateTextureBadJSON() { + String[] tests = { + // JSON Errors + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDViM2Y4Y2E0YjNhNTU1Y2NiM2QxOTQ0NDk4MDhiNGM5ZDc4MzMyNzE5NzgwMGQ0ZDY1OTc0Y2M2ODVhZjJlYS", + "eyJ0ZXh0dXJlcyI6eyJsb2wiOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9kNWIzZjhjYTRiM2E1NTVjY2IzZDE5NDQ0OTgwOGI0YzlkNzgzMzI3MTk3ODAwZDRkNjU5NzRjYzY4NWFmMmVhIn19fQ==", + "aGVsbG8=" + }; + testMultiFail(tests); + } + + @Test + void validateTextureBadURL() { + String[] tests = { + // URL format errors + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6InNzaDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9kNWIzZjhjYTRiM2E1NTVjY2IzZDE5NDQ0OTgwOGI0YzlkNzgzMzI3MTk3ODAwZDRkNjU5NzRjYzY4NWFmMmVhIn19fQ==", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6ImZvbyJ9fX0=" + }; + testMultiFail(tests); + } + + @Test + void validateTextureBadContentType() { + String[] tests = { + // Content type errors + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly9jYXB0aXZlLmFwcGxlLmNvbS9ob3RzcG90LWRldGVjdC5odG1sIn19fQ==", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHBzOi8vYXBpLmFzaGNvbi5hcHAvbW9qYW5nL3YyL3VzZXIvTm90Y2gifX19" + }; + testMultiFail(tests); + } + + @Test + void validateBadImageType() { + String[] tests = { + // Image type errors + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHBzOi8vd3d3LmdzdGF0aWMuY29tL3dlYnAvZ2FsbGVyeS8xLmpwZyJ9fX0=", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHBzOi8vd3d3LmdzdGF0aWMuY29tL3dlYnAvZ2FsbGVyeS8xLndlYnAifX19", + }; + testMultiFail(tests); + } + + @Test + void validateBadImageData() { + String[] tests = { + // Invalid PNG files + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94czFuMGcwMS5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94czJuMGcwMS5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94czRuMGcwMS5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94czduMGcwMS5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94Y3JuMGcwNC5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94bGZuMGcwNC5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94YzFuMGcwOC5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94YzluMmMwOC5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94ZDBuMmMwOC5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94ZDNuMmMwOC5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94ZDluMmMwOC5wbmcifX19", + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly93d3cuc2NoYWlrLmNvbS9wbmdzdWl0ZS94ZHRuMGcwMS5wbmcifX19" + }; + testMultiFail(tests); + } + + + private void testMultiFail(String[] tests) { + for (String invalidTest : tests) { + System.out.println("Now Testing invalid texture " + invalidTest); + assertFalse(TextureValidator.isValidTextureString(invalidTest)); + } + } + + @Test + void getCategories() { + } + + @Test + void getTexturesCategory() { + } + + @Test + void getTexture() { + assertEquals("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTlmMjlmNGFjZDRlNGIyOGVjMGMxYjcyMjU4ZGEzZDM1ZTNiNmE3MWI1Yjk4ZmNlZWFlYjhiYTllMmE2In19fQ==", TextureHelper.getTexture("GEM.AVENTURINE")); + } + + @Test + void validateAllTextures() { + for(String qn : TextureHelper.getQualifiedNames()){ + System.out.println("Testing texture for " + qn + " (" + TextureHelper.getTexture(qn) + ")"); + TextureValidator.validateTextureString(TextureHelper.getTexture(qn)); + assertTrue(TextureValidator.isValidTextureString(TextureHelper.getTexture(qn))); + } + } + + @Test + void getTextures() { + } +} \ No newline at end of file diff --git a/src/test/java/com/dumbdogdiner/stickyapi/common/webapis/CachedMojangAPITest.java b/src/test/java/com/dumbdogdiner/stickyapi/common/webapis/CachedMojangAPITest.java new file mode 100644 index 00000000..ce9e077f --- /dev/null +++ b/src/test/java/com/dumbdogdiner/stickyapi/common/webapis/CachedMojangAPITest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis; + +import com.dumbdogdiner.stickyapi.common.util.textures.TextureValidator; +import com.dumbdogdiner.stickyapi.common.webapis.mojang.CachedMojangAPI; +import com.dumbdogdiner.stickyapi.common.webapis.mojang.MojangStatus; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CachedMojangAPITest { + @Test + void getSkinTexture() { + assertTrue(TextureValidator.isValidTextureString(CachedMojangAPI.getTextureString(UUID.fromString("6ab43178-89fd-4905-97f6-0f67d9d76fd9")))); +// System.out.println(new MojangAPI(UUID.fromString("ffffffff-f53b-49d1-b8c4-ffffffffffff")).getSkinTexture()); + } + + @SuppressWarnings("deprecation") + @Test + void getFullJsonCombinedAPI() { + System.out.println(CachedMojangAPI.getJsonResponse(UUID.fromString("9b6d27b3-f53b-49d1-b8c4-fa807f7575e9")).toString()); + } + + @Test + void getUsernameHistory() { + Map response = CachedMojangAPI.getUsernameHistory(UUID.fromString("9b6d27b3-f53b-49d1-b8c4-fa807f7575e9")); + for(Map.Entry entry : response.entrySet()){ + String dateStr = entry.getKey() == null ? "" :entry.getKey().toString(); + System.out.println("Name: " + entry.getValue() + "; date: " + dateStr); + } + } + + @Test + void getUsername() { + + + assertEquals("MHF_Alex", CachedMojangAPI.getUsername(UUID.fromString("6ab43178-89fd-4905-97f6-0f67d9d76fd9"))); + } + + + @Test + void getMojangAPIStatus() { + Map status = CachedMojangAPI.getMojangAPIStatus(); + assertTrue(status.size() > 0); + + + + System.out.println("Current status of Mojang APIs:"); + for (Map.Entry singleStat: status.entrySet()){ + System.out.println(singleStat.getKey() + ": " + singleStat.getValue().toString()); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/dumbdogdiner/stickyapi/common/webapis/MojangAPITest.java b/src/test/java/com/dumbdogdiner/stickyapi/common/webapis/MojangAPITest.java new file mode 100644 index 00000000..a8681faf --- /dev/null +++ b/src/test/java/com/dumbdogdiner/stickyapi/common/webapis/MojangAPITest.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.common.webapis; + +import com.dumbdogdiner.stickyapi.common.webapis.mojang.OldAPI; +import com.dumbdogdiner.stickyapi.common.webapis.mojang.MojangStatus; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class MojangAPITest { + +} \ No newline at end of file diff --git a/src/test/java/com/dumbdogdiner/stickyapi/mockedplugin/StickyAPIPlugin.java b/src/test/java/com/dumbdogdiner/stickyapi/mockedplugin/StickyAPIPlugin.java new file mode 100644 index 00000000..25a41f30 --- /dev/null +++ b/src/test/java/com/dumbdogdiner/stickyapi/mockedplugin/StickyAPIPlugin.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.mockedplugin; + +import com.dumbdogdiner.stickyapi.bukkit.util.StartupUtil; +import com.dumbdogdiner.stickyapi.common.translation.LocaleProvider; +import lombok.Getter; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.java.JavaPluginLoader; + +import java.io.File; + +public class StickyAPIPlugin extends JavaPlugin { + @Getter + private LocaleProvider localeProvider; + public StickyAPIPlugin() { + super(); + } + + protected StickyAPIPlugin(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { + super(loader, description, dataFolder, file); + } + + @Override + public void onLoad() { + + } + + @Override + public void onEnable() { + if (!StartupUtil.setupConfig(this)) + return; + + this.localeProvider = StartupUtil.setupLocale(this, this.localeProvider); + if (this.localeProvider == null) + return; + + // Do more stuff?? + getLogger().info("StickyAPI Dummy Plugin started successfully!"); + } + + @Override + public void onDisable() { + + } +} diff --git a/src/test/java/com/dumbdogdiner/stickyapi_tests_common/BukkitCommon.java b/src/test/java/com/dumbdogdiner/stickyapi_tests_common/BukkitCommon.java new file mode 100644 index 00000000..5171ad5f --- /dev/null +++ b/src/test/java/com/dumbdogdiner/stickyapi_tests_common/BukkitCommon.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi_tests_common; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.mockito.MockedStatic; + +import java.util.ArrayList; +import java.util.function.Consumer; + +import static org.mockito.Mockito.*; + +@SuppressWarnings({"rawtypes", "unchecked", "ResultOfMethodCallIgnored"}) +public class BukkitCommon { + public static void getMockedBukkit(Consumer func) { + // Mock the Bukkit server + Server server = mock(Server.class); + + // Mock the Bukkit player + Player p1 = mock(Player.class); + //doNothing().when(p1).sendMessage(any(TextComponent.class)); + + // Create a new online player Collection + ArrayList c2 = new ArrayList(); + c2.add(p1); + + try (MockedStatic mocked = mockStatic(Bukkit.class)) { + // When Bukkit.getServer().getOnlinePlayers() is called return the new collection + + mocked.when(Bukkit::getServer).thenReturn(server); + + when(server.getOnlinePlayers()).thenReturn(c2); + + func.accept(null); + + mocked.verify(Bukkit::getServer); + verify(server).getOnlinePlayers(); + } + } +} diff --git a/src/test/java/com/dumbdogdiner/stickyapi_tests_common/TestsCommon.java b/src/test/java/com/dumbdogdiner/stickyapi_tests_common/TestsCommon.java index d8476940..0a5bdb3c 100644 --- a/src/test/java/com/dumbdogdiner/stickyapi_tests_common/TestsCommon.java +++ b/src/test/java/com/dumbdogdiner/stickyapi_tests_common/TestsCommon.java @@ -4,6 +4,15 @@ */ package com.dumbdogdiner.stickyapi_tests_common; +import com.dumbdogdiner.stickyapi.StickyAPI; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Handler; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; +import java.util.logging.StreamHandler; + public class TestsCommon { // Enums are counted in code coverage for some reason - so this function just invlokes valueOf on all enums it can find in a enum class. // https://stackoverflow.com/questions/4512358/emma-coverage-on-enum-types/4548912 @@ -16,4 +25,37 @@ public static void superficialEnumCodeCoverage(Class> enumClas throw new RuntimeException(e); } } + + private static Handler maskedHandler = new StreamHandler(System.out, new SimpleFormatter()); + private static List handlers = new ArrayList<>(); + + + public static void disableHandlers() { + Logger l = StickyAPI.getLogger(); + l.setUseParentHandlers(false); + for (Handler h : l.getHandlers()) { + handlers.add(h); + l.removeHandler(h); + } + } + + public static void enableHandlers() { + Logger l = StickyAPI.getLogger(); + l.setUseParentHandlers(true); + for(Handler h : handlers){ + l.addHandler(h); + } + + + } + + public static void addMaskedHandler(){ + Logger l = StickyAPI.getLogger(); + l.addHandler(maskedHandler); + } + + public static void removeMaskedHandler(){ + Logger l = StickyAPI.getLogger(); + l.removeHandler(maskedHandler); + } } diff --git a/src/test/resources/TestUsers.csv b/src/test/resources/TestUsers.csv new file mode 100644 index 00000000..f55e2d83 --- /dev/null +++ b/src/test/resources/TestUsers.csv @@ -0,0 +1,4 @@ +"Username", "UUID" +MHF_Alex, +MHF_Steve, +, \ No newline at end of file diff --git a/src/test/resources/config.yml b/src/test/resources/config.yml new file mode 100644 index 00000000..7eb03d5b --- /dev/null +++ b/src/test/resources/config.yml @@ -0,0 +1,16 @@ +# +# Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. +# Licensed under the MIT license, see LICENSE for more information... +# + +database: + host: localhost + port: 3306 + name: dbname + username: root + password: root + table-prefix: stickyapi_ + # Maximum number of times mysql will try to reconnect before giving up. + max-reconnects: 5 + # Use SSL? + use-ssl: false \ No newline at end of file diff --git a/src/test/resources/messages.en_us.yml b/src/test/resources/messages.en_us.yml new file mode 100644 index 00000000..c1ab99f5 --- /dev/null +++ b/src/test/resources/messages.en_us.yml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. +# Licensed under the MIT license, see LICENSE for more information... +# + +# Messages.yml - All plugin text is in this file +# +# Anything between curley brackets is replaced with the values of the named variables. + +# Global variables, you can use these in any message in this file. +newline: "&8&l» " +prefix: "&b&lStickyAPI {newline}" +network-name: "&bDumb Dog Diner" +website: "dumbdogdiner.com" + +# Common error messages used throughout the plugin +invalid-syntax: "{prefix}&cInvalid Syntax! Please use &f{syntax}&c!" +server-error: "{prefix}&cThe server encountered an error, please try again later." +no-permission: "{prefix}&cError! Permission denied!" +player-does-not-exist: "{prefix}&cError! The player {bad_user} does not exist!" +player-has-not-joined: "{prefix}&cError! The player {bad_user} has not joined before!" +must-be-player: "{prefix}&cYou must be a player to execute this command!" +not-online-player: "{prefix}&c Error! {PLAYER} is not online!" +invalid-group: "{prefix}&cError! Group {GROUP} is invalid!" + diff --git a/src/test/resources/plugin.yml b/src/test/resources/plugin.yml new file mode 100644 index 00000000..feb5cb30 --- /dev/null +++ b/src/test/resources/plugin.yml @@ -0,0 +1,10 @@ +# +# Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. +# Licensed under the MIT license, see LICENSE for more information... +# + +name: StickyApiTest +main: com.dumbdogdiner.stickyapi.mockedplugin.StickyAPIPlugin +version: 1.0 +api-version: 1.16 +#load: STARTUP \ No newline at end of file