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 {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id("xyz.jpenilla.run-paper") version "2.3.1"
|
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,12 +22,6 @@ dependencies {
|
|||||||
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
|
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
|
||||||
runServer {
|
|
||||||
minecraftVersion("1.21.5")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def targetJavaVersion = 21
|
def targetJavaVersion = 21
|
||||||
java {
|
java {
|
||||||
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
|
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
|
||||||
@@ -47,13 +40,9 @@ tasks.withType(JavaCompile).configureEach {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
task sourcesJar(type: Jar) {
|
||||||
def props = [version: version]
|
archiveClassifier.set('sources')
|
||||||
inputs.properties props
|
from sourceSets.main.allSource
|
||||||
filteringCharset 'UTF-8'
|
|
||||||
filesMatching('plugin.yml') {
|
|
||||||
expand props
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
@@ -63,6 +52,8 @@ publishing {
|
|||||||
groupId = project.group
|
groupId = project.group
|
||||||
artifactId = 'alias'
|
artifactId = 'alias'
|
||||||
version = project.version
|
version = project.version
|
||||||
|
|
||||||
|
artifact sourcesJar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import me.trouper.alias.server.AutoRegistrar;
|
|||||||
import me.trouper.alias.server.commands.QuickCommand;
|
import me.trouper.alias.server.commands.QuickCommand;
|
||||||
import me.trouper.alias.server.events.GuiListener;
|
import me.trouper.alias.server.events.GuiListener;
|
||||||
import me.trouper.alias.server.events.QuickListener;
|
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;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
public final class Alias extends JavaPlugin {
|
public final class Alias extends JavaPlugin {
|
||||||
@@ -21,13 +21,21 @@ public final class Alias extends JavaPlugin {
|
|||||||
Alias.host = plugin.getClass();
|
Alias.host = plugin.getClass();
|
||||||
Alias.common = common;
|
Alias.common = common;
|
||||||
|
|
||||||
new GuiListener().register();
|
AutoUpdater.checkUpdate(plugin,common);
|
||||||
|
|
||||||
autoRegistrar = new AutoRegistrar(plugin);
|
autoRegistrar = new AutoRegistrar(plugin);
|
||||||
|
autoRegistrar.getQuickListeners().add(new GuiListener());
|
||||||
autoRegistrar.loadAll(common.getPackageName());
|
autoRegistrar.loadAll(common.getPackageName());
|
||||||
|
|
||||||
enabled = true;
|
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() {
|
public static Class<? extends JavaPlugin> getHost() {
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,17 @@ public class Common {
|
|||||||
private String flatPrefix;
|
private String flatPrefix;
|
||||||
private boolean flat;
|
private boolean flat;
|
||||||
private boolean debugMode;
|
private boolean debugMode;
|
||||||
|
private final String updateURL;
|
||||||
private final Set<String> debuggerExclusions;
|
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.packageName = packageName;
|
||||||
this.mainColor = mainColor;
|
this.mainColor = mainColor;
|
||||||
this.secondaryColor = secondaryColor;
|
this.secondaryColor = secondaryColor;
|
||||||
this.pluginName = pluginName;
|
this.pluginName = pluginName;
|
||||||
this.flatPrefix = flatPrefix;
|
this.flatPrefix = flatPrefix;
|
||||||
this.flat = flat;
|
this.flat = flat;
|
||||||
|
this.updateURL = updateURL;
|
||||||
this.debugMode = false;
|
this.debugMode = false;
|
||||||
this.debuggerExclusions = new HashSet<>();
|
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 net.kyori.adventure.text.Component;
|
||||||
import org.bukkit.command.*;
|
import org.bukkit.command.*;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.checkerframework.checker.units.qual.A;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -108,4 +107,14 @@ public interface QuickCommand extends TabExecutor, Main {
|
|||||||
default CommandRegistry getRegistry() {
|
default CommandRegistry getRegistry() {
|
||||||
return this.getClass().getAnnotation(CommandRegistry.class);
|
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 me.trouper.alias.server.systems.gui.QuickGui;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
public class GuiListener implements QuickListener {
|
public class GuiListener implements QuickListener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.NORMAL)
|
||||||
public void onInventoryClick(InventoryClickEvent e) {
|
public void onInventoryClick(InventoryClickEvent event) {
|
||||||
QuickGui.handleClick(e);
|
QuickGui.handleClick(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.NORMAL)
|
||||||
public void onInventoryClose(InventoryCloseEvent e) {
|
public void onInventoryClose(InventoryCloseEvent event) {
|
||||||
QuickGui.handleClose(e);
|
QuickGui.handleClose(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.NORMAL)
|
||||||
public void onInventoryDrag(InventoryDragEvent e) {
|
public void onInventoryDrag(InventoryDragEvent event) {
|
||||||
QuickGui.handleDrag(e);
|
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 me.trouper.alias.server.Main;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
public interface QuickListener extends Listener, Main {
|
public interface QuickListener extends Listener, Main {
|
||||||
default QuickListener register() {
|
default void register() {
|
||||||
Bukkit.getPluginManager().registerEvents(this,this.getPlugin());
|
Bukkit.getPluginManager().registerEvents(this,main.getPlugin());
|
||||||
return this;
|
}
|
||||||
|
|
||||||
|
default void unregister() {
|
||||||
|
HandlerList.unregisterAll(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,15 @@ public class Text implements Main {
|
|||||||
* @param args Qualified placeholders to color.
|
* @param args Qualified placeholders to color.
|
||||||
*/
|
*/
|
||||||
public static void messageAny(Pallet pallet, boolean playSound, Audience audience, String text, Object... args) {
|
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.
|
* @return The final component, formatted according to flat/fancy setting.
|
||||||
*/
|
*/
|
||||||
public static Component getMessageAny(Pallet pallet, String text, Object... args) {
|
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.
|
* Wraps a component into multiple lines based on visible character count.
|
||||||
* Preserves all Adventure API formatting including colors, decorations, and events.
|
* Preserves all Adventure API formatting including colors, decorations, and events.
|
||||||
|
* Fixed to prevent unwanted spaces before punctuation.
|
||||||
* @param component The component to wrap
|
* @param component The component to wrap
|
||||||
* @param maxLineLength Maximum visible characters per line
|
* @param maxLineLength Maximum visible characters per line
|
||||||
* @param firstLineOffset Offset for the first line (plugin name length)
|
* @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) {
|
private static List<Component> wrapComponent(Component component, int maxLineLength, int firstLineOffset) {
|
||||||
List<Component> lines = new ArrayList<>();
|
List<Component> lines = new ArrayList<>();
|
||||||
|
|
||||||
List<ComponentWord> words = extractWords(component);
|
List<ComponentWord> words = extractWords(component);
|
||||||
|
|
||||||
if (words.isEmpty()) {
|
if (words.isEmpty()) {
|
||||||
@@ -166,20 +182,22 @@ public class Text implements Main {
|
|||||||
|
|
||||||
Component currentLine = Component.empty();
|
Component currentLine = Component.empty();
|
||||||
int currentLineLength = firstLineOffset;
|
int currentLineLength = firstLineOffset;
|
||||||
boolean isFirstLine = true;
|
|
||||||
|
|
||||||
for (int i = 0; i < words.size(); i++) {
|
for (int i = 0; i < words.size(); i++) {
|
||||||
ComponentWord word = words.get(i);
|
ComponentWord word = words.get(i);
|
||||||
int wordLength = word.visibleLength();
|
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())) {
|
if (currentLineLength + spaceNeeded > maxLineLength && !currentLine.equals(Component.empty())) {
|
||||||
lines.add(currentLine);
|
lines.add(currentLine);
|
||||||
currentLine = Component.empty();
|
currentLine = Component.empty();
|
||||||
currentLineLength = 0;
|
currentLineLength = 0;
|
||||||
|
needsSpace = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentLine.equals(Component.empty())) {
|
if (needsSpace) {
|
||||||
currentLine = currentLine.append(Component.space());
|
currentLine = currentLine.append(Component.space());
|
||||||
currentLineLength++;
|
currentLineLength++;
|
||||||
}
|
}
|
||||||
@@ -195,6 +213,16 @@ public class Text implements Main {
|
|||||||
return lines;
|
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.
|
* Extracts words from a component while preserving their formatting.
|
||||||
* @param component The component to extract words from
|
* @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) {
|
private static void extractWordsRecursive(Component component, Style inheritedStyle, List<ComponentWord> words) {
|
||||||
Style currentStyle = inheritedStyle.merge(component.style());
|
Style currentStyle = inheritedStyle.merge(component.style());
|
||||||
|
|
||||||
|
|
||||||
if (component instanceof TextComponent textComponent) {
|
if (component instanceof TextComponent textComponent) {
|
||||||
String text = textComponent.content();
|
String text = textComponent.content();
|
||||||
if (!text.isEmpty()) {
|
if (!text.isEmpty()) {
|
||||||
|
String[] parts = text.split("\\s+");
|
||||||
|
|
||||||
String[] textWords = text.split("\\s+");
|
for (String part : parts) {
|
||||||
for (String word : textWords) {
|
if (!part.isEmpty()) {
|
||||||
if (!word.isEmpty()) {
|
Component partComponent = Component.text(part).style(currentStyle);
|
||||||
Component wordComponent = Component.text(word).style(currentStyle);
|
words.add(new ComponentWord(partComponent, getVisibleLength(part)));
|
||||||
words.add(new ComponentWord(wordComponent, getVisibleLength(word)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +342,7 @@ public class Text implements Main {
|
|||||||
private static boolean shouldRecolor(Component component) {
|
private static boolean shouldRecolor(Component component) {
|
||||||
Set<TextColor> colors = new HashSet<>();
|
Set<TextColor> colors = new HashSet<>();
|
||||||
collectColors(component,colors);
|
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) {
|
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;
|
return gui;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +379,17 @@ public class QuickGui implements InventoryHolder, Main {
|
|||||||
return item(slot, item, action);
|
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) {
|
public GuiBuilder fillBorder(Material material) {
|
||||||
return fillBorder(new ItemStack(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