3
0
Mirror von https://github.com/TheSilentPro/HeadDB.git synchronisiert 2024-12-27 11:20:05 +01:00

fuck buildnumber maven

Dieser Commit ist enthalten in:
Silent 2022-11-06 15:07:15 +01:00
Ursprung 0ef246aa68
Commit 1677c5d2e4
28 geänderte Dateien mit 510 neuen und 88 gelöschten Zeilen

2
.gitignore vendored
Datei anzeigen

@ -3,6 +3,8 @@ target/
src/test/ src/test/
dependency-reduced-pom.xml dependency-reduced-pom.xml
build.properties
build/
.classpath .classpath
.project .project

Datei anzeigen

@ -14,7 +14,7 @@ copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

Datei anzeigen

@ -10,4 +10,4 @@ You may report issues on the [Issue Tracker](https://github.com/TheSilentPro/Hea
# API # API
All API methods can be found in the [HeadAPI](https://github.com/TheSilentPro/HeadDB/blob/master/src/main/java/tsp/headdb/api/HeadAPI.java) class. <br> All API methods can be found in the [HeadAPI](https://github.com/TheSilentPro/HeadDB/blob/master/src/main/java/tsp/headdb/api/HeadAPI.java) class. <br>
Alternatevly you may view the [javadocs](https://javadocs.pages.dev/headdb/4.0.0/tsp/headdb/api/HeadAPI). Alternatively you may view the [javadocs](https://javadocs.pages.dev/headdb/4.0.0/tsp/headdb/api/HeadAPI).

99
pom.xml
Datei anzeigen

@ -9,14 +9,22 @@
<version>5.0.0</version> <version>5.0.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>HeadDB</name>
<description>Database with thousands of heads</description>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<build.author>TheSilentPro (Silent)</build.author>
<build.timestamp>${maven.build.timestamp}</build.timestamp>
</properties> </properties>
<name>HeadDB</name> <scm>
<description>Database with thousands of heads</description> <connection>scm:git:git@github.com:TheSilentPro/HeadDB.git</connection>
<developerConnection>scm:git:git@github.com:TheSilentPro/HeadDB.git</developerConnection>
<url>git@github.com:TheSilentPro/HeadDB.git</url>
</scm>
<repositories> <repositories>
<repository> <repository>
@ -63,15 +71,10 @@
</dependency> </dependency>
<!-- Hard Dependencies (Shaded) --> <!-- Hard Dependencies (Shaded) -->
<dependency>
<groupId>net.wesjd</groupId>
<artifactId>anvilgui</artifactId>
<version>1.5.3-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.github.TheSilentPro</groupId> <groupId>com.github.TheSilentPro</groupId>
<artifactId>SmartPlugin</artifactId> <artifactId>SmartPlugin</artifactId>
<version>efc139dd46</version> <version>e565e96a72</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.TheSilentPro</groupId> <groupId>com.github.TheSilentPro</groupId>
@ -101,7 +104,8 @@
</dependencies> </dependencies>
<build> <build>
<finalName>HeadDB</finalName> <finalName>${project.name}</finalName>
<resources> <resources>
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
@ -110,6 +114,51 @@
</resources> </resources>
<plugins> <plugins>
<!-- Compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- Buildnumber -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>build-properties</id>
<phase>validate</phase>
<goals>
<goal>create</goal>
<goal>create-metadata</goal>
</goals>
</execution>
</executions>
<configuration>
<doCheck>true</doCheck>
<doUpdate>true</doUpdate>
<buildNumberPropertiesFileLocation>${basedir}/build/buildNumber.properties</buildNumberPropertiesFileLocation>
<outputDirectory>${basedir}/build</outputDirectory>
<outputName>build.properties</outputName>
<useLastCommittedRevision>true</useLastCommittedRevision>
<shortRevisionLength>7</shortRevisionLength>
<revisionOnScmFailure>0</revisionOnScmFailure>
<items>
<item>buildNumber</item>
<item>timestamp</item>
<item>scmVersion</item>
</items>
</configuration>
</plugin>
<!-- Shade --> <!-- Shade -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@ -136,7 +185,37 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version> <version>3.4.1</version>
<executions>
<execution>
<id>javadoc-generate</id>
<phase>package</phase>
<goals>
<goal>javadoc</goal>
</goals>
</execution>
</executions>
<configuration>
<show>public</show>
<doclint>none</doclint>
<additionalDependencies>
<additionalDependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.19.2-R0.1-SNAPSHOT</version>
</additionalDependency>
<additionalDependency>
<groupId>com.github.TheSilentPro</groupId>
<artifactId>SmartPlugin</artifactId>
<version>e565e96a72</version>
</additionalDependency>
<additionalDependency>
<groupId>com.github.TheSilentPro</groupId>
<artifactId>Warehouse</artifactId>
<version>f40f72cb19</version>
</additionalDependency>
</additionalDependencies>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>

Datei anzeigen

@ -2,14 +2,20 @@ package tsp.headdb;
import tsp.headdb.core.command.CommandCategory; import tsp.headdb.core.command.CommandCategory;
import tsp.headdb.core.command.CommandHelp; import tsp.headdb.core.command.CommandHelp;
import tsp.headdb.core.command.CommandInfo;
import tsp.headdb.core.command.CommandLanguage;
import tsp.headdb.core.command.CommandMain; import tsp.headdb.core.command.CommandMain;
import tsp.headdb.core.command.CommandManager; import tsp.headdb.core.command.CommandManager;
import tsp.headdb.core.command.CommandSearch; import tsp.headdb.core.command.CommandSearch;
import tsp.headdb.core.command.CommandSettings;
import tsp.headdb.core.command.CommandTexture;
import tsp.headdb.core.command.CommandUpdate;
import tsp.headdb.core.listener.PlayerJoinListener; import tsp.headdb.core.listener.PlayerJoinListener;
import tsp.headdb.core.storage.Storage; import tsp.headdb.core.storage.Storage;
import tsp.headdb.core.task.UpdateTask; import tsp.headdb.core.task.UpdateTask;
import tsp.headdb.core.util.BuildProperties;
import tsp.smartplugin.SmartPlugin; import tsp.smartplugin.SmartPlugin;
import tsp.smartplugin.inventory.PaneListener; import tsp.smartplugin.inventory.PaneListener;
import tsp.smartplugin.localization.TranslatableLocalization; import tsp.smartplugin.localization.TranslatableLocalization;
@ -22,6 +28,7 @@ public class HeadDB extends SmartPlugin {
private static HeadDB instance; private static HeadDB instance;
private PluginLogger logger; private PluginLogger logger;
private BuildProperties buildProperties;
private TranslatableLocalization localization; private TranslatableLocalization localization;
private CommandManager commandManager; private CommandManager commandManager;
private Storage storage; private Storage storage;
@ -31,19 +38,19 @@ public class HeadDB extends SmartPlugin {
instance = this; instance = this;
instance.logger = new PluginLogger(this, getConfig().getBoolean("debug")); instance.logger = new PluginLogger(this, getConfig().getBoolean("debug"));
instance.logger.info("Loading HeadDB - " + instance.getDescription().getVersion()); instance.logger.info("Loading HeadDB - " + instance.getDescription().getVersion());
instance.buildProperties = new BuildProperties(this);
instance.saveDefaultConfig(); instance.saveDefaultConfig();
instance.commandManager = new CommandManager();
new UpdateTask(getConfig().getLong("refresh", 3600L)).schedule(this); new UpdateTask(getConfig().getLong("refresh", 3600L)).schedule(this);
instance.logger.info("Loaded " + loadLocalization() + " languages!"); instance.logger.info("Loaded " + loadLocalization() + " languages!");
instance.storage = new Storage(); instance.storage = new Storage();
instance.storage.load(); //instance.storage.load();
new PaneListener(this); new PaneListener(this);
new PlayerJoinListener(); new PlayerJoinListener();
instance.commandManager = new CommandManager();
loadCommands(); loadCommands();
//noinspection ConstantConditions //noinspection ConstantConditions
instance.getCommand("headdb").setExecutor(new CommandMain()); instance.getCommand("headdb").setExecutor(new CommandMain());
@ -74,6 +81,11 @@ public class HeadDB extends SmartPlugin {
new CommandHelp().register(); new CommandHelp().register();
new CommandCategory().register(); new CommandCategory().register();
new CommandSearch().register(); new CommandSearch().register();
new CommandUpdate().register();
new CommandTexture().register();
new CommandLanguage().register();
new CommandSettings().register();
new CommandInfo().register();
} }
public Storage getStorage() { public Storage getStorage() {
@ -88,6 +100,10 @@ public class HeadDB extends SmartPlugin {
return localization; return localization;
} }
public BuildProperties getBuildProperties() {
return buildProperties;
}
public PluginLogger getLog() { public PluginLogger getLog() {
return logger; return logger;
} }

Datei anzeigen

@ -11,8 +11,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class HeadAPI { public final class HeadAPI {
@ -20,21 +18,25 @@ public final class HeadAPI {
private static final HeadDatabase database = new HeadDatabase(HeadDB.getInstance(), HeadProvider.HEAD_STORAGE); private static final HeadDatabase database = new HeadDatabase(HeadDB.getInstance(), HeadProvider.HEAD_STORAGE);
public static synchronized Optional<Head> getById(int id) { public static synchronized Optional<Head> getHeadById(int id) {
return getHeads().filter(head -> head.getId() == id).findFirst(); return getHeads().stream().filter(head -> head.getId() == id).findAny();
} }
public static synchronized Stream<Head> getHeads() { public static synchronized Optional<Head> getHeadByTexture(String texture) {
return getHeads().stream().filter(head -> head.getTexture().equals(texture)).findAny();
}
public static List<Head> getHeads() {
List<Head> result = new ArrayList<>(); List<Head> result = new ArrayList<>();
for (Category category : getHeadsMap().keySet()) { for (Category category : getHeadsMap().keySet()) {
result.addAll(getHeads(category).collect(Collectors.toList())); result.addAll(getHeads(category));
} }
return result.stream(); return result;
} }
public static synchronized Stream<Head> getHeads(Category category) { public static List<Head> getHeads(Category category) {
return getHeadsMap().get(category).stream(); return getHeadsMap().get(category);
} }
public static synchronized Map<Category, List<Head>> getHeadsMap() { public static synchronized Map<Category, List<Head>> getHeadsMap() {
@ -42,7 +44,7 @@ public final class HeadAPI {
} }
public static int getTotalHeads() { public static int getTotalHeads() {
return database.getSize(); return getHeads().size();
} }
public static HeadDatabase getDatabase() { public static HeadDatabase getDatabase() {

Datei anzeigen

@ -2,23 +2,19 @@ package tsp.headdb.core.command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import tsp.headdb.HeadDB; import tsp.headdb.HeadDB;
import tsp.headdb.core.api.HeadAPI; import tsp.headdb.core.api.HeadAPI;
import tsp.headdb.core.util.Utils; import tsp.headdb.core.util.Utils;
import tsp.headdb.implementation.category.Category; import tsp.headdb.implementation.category.Category;
import tsp.headdb.implementation.head.Head; import tsp.headdb.implementation.head.Head;
import tsp.smartplugin.inventory.Button; import tsp.smartplugin.inventory.PagedPane;
import tsp.smartplugin.inventory.paged.PagedPane;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class CommandCategory extends SubCommand { public class CommandCategory extends SubCommand {
public CommandCategory() { public CommandCategory() {
super("open", new String[]{"category", "c"}); super("open", new String[]{"o"});
} }
@Override @Override
@ -35,17 +31,17 @@ public class CommandCategory extends SubCommand {
return; return;
} }
int page = 1; int page = 0;
if (args[2] != null) { if (args.length >= 3) {
try { try {
page = Integer.parseInt(args[2]); page = Integer.parseInt(args[2]) - 1;
} catch (NumberFormatException ignored) {} } catch (NumberFormatException ignored) {}
} }
List<Head> heads = HeadAPI.getHeads(category).collect(Collectors.toList()); List<Head> heads = HeadAPI.getHeads(category);
PagedPane main = new PagedPane(6, 6, Utils.translateTitle(getLocalization().getMessage(player.getUniqueId(), "menu.category.name").orElse(category.getName()), heads.size(), category.getName())); PagedPane main = Utils.createPaged(player, Utils.translateTitle(getLocalization().getMessage(player.getUniqueId(), "menu.category.name").orElse(category.getName()), heads.size(), category.getName()));
Utils.addHeads(player, category, main, heads); Utils.addHeads(player, category, main, heads);
if (page > main.getPageAmount()) { if (page < 0 || page > main.getPageAmount()) {
getLocalization().sendMessage(player.getUniqueId(), "invalidPageIndex", msg -> msg.replace("%pages%", String.valueOf(main.getPageAmount()))); getLocalization().sendMessage(player.getUniqueId(), "invalidPageIndex", msg -> msg.replace("%pages%", String.valueOf(main.getPageAmount())));
return; return;
} }

Datei anzeigen

@ -6,7 +6,7 @@ import tsp.smartplugin.player.PlayerUtils;
public class CommandHelp extends SubCommand { public class CommandHelp extends SubCommand {
public CommandHelp() { public CommandHelp() {
super("help"); super("help", new String[]{"h"});
} }
@Override @Override
@ -15,7 +15,13 @@ public class CommandHelp extends SubCommand {
PlayerUtils.sendMessage(sender, "&7Format: /hdb &9<sub-command>(aliases) &c<parameters> &7- Description"); PlayerUtils.sendMessage(sender, "&7Format: /hdb &9<sub-command>(aliases) &c<parameters> &7- Description");
PlayerUtils.sendMessage(sender, "&7Required: &c<> &7| Optional: &b[]"); PlayerUtils.sendMessage(sender, "&7Required: &c<> &7| Optional: &b[]");
PlayerUtils.sendMessage(sender, " "); PlayerUtils.sendMessage(sender, " ");
PlayerUtils.sendMessage(sender, "&7/hdb &9open(category|c) &c<category> &b[page] &7- Open a specific category."); PlayerUtils.sendMessage(sender, "&7/hdb &9info(i) &7- Show plugin information.");
PlayerUtils.sendMessage(sender, "&7/hdb &9open(o) &c<category> &b[page] &7- Open a specific category.");
PlayerUtils.sendMessage(sender, "&7/hdb &9search(s) &b(id:|tg:)&c<query> &7- Search for specific heads.");
PlayerUtils.sendMessage(sender, "&7/hdb &9update(u) &7- Manually update the database.");
PlayerUtils.sendMessage(sender, "&7/hdb &9language(l) &7- Change your language.");
PlayerUtils.sendMessage(sender, "&7/hdb &9settings(st) &7- Open the settings menu.");
PlayerUtils.sendMessage(sender, "&7/hdb &9texture(t) &7- Get the texture for the head your item.");
PlayerUtils.sendMessage(sender, "&7<===============================================================>"); PlayerUtils.sendMessage(sender, "&7<===============================================================>");
} }

Datei anzeigen

@ -0,0 +1,27 @@
package tsp.headdb.core.command;
import org.bukkit.command.CommandSender;
import tsp.headdb.HeadDB;
import tsp.headdb.core.util.BuildProperties;
import tsp.smartplugin.player.PlayerUtils;
public class CommandInfo extends SubCommand {
public CommandInfo() {
super("info", new String[]{"i"});
}
@Override
public void handle(CommandSender sender, String[] args) {
if (HeadDB.getInstance().getConfig().getBoolean("showAdvancedPluginInfo")) {
BuildProperties build = HeadDB.getInstance().getBuildProperties();
PlayerUtils.sendMessage(sender, "&7Running &6HeadDB - " + build.getVersion() + " &7(&6Build " + build.getBuildNumber() + "&7)");
PlayerUtils.sendMessage(sender, "&7Created by &6" + HeadDB.getInstance().getDescription().getAuthors());
PlayerUtils.sendMessage(sender, "&7Compiled on &6" + build.getTimestamp() + " &7by &6" + build.getAuthor());
} else {
PlayerUtils.sendMessage(sender, "&7Running &6HeadDB &7by &6TheSilentPro (Silent)");
PlayerUtils.sendMessage(sender, "&7GitHub: &6https://github.com/TheSilentPro/HeadDB");
}
}
}

Datei anzeigen

@ -0,0 +1,57 @@
package tsp.headdb.core.command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tsp.smartplugin.localization.Message;
import java.util.Set;
public class CommandLanguage extends SubCommand {
public CommandLanguage() {
super("language", new String[]{"l", "lang"});
}
@Override
public void handle(CommandSender sender, String[] args) {
String lang = "en";
if (args.length < 1) {
getLocalization().sendMessage(new Message()
.receiver(sender)
.text("invalidArguments")
);
return;
}
if (!getLocalization().getData().containsKey(lang)) {
getLocalization().sendMessage(new Message()
.receiver(sender)
.text("invalidLanguage")
.function(msg -> msg.replace("%languages%", toString(getLocalization().getData().keySet()))));
return;
}
if (!(sender instanceof Player player)) {
getLocalization().setConsoleLanguage(lang);
} else {
getLocalization().setLanguage(player.getUniqueId(), lang);
}
getLocalization().sendMessage(new Message().receiver(sender).text("languageChanged").function(msg -> msg.replace("%language%", lang)));
}
private String toString(Set<String> set) {
String[] array = set.toArray(new String[0]);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < array.length; i++) {
builder.append(array[i]);
if (i < array.length - 1) {
builder.append(",");
}
}
return builder.toString();
}
}

Datei anzeigen

@ -1,7 +1,6 @@
package tsp.headdb.core.command; package tsp.headdb.core.command;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -9,14 +8,12 @@ import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.RemoteConsoleCommandSender; import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import tsp.headdb.HeadDB; import tsp.headdb.HeadDB;
import tsp.headdb.core.api.HeadAPI; import tsp.headdb.core.api.HeadAPI;
import tsp.headdb.core.util.Utils; import tsp.headdb.core.util.Utils;
import tsp.headdb.implementation.category.Category; import tsp.headdb.implementation.category.Category;
import tsp.smartplugin.builder.item.ItemBuilder;
import tsp.smartplugin.inventory.Button; import tsp.smartplugin.inventory.Button;
import tsp.smartplugin.inventory.single.Pane; import tsp.smartplugin.inventory.Pane;
import tsp.smartplugin.localization.TranslatableLocalization; import tsp.smartplugin.localization.TranslatableLocalization;
import tsp.smartplugin.utils.Validate; import tsp.smartplugin.utils.Validate;
@ -49,7 +46,7 @@ public class CommandMain extends HeadDBCommand implements CommandExecutor {
for (Category category : Category.VALUES) { for (Category category : Category.VALUES) {
pane.addButton(new Button(category.getItem(player.getUniqueId()), e -> { pane.addButton(new Button(category.getItem(player.getUniqueId()), e -> {
if (e.isLeftClick()) { if (e.isLeftClick()) {
Bukkit.dispatchCommand(e.getWhoClicked(), "hdb open " + category.name()); Bukkit.dispatchCommand(e.getWhoClicked(), "hdb open " + category.getName());
} else if (e.isRightClick()) { } else if (e.isRightClick()) {
// todo: specific page // todo: specific page
} }

Datei anzeigen

@ -4,7 +4,6 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream;
public class CommandManager { public class CommandManager {

Datei anzeigen

@ -5,10 +5,10 @@ import org.bukkit.entity.Player;
import tsp.headdb.core.api.HeadAPI; import tsp.headdb.core.api.HeadAPI;
import tsp.headdb.core.util.Utils; import tsp.headdb.core.util.Utils;
import tsp.headdb.implementation.head.Head; import tsp.headdb.implementation.head.Head;
import tsp.smartplugin.inventory.paged.PagedPane; import tsp.smartplugin.inventory.PagedPane;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class CommandSearch extends SubCommand { public class CommandSearch extends SubCommand {
@ -25,16 +25,38 @@ public class CommandSearch extends SubCommand {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (int i = 1; i < args.length; i++) { for (int i = 1; i < args.length; i++) {
builder.append(args[i]).append(" "); builder.append(args[i]);
if (i != args.length - 1) {
builder.append(" ");
}
} }
String query = builder.toString(); final String query = builder.toString();
getLocalization().sendMessage(player.getUniqueId(), "searchCommand", msg -> msg.replace("%query%", query));
List<Head> heads = HeadAPI.getHeads().filter(head -> Utils.matches(head.getName(), query)).collect(Collectors.toList()); List<Head> heads = new ArrayList<>();
PagedPane main = new PagedPane(6, 6, Utils.translateTitle(getLocalization().getMessage(player.getUniqueId(), "menu.search.name").orElse("&cHeadDB - &eSearch Results"), heads.size(), "None", query)); List<Head> headList = HeadAPI.getHeads();
if (query.length() > 3) {
if (query.startsWith("id:")) {
try {
HeadAPI.getHeadById(Integer.parseInt(query.substring(3))).ifPresent(heads::add);
} catch (NumberFormatException ignored) {
}
} else if (query.startsWith("tg:")) {
heads.addAll(headList.stream().filter(head -> Utils.matches(head.getTags(), query.substring(3))).toList());
} else {
// no query prefix
heads.addAll(headList.stream().filter(head -> Utils.matches(head.getName(), query.substring(3))).toList());
}
} else {
// query is <=3, no point in looking for prefixes
heads.addAll(headList.stream().filter(head -> Utils.matches(head.getName(), query.substring(3))).toList());
}
getLocalization().sendMessage(player.getUniqueId(), "searchCommand", msg -> msg.replace("%query%", query));
PagedPane main = Utils.createPaged(player, Utils.translateTitle(getLocalization().getMessage(player.getUniqueId(), "menu.search.name").orElse("&cHeadDB - &eSearch Results"), heads.size(), "None", query));
Utils.addHeads(player, null, main, heads); Utils.addHeads(player, null, main, heads);
getLocalization().sendMessage(player.getUniqueId(), "searchCommandResults", msg -> msg.replace("%size%", String.valueOf(heads.size())).replace("%query%", query)); getLocalization().sendMessage(player.getUniqueId(), "searchCommandResults", msg -> msg.replace("%size%", String.valueOf(heads.size())).replace("%query%", query));
main.open(player);
} }
} }

Datei anzeigen

@ -0,0 +1,47 @@
package tsp.headdb.core.command;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tsp.headdb.core.util.Utils;
import tsp.smartplugin.builder.item.ItemBuilder;
import tsp.smartplugin.inventory.Button;
import tsp.smartplugin.inventory.PagedPane;
import tsp.smartplugin.inventory.Pane;
import tsp.smartplugin.utils.StringUtils;
import java.util.Set;
public class CommandSettings extends SubCommand {
public CommandSettings() {
super("settings", new String[]{"st"});
}
@Override
public void handle(CommandSender sender, String[] args) {
if (!(sender instanceof Player player)) {
getLocalization().sendConsoleMessage("noConsole");
return;
}
Set<String> langs = getLocalization().getData().keySet();
Pane pane = new Pane(1, StringUtils.colorize(getLocalization().getMessage(player.getUniqueId(), "menu.settings.name").orElse("&cHeadDB - Settings")));
pane.addButton(new Button(new ItemBuilder(Material.BOOK)
.name(getLocalization().getMessage(player.getUniqueId(), "menu.settings.language.name").orElse("&cLanguage"))
.setLore(getLocalization().getMessage(player.getUniqueId(), "menu.settings.language.available").orElse("&7Languages Available: &e%size%").replace("%size%", String.valueOf(langs.size())))
.build(), e -> {
PagedPane langPane = new PagedPane(4, 6, Utils.translateTitle(getLocalization().getMessage(player.getUniqueId(), "menu.settings.language.title").orElse("&cHeadDB &7- &eSelect Language").replace("%languages%", "%size%"), langs.size(), "Selector: Language"));
for (String lang : langs) {
langPane.addButton(new Button(new ItemBuilder(Material.PAPER)
.name(getLocalization().getMessage(player.getUniqueId(), "menu.settings.language.format").orElse(ChatColor.YELLOW + lang).replace("%language%", lang))
.build(), langEvent -> {
getLocalization().setLanguage(player.getUniqueId(), lang);
getLocalization().sendMessage(player.getUniqueId(), "languageChanged", msg -> msg.replace("%language%", lang));
}));
}
}));
}
}

Datei anzeigen

@ -0,0 +1,32 @@
package tsp.headdb.core.command;
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.command.CommandSender;
import org.bukkit.entity.Player;
import tsp.headdb.core.util.Utils;
public class CommandTexture extends SubCommand {
public CommandTexture() {
super("texture", new String[]{"t"});
}
@Override
public void handle(CommandSender sender, String[] args) {
if (!(sender instanceof Player player)) {
getLocalization().sendConsoleMessage("noConsole");
return;
}
Utils.getTexture(player.getInventory().getItemInMainHand()).ifPresentOrElse(texture -> getLocalization().getMessage(player.getUniqueId(), "itemTexture").ifPresent(message -> {
TextComponent component = new TextComponent(message.replace("%texture%", texture));
component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(getLocalization().getMessage(player.getUniqueId(), "copyTexture").orElse("Click to copy!"))));
component.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, texture));
player.spigot().sendMessage(component);
}), () -> getLocalization().sendMessage("itemNoTexture"));
}
}

Datei anzeigen

@ -0,0 +1,27 @@
package tsp.headdb.core.command;
import org.bukkit.command.CommandSender;
import tsp.headdb.core.api.HeadAPI;
import tsp.smartplugin.localization.Message;
public class CommandUpdate extends SubCommand {
public CommandUpdate() {
super("update", new String[]{"u"});
}
@Override
public void handle(CommandSender sender, String[] args) {
getLocalization().sendMessage(new Message()
.receiver(sender)
.text("updateDatabase")
);
HeadAPI.getDatabase().update((time, heads) -> getLocalization().sendMessage(new Message()
.receiver(sender)
.text("updateDatabaseDone")
.function(msg -> msg.replace("%size%", String.valueOf(heads.size()).replace("%time%", String.valueOf(time)))))
);
}
}

Datei anzeigen

@ -61,6 +61,7 @@ public class HeadStorage extends SQLiteDataManager<Collection<Head>> {
)); ));
} }
//noinspection StringOperationCanBeSimplified
return sendPreparedUpdate("INSERT OR REPLACE INTO data VALUES" + builder.toString().substring(0, builder.length() - 1) + ";").thenApply(r -> true); return sendPreparedUpdate("INSERT OR REPLACE INTO data VALUES" + builder.toString().substring(0, builder.length() - 1) + ";").thenApply(r -> true);
} }

Datei anzeigen

@ -3,8 +3,6 @@ package tsp.headdb.core.storage;
import tsp.headdb.HeadDB; import tsp.headdb.HeadDB;
import tsp.headdb.core.api.HeadAPI; import tsp.headdb.core.api.HeadAPI;
import java.util.stream.Collectors;
public final class Storage { public final class Storage {
private final HeadDB instance = HeadDB.getInstance(); private final HeadDB instance = HeadDB.getInstance();
@ -24,7 +22,7 @@ public final class Storage {
public void save() { public void save() {
playerStorage.save(playerStorage.getPlayers().values()).join(); playerStorage.save(playerStorage.getPlayers().values()).join();
headStorage.save(HeadAPI.getHeads().collect(Collectors.toList())).join(); headStorage.save(HeadAPI.getHeads()).join();
} }
public PlayerStorage getPlayerStorage() { public PlayerStorage getPlayerStorage() {

Datei anzeigen

@ -4,6 +4,8 @@ import tsp.headdb.HeadDB;
import tsp.headdb.core.api.HeadAPI; import tsp.headdb.core.api.HeadAPI;
import tsp.smartplugin.tasker.Task; import tsp.smartplugin.tasker.Task;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("ClassCanBeRecord") @SuppressWarnings("ClassCanBeRecord")
public class UpdateTask implements Task { public class UpdateTask implements Task {
@ -15,7 +17,7 @@ public class UpdateTask implements Task {
@Override @Override
public void run() { public void run() {
HeadAPI.getDatabase().update(); HeadAPI.getDatabase().update((time, heads) -> HeadDB.getInstance().getLog().debug("Fetched: " + heads.size() + " Heads | Provider: " + HeadAPI.getDatabase().getRequester().getProvider().name() + " | Time: " + time + "ms (" + TimeUnit.MILLISECONDS.toSeconds(time) + "s)"));
//HeadDB.getInstance().getStorage().save(); //HeadDB.getInstance().getStorage().save();
HeadDB.getInstance().getLog().debug("UpdateTask finished!"); HeadDB.getInstance().getLog().debug("UpdateTask finished!");
} }

Datei anzeigen

@ -0,0 +1,51 @@
package tsp.headdb.core.util;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* This class contains the properties of the build.
*
* @author TheSilentPro (Silent)
*/
public class BuildProperties {
private String version = "unknown";
private int buildNumber = 0;
private String timestamp = "unknown";
private String author = "unknown";
public BuildProperties(JavaPlugin plugin) {
InputStream in = plugin.getResource("plugin.yml");
if (in == null) {
plugin.getLogger().severe("Could not get plugin.yml to read build information.");
return;
}
YamlConfiguration data = YamlConfiguration.loadConfiguration(new InputStreamReader(in));
this.version = data.getString("version", "unknown");
this.buildNumber = data.getInt("build", 0);
this.timestamp = data.getString("buildTimestamp", "unknown");
this.author = data.getString("buildAuthor", "unknown");
}
public String getVersion() {
return version;
}
public int getBuildNumber() {
return buildNumber;
}
public String getTimestamp() {
return timestamp;
}
public String getAuthor() {
return author;
}
}

Datei anzeigen

@ -1,17 +1,23 @@
package tsp.headdb.core.util; package tsp.headdb.core.util;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import tsp.headdb.HeadDB; import tsp.headdb.HeadDB;
import tsp.headdb.core.api.HeadAPI;
import tsp.headdb.implementation.category.Category; import tsp.headdb.implementation.category.Category;
import tsp.headdb.implementation.head.Head; import tsp.headdb.implementation.head.Head;
import tsp.smartplugin.inventory.Button; import tsp.smartplugin.inventory.Button;
import tsp.smartplugin.inventory.paged.PagedPane; import tsp.smartplugin.inventory.PagedPane;
import tsp.smartplugin.utils.StringUtils; import tsp.smartplugin.utils.StringUtils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import java.lang.reflect.Field;
import java.util.Collection; import java.util.Collection;
import java.util.Locale; import java.util.Locale;
import java.util.Optional; import java.util.Optional;
@ -43,22 +49,33 @@ public class Utils {
} }
public static boolean matches(String provided, String query) { public static boolean matches(String provided, String query) {
provided = provided.toLowerCase(Locale.ROOT); provided = ChatColor.stripColor(provided.toLowerCase(Locale.ROOT));
query = query.toLowerCase(Locale.ROOT); query = query.toLowerCase(Locale.ROOT);
return provided.equals(query) return provided.equals(query)
|| provided.startsWith(query) || provided.startsWith(query)
|| provided.contains(query) || provided.contains(query);
|| provided.endsWith(query); //|| provided.endsWith(query);
}
public static PagedPane createPaged(Player player, String title) {
PagedPane main = new PagedPane(4, 6, title);
main.getInventory().clear();
HeadAPI.getHeadByTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODY1MmUyYjkzNmNhODAyNmJkMjg2NTFkN2M5ZjI4MTlkMmU5MjM2OTc3MzRkMThkZmRiMTM1NTBmOGZkYWQ1ZiJ9fX0=").ifPresent(head -> main.setBackItem(head.getItem(player.getUniqueId())));
HeadAPI.getHeadByTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2Q5MWY1MTI2NmVkZGM2MjA3ZjEyYWU4ZDdhNDljNWRiMDQxNWFkYTA0ZGFiOTJiYjc2ODZhZmRiMTdmNGQ0ZSJ9fX0=").ifPresent(head -> main.setCurrentItem(head.getItem(player.getUniqueId())));
HeadAPI.getHeadByTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmEzYjhmNjgxZGFhZDhiZjQzNmNhZThkYTNmZTgxMzFmNjJhMTYyYWI4MWFmNjM5YzNlMDY0NGFhNmFiYWMyZiJ9fX0=").ifPresent(head -> main.setNextItem(head.getItem(player.getUniqueId())));
return main;
} }
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
public static void addHeads(Player player, @Nullable Category category, PagedPane pane, Collection<Head> heads) { public static void addHeads(Player player, @Nullable Category category, PagedPane pane, Collection<Head> heads) {
pane.getInventory().clear();
for (Head head : heads) { for (Head head : heads) {
ItemStack item = head.getItem(player.getUniqueId()); ItemStack item = head.getItem(player.getUniqueId());
pane.addButton(new Button(head.getItem(player.getUniqueId()), e -> { pane.addButton(new Button(item, e -> {
e.setCancelled(true);
if (category != null && instance.getConfig().getBoolean("requireCategoryPermission") && !player.hasPermission("headdb.category." + category.getName())) { if (category != null && instance.getConfig().getBoolean("requireCategoryPermission") && !player.hasPermission("headdb.category." + category.getName())) {
instance.getLocalization().sendMessage(player.getUniqueId(), "noPermission"); instance.getLocalization().sendMessage(player.getUniqueId(), "noPermission");
e.setCancelled(true);
return; return;
} }
@ -71,10 +88,31 @@ public class Utils {
} else if (e.isRightClick()) { } else if (e.isRightClick()) {
// todo: favorites // todo: favorites
} }
e.setCancelled(true);
})); }));
} }
} }
public static Optional<String> getTexture(ItemStack head) {
ItemMeta meta = head.getItemMeta();
if (meta == null) {
return Optional.empty();
}
try {
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
GameProfile profile = (GameProfile) profileField.get(meta);
if (profile == null) {
return Optional.empty();
}
return profile.getProperties().get("textures").stream()
.filter(p -> p.getValue().equals("textures"))
.findAny()
.map(Property::getName);
} catch (NoSuchFieldException | SecurityException | IllegalAccessException e ) {
return Optional.empty();
}
}
} }

Datei anzeigen

@ -51,12 +51,12 @@ public enum Category {
@Nonnull @Nonnull
public ItemStack getItem(UUID receiver) { public ItemStack getItem(UUID receiver) {
if (item == null) { if (item == null) {
HeadAPI.getHeads(this).findFirst() HeadAPI.getHeads(this).stream().findFirst()
.ifPresentOrElse(head -> item = new ItemBuilder(head.getItem(receiver)) .ifPresentOrElse(head -> item = new ItemBuilder(head.getItem(receiver))
.name(Utils.translateTitle(HeadDB.getInstance().getLocalization().getMessage(receiver, "menu.category.name").orElse("&e" + getName()), HeadAPI.getHeads(this).toList().size(), getName().toUpperCase(Locale.ROOT))) .name(Utils.translateTitle(HeadDB.getInstance().getLocalization().getMessage(receiver, "menu.category.name").orElse("&e" + getName()), HeadAPI.getHeads(this).size(), getName().toUpperCase(Locale.ROOT)))
.setLore("") .setLore((String[]) null)
.build(), .build(),
() -> item = new ItemBuilder(Material.BARRIER).name(getName().toUpperCase(Locale.ROOT)).build()); () -> item = new ItemBuilder(Material.PLAYER_HEAD).name(getName().toUpperCase(Locale.ROOT)).build());
} }
return item; return item;

Datei anzeigen

@ -46,16 +46,15 @@ public class Head {
TranslatableLocalization localization = HeadDB.getInstance().getLocalization(); TranslatableLocalization localization = HeadDB.getInstance().getLocalization();
item = new ItemBuilder(Material.PLAYER_HEAD) item = new ItemBuilder(Material.PLAYER_HEAD)
.name(localization.getMessage(receiver, "menu.head.name").orElse("&e" + name.toUpperCase(Locale.ROOT)).replace("%name%", name)) .name(localization.getMessage(receiver, "menu.head.name").orElse("&e" + name.toUpperCase(Locale.ROOT)).replace("%name%", name))
.setLore("&7Tags: &e" + tags) .setLore("&cID: " + id, "&7Tags: &e" + tags)
.build(); .build();
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
GameProfile profile = new GameProfile(uniqueId, name); GameProfile profile = new GameProfile(uniqueId, name);
profile.getProperties().put("textures", new Property("textures", texture)); profile.getProperties().put("textures", new Property("textures", texture));
Field profileField;
try { try {
//noinspection ConstantConditions //noinspection ConstantConditions
profileField = meta.getClass().getDeclaredField("profile"); Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true); profileField.setAccessible(true);
profileField.set(meta, profile); profileField.set(meta, profile);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) { } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
@ -65,6 +64,7 @@ public class Head {
item.setItemMeta(meta); item.setItemMeta(meta);
} }
return item; return item;
} }

Datei anzeigen

@ -7,12 +7,13 @@ import tsp.headdb.implementation.category.Category;
import tsp.headdb.implementation.requester.HeadProvider; import tsp.headdb.implementation.requester.HeadProvider;
import tsp.headdb.implementation.requester.Requester; import tsp.headdb.implementation.requester.Requester;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
public class HeadDatabase { public class HeadDatabase {
@ -22,7 +23,6 @@ public class HeadDatabase {
private final Requester requester; private final Requester requester;
private final Map<Category, List<Head>> heads; private final Map<Category, List<Head>> heads;
private long timestamp; private long timestamp;
private int size;
public HeadDatabase(JavaPlugin plugin, HeadProvider provider) { public HeadDatabase(JavaPlugin plugin, HeadProvider provider) {
this.plugin = plugin; this.plugin = plugin;
@ -35,28 +35,26 @@ public class HeadDatabase {
return heads; return heads;
} }
public void getHeadsNoCache(Consumer<Map<Category, List<Head>>> heads) { public void getHeadsNoCache(BiConsumer<Long, Map<Category, List<Head>>> heads) {
getScheduler().runTaskAsynchronously(plugin, () -> { getScheduler().runTaskAsynchronously(plugin, () -> {
long start = System.currentTimeMillis();
Map<Category, List<Head>> result = new HashMap<>(); Map<Category, List<Head>> result = new HashMap<>();
for (Category category : Category.VALUES) { for (Category category : Category.VALUES) {
requester.fetchAndResolve(category, response -> result.put(category, response)); requester.fetchAndResolve(category, response -> result.put(category, response));
} }
heads.accept(result); heads.accept(System.currentTimeMillis() - start, result);
}); });
} }
public void update() { public void update(BiConsumer<Long, Map<Category, List<Head>>> fetched) {
getHeadsNoCache(result -> { getHeadsNoCache((elapsed, result) -> {
synchronized (heads) {
heads.putAll(result); heads.putAll(result);
timestamp = System.currentTimeMillis();
size = heads.values().size();
HeadDB.getInstance().getLog().debug("Fetched: " + heads.size() + " Heads | Provider: " + getRequester().getProvider().name());
});
} }
timestamp = System.currentTimeMillis();
public int getSize() { fetched.accept(elapsed, result);
return size; });
} }
public long getTimestamp() { public long getTimestamp() {

Datei anzeigen

@ -20,7 +20,6 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
@SuppressWarnings("ClassCanBeRecord")
public class Requester { public class Requester {
private final JavaPlugin plugin; private final JavaPlugin plugin;

Datei anzeigen

@ -89,7 +89,7 @@ ui:
local: local:
location: 41 location: 41
item: COMPASS item: COMPASS
# Item used to fill unused slots in the categories menu. AIR is supported. You can use head: instead of item: here. # Item used to fill unused slots in the categories' menu. AIR is supported. You can use head: instead of item: here.
fill: fill:
item: BLACK_STAINED_GLASS_PANE item: BLACK_STAINED_GLASS_PANE
sound: sound:
@ -154,5 +154,8 @@ headProvider: "HEAD_STORAGE"
# The archive is static (manually updated) so some heads may be missing, this will only be used when all else fails. # The archive is static (manually updated) so some heads may be missing, this will only be used when all else fails.
fallback: true fallback: true
# Shows more plugin information in /hdb info
showAdvancedPluginInfo: true
# Debug Mode # Debug Mode
debug: false debug: false

Datei anzeigen

@ -6,8 +6,16 @@ invalidCategory: "&cInvalid Category!"
invalidPageIndex: "&cThat page is out of bounds! Max: %pages%" invalidPageIndex: "&cThat page is out of bounds! Max: %pages%"
openDatabase: "&7Opening &cHeadDatabase&7..." openDatabase: "&7Opening &cHeadDatabase&7..."
updateDatabase: "&7Updating..."
updateDatabaseDone: "&7Database was updated! Heads: &6%size% &7| Took: &a%time%"
searchCommand: "&7Searching for heads matching: &e%query%" searchCommand: "&7Searching for heads matching: &e%query%"
searchCommandResults: "&7Found &e%size% &7matches!" searchCommandResults: "&7Found &e%size% &7matches!"
itemTexture: "&7Texture: %texture%"
itemNoTexture: "&cThis item does not have a texture!"
copyTexture: "&6Click to copy texture!"
invalidLanguage: "&cInvalid Language! Available: &e%languages%"
languageChanged: "&7Your language was set to: &e%language%"
menu: menu:
main: main:
@ -22,3 +30,14 @@ menu:
name: "&e%name%" name: "&e%name%"
search: search:
name: "&cHeadDB &7- &eSearch: %query% &7(%size%)" name: "&cHeadDB &7- &eSearch: %query% &7(%size%)"
settings:
name: "&cHeadDB &7- &eSettings"
language:
# Setting name
name: "&cLanguage"
# Available languages lore
available: "&7Languages Available: &e%size%"
# Title of the language selection inventory
title: "&cHeadDB &7- &eLanguages &7(%size%)"
# Language item name
format: "&e%language%"

Datei anzeigen

@ -8,6 +8,10 @@ api-version: 1.19
author: TheSilentPro (Silent) author: TheSilentPro (Silent)
spigot-id: 84967 spigot-id: 84967
build: ${build.number}
buildTimestamp: ${build.timestamp}
buildAuthor: ${build.author}
libraries: libraries:
- "com.google.code.gson:gson:2.10" - "com.google.code.gson:gson:2.10"