diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java index 4e28500..b2ec42d 100644 --- a/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java +++ b/Common/src/main/java/net/frankheijden/serverutils/common/commands/CommandServerUtils.java @@ -17,13 +17,13 @@ import net.frankheijden.serverutils.common.commands.arguments.PluginArgument; import net.frankheijden.serverutils.common.commands.arguments.PluginsArgument; import net.frankheijden.serverutils.common.config.MessagesResource; import net.frankheijden.serverutils.common.config.ServerUtilsConfig; -import net.frankheijden.serverutils.common.entities.results.AbstractResult; import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; import net.frankheijden.serverutils.common.entities.results.PluginResult; import net.frankheijden.serverutils.common.entities.results.PluginResults; import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; +import net.frankheijden.serverutils.common.entities.results.WatchResult; import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.ListBuilder; @@ -73,7 +73,11 @@ public abstract class CommandServerUtils( + true, + "plugins", + new PluginsArgument.PluginsParser<>(plugin, arrayCreator, getRawPath("watchplugin")) + )) .handler(this::handleWatchPlugin)); manager.command(buildSubcommand(builder, "unwatchplugin") .argument(getArgument("plugin")) @@ -256,24 +260,23 @@ public abstract class CommandServerUtils context) { C sender = context.getSender(); - P pluginArg = context.get("plugin"); + List
plugins = Arrays.asList(context.get("plugins")); - AbstractPluginManager
pluginManager = plugin.getPluginManager();
- String pluginId = pluginManager.getPluginId(pluginArg);
+ if (checkDependingPlugins(context, sender, plugins, "watchplugin")) {
+ return;
+ }
- AbstractResult result = pluginManager.watchPlugin(sender, pluginId);
- result.sendTo(sender, "watch", pluginId);
+ WatchResult result = plugin.getWatchManager().watchPlugins(sender, plugins);
+ result.sendTo(sender);
}
private void handleUnwatchPlugin(CommandContext pluginManager = plugin.getPluginManager();
- String pluginId = pluginManager.getPluginId(pluginArg);
-
- AbstractResult result = pluginManager.unwatchPlugin(pluginId);
- result.sendTo(sender, "unwatch", pluginId);
+ String pluginId = plugin.getPluginManager().getPluginId(pluginArg);
+ WatchResult result = plugin.getWatchManager().unwatchPluginsAssociatedWith(pluginId);
+ result.sendTo(sender);
}
private void handlePluginInfo(CommandContext , S, D extends ServerUtilsPluginDescription> {
private final UpdateManager updateManager = new UpdateManager();
+ private final WatchManager watchManager = new WatchManager<>(this);
private CommandsResource commandsResource;
private ConfigResource configResource;
private MessagesResource messagesResource;
@@ -57,6 +59,10 @@ public abstract class ServerUtilsPlugin ,
return updateManager;
}
+ public WatchManager getWatchManager() {
+ return watchManager;
+ }
+
public abstract Logger getLogger();
public abstract File getDataFolder();
diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java
index 787378d..091981d 100644
--- a/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java
+++ b/Common/src/main/java/net/frankheijden/serverutils/common/entities/results/WatchResult.java
@@ -1,28 +1,60 @@
package net.frankheijden.serverutils.common.entities.results;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import net.frankheijden.serverutils.common.ServerUtilsApp;
+import net.frankheijden.serverutils.common.config.MessagesResource;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
public enum WatchResult implements AbstractResult {
START,
CHANGE,
+ ALREADY_WATCHING,
NOT_WATCHING,
+ FILE_DELETED,
+ DELETED_FILE_IS_CREATED,
STOPPED;
- /**
- * Retrieves the associated message of the result
- * and sends it to a CommandSender.
- * @param sender The receiver.
- * @param action The action which let to the result.
- * @param what An associated variable.
- */
+ private List implements PluginProvider {
@@ -294,23 +289,4 @@ public abstract class AbstractPluginManager sender, String pluginId) {
- if (getPlugin(pluginId).isPresent()) return Result.NOT_EXISTS;
- ServerUtilsApp.getPlugin().getTaskManager()
- .runTaskAsynchronously(pluginId, new PluginWatcherTask(sender, pluginId));
- return WatchResult.START;
- }
-
- /**
- * Stops watching the plugin for changes.
- */
- public AbstractResult unwatchPlugin(String pluginId) {
- if (ServerUtilsApp.getPlugin().getTaskManager().cancelTask(pluginId)) return WatchResult.STOPPED;
- return WatchResult.NOT_WATCHING;
- }
}
diff --git a/Common/src/main/java/net/frankheijden/serverutils/common/managers/WatchManager.java b/Common/src/main/java/net/frankheijden/serverutils/common/managers/WatchManager.java
new file mode 100644
index 0000000..d9e53f3
--- /dev/null
+++ b/Common/src/main/java/net/frankheijden/serverutils/common/managers/WatchManager.java
@@ -0,0 +1,73 @@
+package net.frankheijden.serverutils.common.managers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import net.frankheijden.serverutils.common.entities.ServerCommandSender;
+import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin;
+import net.frankheijden.serverutils.common.entities.results.WatchResult;
+import net.frankheijden.serverutils.common.tasks.PluginWatcherTask;
+
+public class WatchManager {
+
+ private final ServerUtilsPlugin plugin;
+ private final Map plugin) {
+ this.plugin = plugin;
+ this.watchTasks = new HashMap<>();
+ }
+
+ /**
+ * Starts watching the specified plugin and reloads it when a change is detected.
+ */
+ public WatchResult watchPlugins(ServerCommandSender> sender, List plugins) {
+ List extends AbstractTask {
private static final WatchEvent.Kind>[] EVENTS = new WatchEvent.Kind[]{
StandardWatchEventKinds.ENTRY_CREATE,
@@ -28,32 +36,33 @@ public class PluginWatcherTask extends AbstractTask {
StandardWatchEventKinds.ENTRY_DELETE
};
- private final ServerUtilsPlugin, ?, ?, ?, ?> plugin = ServerUtilsApp.getPlugin();
- private final AbstractPluginManager, ?> pluginManager = plugin.getPluginManager();
- private final ChatProvider, ?> chatProvider = plugin.getChatProvider();
- @SuppressWarnings("rawtypes")
- private final AbstractTaskManager taskManager = plugin.getTaskManager();
-
+ private final ServerUtilsPlugin plugin;
private final ServerCommandSender> sender;
- private final String pluginName;
- private final AtomicBoolean run;
- private File file;
- private String hash;
- private long hashTimestamp = 0;
+ private final Map plugin, ServerCommandSender> sender, List plugins) {
+ this.plugin = plugin;
this.sender = sender;
- this.pluginName = pluginName;
- this.file = pluginManager.getPluginFile(pluginName).orElse(null);
- this.run = new AtomicBoolean(true);
+ this.fileNameToWatchEntryMap = new HashMap<>();
+ this.pluginIdToWatchEntryMap = new HashMap<>();
+
+ AbstractPluginManager pluginManager = plugin.getPluginManager();
+ for (P watchPlugin : plugins) {
+ File file = pluginManager.getPluginFile(watchPlugin);
+
+ WatchEntry entry = new WatchEntry(pluginManager.getPluginId(watchPlugin));
+ entry.update(file);
+
+ this.fileNameToWatchEntryMap.put(file.getName(), entry);
+ }
}
@Override
@@ -61,34 +70,21 @@ public class PluginWatcherTask extends AbstractTask {
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
this.watchService = watchService;
- File folder = pluginManager.getPluginsFolder();
- folder.toPath().register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH);
+ AbstractPluginManager pluginManager = plugin.getPluginManager();
+ Path basePath = pluginManager.getPluginsFolder().toPath();
+ basePath.register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH);
while (run.get()) {
WatchKey key = watchService.take();
for (WatchEvent> event : key.pollEvents()) {
- if (file.getName().equals(event.context().toString())) {
- if (task != null) {
- taskManager.cancelTask(task);
- }
+ Path path = basePath.resolve((Path) event.context());
- String previousHash = hash;
- long previousHashTimestamp = hashTimestamp;
-
- hash = FileUtils.getHash(file.toPath());
- hashTimestamp = System.currentTimeMillis();
- task = ServerUtilsApp.getPlugin().getTaskManager().runTaskLater(() -> {
- if (hash.equals(previousHash) || previousHashTimestamp < hashTimestamp - 1000L) {
- send(WatchResult.CHANGE);
-
- pluginManager.reloadPlugin(pluginName);
- file = pluginManager.getPluginFile(pluginName).orElse(null);
- }
- }, 10L);
+ if (!Files.isDirectory(path)) {
+ handleWatchEvent(path);
}
}
- if (file == null || !key.reset()) {
+ if ((fileNameToWatchEntryMap.isEmpty() && pluginIdToWatchEntryMap.isEmpty()) || !key.reset()) {
send(WatchResult.STOPPED);
break;
}
@@ -102,10 +98,93 @@ public class PluginWatcherTask extends AbstractTask {
}
}
+ private void handleWatchEvent(Path path) {
+ String fileName = path.getFileName().toString();
+ WatchEntry entry = fileNameToWatchEntryMap.get(fileName);
+
+ if (entry == null && Files.exists(path)) {
+ Optional extends ServerUtilsPluginDescription> descriptionOptional;
+ try {
+ descriptionOptional = plugin.getPluginManager().getPluginDescription(path.toFile());
+ } catch (InvalidPluginDescriptionException ignored) {
+ return;
+ }
+
+ if (descriptionOptional.isPresent()) {
+ ServerUtilsPluginDescription description = descriptionOptional.get();
+ WatchEntry foundEntry = pluginIdToWatchEntryMap.remove(description.getId());
+ if (foundEntry != null) {
+ send(WatchResult.DELETED_FILE_IS_CREATED.arg(foundEntry.pluginId));
+ fileNameToWatchEntryMap.put(fileName, foundEntry);
+
+ if (pluginIdToWatchEntryMap.isEmpty()) {
+ entry = foundEntry;
+ }
+ }
+ }
+ }
+
+ if (entry != null) {
+ checkWatchEntry(entry, fileName);
+ }
+ }
+
+ private void checkWatchEntry(WatchEntry entry, String fileName) {
+ if (task != null) {
+ plugin.getTaskManager().cancelTask(task);
+ }
+
+ AbstractPluginManager pluginManager = plugin.getPluginManager();
+ Optional plugins = new ArrayList<>(fileNameToWatchEntryMap.size());
+ Map pluginOptional = pluginManager.getPlugin(oldEntry.pluginId);
+ if (!pluginOptional.isPresent()) continue;
+
+ plugins.add(pluginOptional.get());
+ retainedWatchEntries.put(oldEntry.pluginId, oldEntry);
+ }
+
+ fileNameToWatchEntryMap.clear();
+
+ PluginResults reloadResults = pluginManager.reloadPlugins(plugins);
+ reloadResults.sendTo(sender, "reload");
+
+ for (PluginResult reloadResult : reloadResults) {
+ if (!reloadResult.isSuccess()) continue;
+
+ P reloadedPlugin = reloadResult.getPlugin();
+ String pluginId = pluginManager.getPluginId(reloadedPlugin);
+
+ WatchEntry retainedEntry = retainedWatchEntries.get(pluginId);
+ String pluginFileName = pluginManager.getPluginFile(reloadedPlugin).getName();
+ fileNameToWatchEntryMap.put(pluginFileName, retainedEntry);
+ }
+ }
+ }, 10L);
+ }
+
private void send(WatchResult result) {
- result.sendTo(sender, null, pluginName);
+ result.sendTo(sender);
if (sender.isPlayer()) {
- result.sendTo(chatProvider.getConsoleSender(), null, pluginName);
+ result.sendTo(plugin.getChatProvider().getConsoleSender());
}
}
@@ -118,4 +197,20 @@ public class PluginWatcherTask extends AbstractTask {
ex.printStackTrace();
}
}
+
+ private static final class WatchEntry {
+
+ private final String pluginId;
+ private String hash = null;
+ private long timestamp = 0L;
+
+ public WatchEntry(String pluginId) {
+ this.pluginId = pluginId;
+ }
+
+ public void update(File file) {
+ this.hash = FileUtils.getHash(file.toPath());
+ this.timestamp = System.currentTimeMillis();
+ }
+ }
}
diff --git a/Common/src/main/resources/commands.json b/Common/src/main/resources/commands.json
index 7357d8d..ffecd21 100644
--- a/Common/src/main/resources/commands.json
+++ b/Common/src/main/resources/commands.json
@@ -80,8 +80,17 @@
"main": "watchplugin",
"aliases": ["wp"],
"permission": "serverutils.watchplugin",
- "description": "Watches the specified plugin for changes.",
- "display-in-help": true
+ "description": "Watches the specified plugin(s) for changes.",
+ "display-in-help": true,
+ "flags": {
+ "force": {
+ "main": "force",
+ "aliases": ["f"],
+ "permission": "serverutils.watchplugin",
+ "description": "Force watches the specified plugin(s) for changes.",
+ "display-in-help": false
+ }
+ }
},
"unwatchplugin": {
"main": "unwatchplugin",
diff --git a/Common/src/main/resources/messages.json b/Common/src/main/resources/messages.json
index b2a02ce..1cf76d5 100644
--- a/Common/src/main/resources/messages.json
+++ b/Common/src/main/resources/messages.json
@@ -20,11 +20,14 @@
"last_separator": " &cand ",
"override": "&cUse \"&4/%command%&c\" to force command execution."
},
- "watcher": {
+ "watchplugin": {
"start": "&3Started watching &b%what%&3!",
- "change": "&3Change detected for plugin &b%what%&3, reloading now...",
+ "change": "&3Changes detected, reloading plugins now...",
"stopped": "&3Stopped watching &b%what%&3!",
- "not_watching": "&cWe aren't watching that plugin!"
+ "file_deleted": "&cPlugin file &4%what%&c has been deleted! Waiting for plugin to show up...",
+ "deleted_file_is_created": "&3Plugin file &b%what%&3 has shown up again!",
+ "already_watching": "&cYou are already watching &4%what%&c!",
+ "not_watching": "&cYou are not watching &4%what%&c!"
},
"update": {
"available": "&8&m------------=&r&8[ &b&lServerUtils Update&r &8]&m=--------------\n &3Current version: &b%old%\n &3New version: &b%new%\n &3Release info: &b%info%\n&8&m-------------------------------------------------",