Added an auto updater and a QuickCommandListener combo. Also fixed text bugs.
This commit is contained in:
19
build.gradle
19
build.gradle
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id("xyz.jpenilla.run-paper") version "2.3.1"
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
@@ -23,12 +22,6 @@ dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
|
||||
}
|
||||
|
||||
tasks {
|
||||
runServer {
|
||||
minecraftVersion("1.21.5")
|
||||
}
|
||||
}
|
||||
|
||||
def targetJavaVersion = 21
|
||||
java {
|
||||
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
|
||||
@@ -47,13 +40,9 @@ tasks.withType(JavaCompile).configureEach {
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
def props = [version: version]
|
||||
inputs.properties props
|
||||
filteringCharset 'UTF-8'
|
||||
filesMatching('plugin.yml') {
|
||||
expand props
|
||||
}
|
||||
task sourcesJar(type: Jar) {
|
||||
archiveClassifier.set('sources')
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
publishing {
|
||||
@@ -63,6 +52,8 @@ publishing {
|
||||
groupId = project.group
|
||||
artifactId = 'alias'
|
||||
version = project.version
|
||||
|
||||
artifact sourcesJar
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
|
||||
@@ -5,7 +5,7 @@ import me.trouper.alias.server.AutoRegistrar;
|
||||
import me.trouper.alias.server.commands.QuickCommand;
|
||||
import me.trouper.alias.server.events.GuiListener;
|
||||
import me.trouper.alias.server.events.QuickListener;
|
||||
import me.trouper.alias.server.systems.AbstractWand;
|
||||
import me.trouper.alias.update.AutoUpdater;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class Alias extends JavaPlugin {
|
||||
@@ -21,13 +21,21 @@ public final class Alias extends JavaPlugin {
|
||||
Alias.host = plugin.getClass();
|
||||
Alias.common = common;
|
||||
|
||||
new GuiListener().register();
|
||||
AutoUpdater.checkUpdate(plugin,common);
|
||||
|
||||
autoRegistrar = new AutoRegistrar(plugin);
|
||||
autoRegistrar.getQuickListeners().add(new GuiListener());
|
||||
autoRegistrar.loadAll(common.getPackageName());
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public static synchronized void stop(JavaPlugin plugin, Common common) {
|
||||
autoRegistrar.getQuickListeners().forEach(QuickListener::unregister);
|
||||
autoRegistrar.getQuickCommands().forEach(QuickCommand::disable);
|
||||
AutoUpdater.checkUpdate(plugin,common);
|
||||
}
|
||||
|
||||
public static Class<? extends JavaPlugin> getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@@ -12,15 +12,17 @@ public class Common {
|
||||
private String flatPrefix;
|
||||
private boolean flat;
|
||||
private boolean debugMode;
|
||||
private final String updateURL;
|
||||
private final Set<String> debuggerExclusions;
|
||||
|
||||
public Common(String packageName, int mainColor, int secondaryColor, String pluginName, String flatPrefix, boolean flat) {
|
||||
public Common(String packageName, int mainColor, int secondaryColor, String pluginName, String flatPrefix, boolean flat, String updateURL) {
|
||||
this.packageName = packageName;
|
||||
this.mainColor = mainColor;
|
||||
this.secondaryColor = secondaryColor;
|
||||
this.pluginName = pluginName;
|
||||
this.flatPrefix = flatPrefix;
|
||||
this.flat = flat;
|
||||
this.updateURL = updateURL;
|
||||
this.debugMode = false;
|
||||
this.debuggerExclusions = new HashSet<>();
|
||||
}
|
||||
@@ -94,4 +96,7 @@ public class Common {
|
||||
}
|
||||
|
||||
|
||||
public String getUpdateURL() {
|
||||
return updateURL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import me.trouper.alias.server.commands.completions.CompletionNode;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -108,4 +107,14 @@ public interface QuickCommand extends TabExecutor, Main {
|
||||
default CommandRegistry getRegistry() {
|
||||
return this.getClass().getAnnotation(CommandRegistry.class);
|
||||
}
|
||||
|
||||
default void disable() {
|
||||
CommandRegistry registry = this.getClass().getAnnotation(CommandRegistry.class);
|
||||
PluginCommand command = getPlugin().getCommand(registry.value());
|
||||
|
||||
if (command != null) {
|
||||
command.setExecutor((sender, command1, label, args) -> true);
|
||||
command.setTabCompleter((sender, command2, label, args) -> List.of());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.trouper.alias.server.commands;
|
||||
|
||||
import me.trouper.alias.server.events.QuickListener;
|
||||
|
||||
public interface QuickCommandListener extends QuickCommand, QuickListener {
|
||||
@Override
|
||||
default void register() {
|
||||
QuickCommand.super.register();
|
||||
QuickListener.super.register();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void disable() {
|
||||
QuickListener.super.unregister();
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,34 @@ package me.trouper.alias.server.events;
|
||||
|
||||
import me.trouper.alias.server.systems.gui.QuickGui;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
public class GuiListener implements QuickListener {
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent e) {
|
||||
QuickGui.handleClick(e);
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
QuickGui.handleClick(event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClose(InventoryCloseEvent e) {
|
||||
QuickGui.handleClose(e);
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onInventoryClose(InventoryCloseEvent event) {
|
||||
QuickGui.handleClose(event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryDrag(InventoryDragEvent e) {
|
||||
QuickGui.handleDrag(e);
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onInventoryDrag(InventoryDragEvent event) {
|
||||
QuickGui.handleDrag(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
QuickGui.getRegistries().values().forEach(gui -> {
|
||||
gui.getViewers().remove(event.getPlayer());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@ package me.trouper.alias.server.events;
|
||||
|
||||
import me.trouper.alias.server.Main;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
public interface QuickListener extends Listener, Main {
|
||||
default QuickListener register() {
|
||||
Bukkit.getPluginManager().registerEvents(this,this.getPlugin());
|
||||
return this;
|
||||
default void register() {
|
||||
Bukkit.getPluginManager().registerEvents(this,main.getPlugin());
|
||||
}
|
||||
|
||||
default void unregister() {
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,15 @@ public class Text implements Main {
|
||||
* @param args Qualified placeholders to color.
|
||||
*/
|
||||
public static void messageAny(Pallet pallet, boolean playSound, Audience audience, String text, Object... args) {
|
||||
message(pallet, playSound, audience, color(text), Arrays.stream(args).map(object -> Component.text(String.valueOf(object))).toArray(ComponentLike[]::new));
|
||||
message(
|
||||
pallet,
|
||||
playSound,
|
||||
audience,
|
||||
color(text),
|
||||
Arrays.stream(args)
|
||||
.map(object -> object instanceof ComponentLike ? (ComponentLike) object : Component.text(String.valueOf(object)))
|
||||
.toArray(ComponentLike[]::new)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +90,13 @@ public class Text implements Main {
|
||||
* @return The final component, formatted according to flat/fancy setting.
|
||||
*/
|
||||
public static Component getMessageAny(Pallet pallet, String text, Object... args) {
|
||||
return getMessage(pallet, color(text), Arrays.stream(args).map(arg -> color(String.valueOf(arg))).toArray(ComponentLike[]::new));
|
||||
return getMessage(
|
||||
pallet,
|
||||
color(text),
|
||||
Arrays.stream(args)
|
||||
.map(object -> object instanceof ComponentLike ? (ComponentLike) object : Component.text(String.valueOf(object)))
|
||||
.toArray(ComponentLike[]::new)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,6 +164,7 @@ public class Text implements Main {
|
||||
/**
|
||||
* Wraps a component into multiple lines based on visible character count.
|
||||
* Preserves all Adventure API formatting including colors, decorations, and events.
|
||||
* Fixed to prevent unwanted spaces before punctuation.
|
||||
* @param component The component to wrap
|
||||
* @param maxLineLength Maximum visible characters per line
|
||||
* @param firstLineOffset Offset for the first line (plugin name length)
|
||||
@@ -157,6 +172,7 @@ public class Text implements Main {
|
||||
*/
|
||||
private static List<Component> wrapComponent(Component component, int maxLineLength, int firstLineOffset) {
|
||||
List<Component> lines = new ArrayList<>();
|
||||
|
||||
List<ComponentWord> words = extractWords(component);
|
||||
|
||||
if (words.isEmpty()) {
|
||||
@@ -166,20 +182,22 @@ public class Text implements Main {
|
||||
|
||||
Component currentLine = Component.empty();
|
||||
int currentLineLength = firstLineOffset;
|
||||
boolean isFirstLine = true;
|
||||
|
||||
for (int i = 0; i < words.size(); i++) {
|
||||
ComponentWord word = words.get(i);
|
||||
int wordLength = word.visibleLength();
|
||||
int spaceNeeded = (currentLine.equals(Component.empty()) ? 0 : 1) + wordLength;
|
||||
|
||||
boolean needsSpace = !currentLine.equals(Component.empty()) && !startsWithPunctuation(word);
|
||||
int spaceNeeded = (needsSpace ? 1 : 0) + wordLength;
|
||||
|
||||
if (currentLineLength + spaceNeeded > maxLineLength && !currentLine.equals(Component.empty())) {
|
||||
lines.add(currentLine);
|
||||
currentLine = Component.empty();
|
||||
currentLineLength = 0;
|
||||
needsSpace = false;
|
||||
}
|
||||
|
||||
if (!currentLine.equals(Component.empty())) {
|
||||
if (needsSpace) {
|
||||
currentLine = currentLine.append(Component.space());
|
||||
currentLineLength++;
|
||||
}
|
||||
@@ -195,6 +213,16 @@ public class Text implements Main {
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a word starts with punctuation that shouldn't have a space before it.
|
||||
* @param word The word to check
|
||||
* @return true if the word starts with punctuation
|
||||
*/
|
||||
private static boolean startsWithPunctuation(ComponentWord word) {
|
||||
String text = PlainTextComponentSerializer.plainText().serialize(word.component());
|
||||
return !text.isEmpty() && ".,!?;:)]}".indexOf(text.charAt(0)) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts words from a component while preserving their formatting.
|
||||
* @param component The component to extract words from
|
||||
@@ -215,16 +243,15 @@ public class Text implements Main {
|
||||
private static void extractWordsRecursive(Component component, Style inheritedStyle, List<ComponentWord> words) {
|
||||
Style currentStyle = inheritedStyle.merge(component.style());
|
||||
|
||||
|
||||
if (component instanceof TextComponent textComponent) {
|
||||
String text = textComponent.content();
|
||||
if (!text.isEmpty()) {
|
||||
String[] parts = text.split("\\s+");
|
||||
|
||||
String[] textWords = text.split("\\s+");
|
||||
for (String word : textWords) {
|
||||
if (!word.isEmpty()) {
|
||||
Component wordComponent = Component.text(word).style(currentStyle);
|
||||
words.add(new ComponentWord(wordComponent, getVisibleLength(word)));
|
||||
for (String part : parts) {
|
||||
if (!part.isEmpty()) {
|
||||
Component partComponent = Component.text(part).style(currentStyle);
|
||||
words.add(new ComponentWord(partComponent, getVisibleLength(part)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,7 +342,7 @@ public class Text implements Main {
|
||||
private static boolean shouldRecolor(Component component) {
|
||||
Set<TextColor> colors = new HashSet<>();
|
||||
collectColors(component,colors);
|
||||
return colors.size() > 1;
|
||||
return colors.size() <= 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,9 +67,7 @@ public class QuickGui implements InventoryHolder, Main {
|
||||
}
|
||||
|
||||
public static QuickGui register(String id, QuickGui gui) {
|
||||
if (gui != null && id != null && !id.isEmpty()) {
|
||||
registry.put(id, gui);
|
||||
}
|
||||
registry.put(id, gui);
|
||||
return gui;
|
||||
}
|
||||
|
||||
@@ -381,6 +379,17 @@ public class QuickGui implements InventoryHolder, Main {
|
||||
return item(slot, item, action);
|
||||
}
|
||||
|
||||
|
||||
public GuiBuilder fillSlots(ItemStack item, GuiAction action, int... slots) {
|
||||
for (int slot : slots) {
|
||||
if (slot >= 0 && slot < 54 && item != null) {
|
||||
slotItems.put(slot,item);
|
||||
slotActions.put(slot,action);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GuiBuilder fillBorder(Material material) {
|
||||
return fillBorder(new ItemStack(material));
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
package me.trouper.alias.server.systems.world;
|
||||
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class BlockHistory {
|
||||
|
||||
private final List<State> history = new ArrayList<>();
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private int currentIndex = -1;
|
||||
|
||||
public void addChange(State state) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (currentIndex < history.size() - 1) {
|
||||
history.subList(currentIndex + 1, history.size()).clear();
|
||||
}
|
||||
history.add(state);
|
||||
currentIndex = history.size() - 1;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void addChange(Collection<Block> blocks) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (currentIndex < history.size() - 1) {
|
||||
history.subList(currentIndex + 1, history.size()).clear();
|
||||
}
|
||||
State newState = new State(blocks);
|
||||
history.add(newState);
|
||||
currentIndex = history.size() - 1;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canUndo() {
|
||||
lock.lock();
|
||||
try {
|
||||
return currentIndex > 0;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canRedo() {
|
||||
lock.lock();
|
||||
try {
|
||||
return currentIndex < history.size() - 1;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean undo() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (!canUndo()) return false;
|
||||
currentIndex--;
|
||||
history.get(currentIndex).restore();
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean redo() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (!canRedo()) return false;
|
||||
currentIndex++;
|
||||
history.get(currentIndex).restore();
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrentIndex() {
|
||||
lock.lock();
|
||||
try {
|
||||
return currentIndex;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getHistorySize() {
|
||||
lock.lock();
|
||||
try {
|
||||
return history.size();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Block> getCurrentBlocks() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (currentIndex >= 0 && currentIndex < history.size()) {
|
||||
return history.get(currentIndex).getBlocks();
|
||||
}
|
||||
return Collections.emptySet();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
111
src/main/java/me/trouper/alias/update/AutoUpdater.java
Normal file
111
src/main/java/me/trouper/alias/update/AutoUpdater.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package me.trouper.alias.update;
|
||||
|
||||
import me.trouper.alias.Alias;
|
||||
import me.trouper.alias.data.Common;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class AutoUpdater {
|
||||
|
||||
public static boolean checkUpdate(JavaPlugin plugin, Common common) {
|
||||
try {
|
||||
if (UpdateUtils.isDevelopmentEnvironment(plugin)) {
|
||||
plugin.getLogger().info("Development environment detected, bypassing update check.");
|
||||
return false;
|
||||
}
|
||||
|
||||
String updateURL = common.getUpdateURL();
|
||||
if (updateURL == null || updateURL.isEmpty()) {
|
||||
plugin.getLogger().warning("Update URL is not set.");
|
||||
return false;
|
||||
}
|
||||
|
||||
File updateDir = new File("plugins/update");
|
||||
if (!updateDir.exists()) updateDir.mkdirs();
|
||||
|
||||
File currentFile = UpdateUtils.findPluginJar(plugin);
|
||||
if (currentFile == null) {
|
||||
plugin.getLogger().severe("Could not locate plugin file in plugins folder.");
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] currentHash = UpdateUtils.getFileHash(currentFile);
|
||||
String remoteHashURL = updateURL.replace("/download/", "/hash/sha256/");
|
||||
|
||||
String remoteHashHex = UpdateUtils.fetchRemoteHash(remoteHashURL);
|
||||
if (remoteHashHex == null) {
|
||||
plugin.getLogger().warning("Failed to fetch remote hash from: " + remoteHashURL);
|
||||
return false;
|
||||
}
|
||||
|
||||
String currentHashHex = UpdateUtils.bytesToHex(currentHash);
|
||||
if (remoteHashHex.equalsIgnoreCase(currentHashHex)) {
|
||||
plugin.getLogger().info("Plugin is up to date.");
|
||||
return false;
|
||||
}
|
||||
|
||||
File updateFile = new File(updateDir, currentFile.getName());
|
||||
if (updateFile.exists()) {
|
||||
byte[] updateHash = UpdateUtils.getFileHash(updateFile);
|
||||
String updateHashHex = UpdateUtils.bytesToHex(updateHash);
|
||||
|
||||
if (remoteHashHex.equalsIgnoreCase(updateHashHex)) {
|
||||
plugin.getLogger().info("An update is already downloaded and ready.");
|
||||
return false;
|
||||
} else {
|
||||
plugin.getLogger().info("Found outdated update file in plugins/update/. It will be replaced.");
|
||||
updateFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Update available. Downloading new version...");
|
||||
File downloaded = downloadFile(updateURL);
|
||||
if (downloaded == null) {
|
||||
plugin.getLogger().warning("Failed to download update file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
File destination = new File(updateDir, currentFile.getName());
|
||||
Files.copy(downloaded.toPath(), destination.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
||||
plugin.getLogger().info("Saved updated plugin to: " + destination.getAbsolutePath());
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error during update check", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static File downloadFile(String urlStr) throws IOException {
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestProperty("User-Agent", "AliasUpdater");
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() != 200) return null;
|
||||
|
||||
File tempFile = File.createTempFile("plugin_update_", ".jar");
|
||||
try (InputStream in = conn.getInputStream(); FileOutputStream out = new FileOutputStream(tempFile)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
93
src/main/java/me/trouper/alias/update/UpdateUtils.java
Normal file
93
src/main/java/me/trouper/alias/update/UpdateUtils.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package me.trouper.alias.update;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class UpdateUtils {
|
||||
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static String fetchRemoteHash(String urlStr) {
|
||||
try {
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestProperty("User-Agent", "AliasUpdater");
|
||||
conn.setConnectTimeout(5000);
|
||||
conn.setReadTimeout(5000);
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
|
||||
return reader.readLine().trim();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getFileHash(File file) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
try (InputStream fis = new FileInputStream(file)) {
|
||||
byte[] byteArray = new byte[8192];
|
||||
int bytesCount;
|
||||
while ((bytesCount = fis.read(byteArray)) != -1) {
|
||||
digest.update(byteArray, 0, bytesCount);
|
||||
}
|
||||
}
|
||||
return digest.digest();
|
||||
}
|
||||
|
||||
public static File findPluginJar(JavaPlugin plugin) {
|
||||
File pluginDir = new File("plugins");
|
||||
if (!pluginDir.exists() || !pluginDir.isDirectory()) return null;
|
||||
|
||||
String expectedName = plugin.getDescription().getName();
|
||||
String expectedMain = plugin.getDescription().getMain();
|
||||
|
||||
for (File file : pluginDir.listFiles()) {
|
||||
if (!file.getName().endsWith(".jar")) continue;
|
||||
|
||||
try (JarFile jar = new JarFile(file)) {
|
||||
JarEntry entry = jar.getJarEntry("plugin.yml");
|
||||
if (entry == null) continue;
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
YamlConfiguration yml = new YamlConfiguration();
|
||||
yml.load(new InputStreamReader(is));
|
||||
|
||||
String name = yml.getString("name");
|
||||
String main = yml.getString("main");
|
||||
|
||||
if (expectedName.equalsIgnoreCase(name) && expectedMain.equalsIgnoreCase(main)) {
|
||||
return file;
|
||||
}
|
||||
|
||||
} catch (Exception ignored) {}
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isDevelopmentEnvironment(JavaPlugin plugin) {
|
||||
try {
|
||||
return "TRUE".equalsIgnoreCase(System.getenv("ALIAS_DEVELOPMENT"));
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Could not determine runtime environment, assuming production.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user