Add multi plugin watcher

This commit is contained in:
Frank van der Heijden 2021-07-31 21:05:03 +02:00
parent 1b4997869d
commit 8d3a85d472
No known key found for this signature in database
GPG key ID: B808721C2DD5B5B8
8 changed files with 300 additions and 103 deletions

View file

@ -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.commands.arguments.PluginsArgument;
import net.frankheijden.serverutils.common.config.MessagesResource; import net.frankheijden.serverutils.common.config.MessagesResource;
import net.frankheijden.serverutils.common.config.ServerUtilsConfig; 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.CloseablePluginResults;
import net.frankheijden.serverutils.common.entities.results.PluginResult; import net.frankheijden.serverutils.common.entities.results.PluginResult;
import net.frankheijden.serverutils.common.entities.results.PluginResults; import net.frankheijden.serverutils.common.entities.results.PluginResults;
import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.entities.results.Result;
import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin; 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.managers.AbstractPluginManager;
import net.frankheijden.serverutils.common.utils.FormatBuilder; import net.frankheijden.serverutils.common.utils.FormatBuilder;
import net.frankheijden.serverutils.common.utils.ListBuilder; import net.frankheijden.serverutils.common.utils.ListBuilder;
@ -73,7 +73,11 @@ public abstract class CommandServerUtils<U extends ServerUtilsPlugin<P, ?, C, ?,
)) ))
.handler(this::handleReloadPlugin)); .handler(this::handleReloadPlugin));
manager.command(buildSubcommand(builder, "watchplugin") manager.command(buildSubcommand(builder, "watchplugin")
.argument(getArgument("plugin")) .argument(new PluginsArgument<>(
true,
"plugins",
new PluginsArgument.PluginsParser<>(plugin, arrayCreator, getRawPath("watchplugin"))
))
.handler(this::handleWatchPlugin)); .handler(this::handleWatchPlugin));
manager.command(buildSubcommand(builder, "unwatchplugin") manager.command(buildSubcommand(builder, "unwatchplugin")
.argument(getArgument("plugin")) .argument(getArgument("plugin"))
@ -256,24 +260,23 @@ public abstract class CommandServerUtils<U extends ServerUtilsPlugin<P, ?, C, ?,
private void handleWatchPlugin(CommandContext<C> context) { private void handleWatchPlugin(CommandContext<C> context) {
C sender = context.getSender(); C sender = context.getSender();
P pluginArg = context.get("plugin"); List<P> plugins = Arrays.asList(context.get("plugins"));
AbstractPluginManager<P, ?> pluginManager = plugin.getPluginManager(); if (checkDependingPlugins(context, sender, plugins, "watchplugin")) {
String pluginId = pluginManager.getPluginId(pluginArg); return;
}
AbstractResult result = pluginManager.watchPlugin(sender, pluginId); WatchResult result = plugin.getWatchManager().watchPlugins(sender, plugins);
result.sendTo(sender, "watch", pluginId); result.sendTo(sender);
} }
private void handleUnwatchPlugin(CommandContext<C> context) { private void handleUnwatchPlugin(CommandContext<C> context) {
C sender = context.getSender(); C sender = context.getSender();
P pluginArg = context.get("plugin"); P pluginArg = context.get("plugin");
AbstractPluginManager<P, ?> pluginManager = plugin.getPluginManager(); String pluginId = plugin.getPluginManager().getPluginId(pluginArg);
String pluginId = pluginManager.getPluginId(pluginArg); WatchResult result = plugin.getWatchManager().unwatchPluginsAssociatedWith(pluginId);
result.sendTo(sender);
AbstractResult result = pluginManager.unwatchPlugin(pluginId);
result.sendTo(sender, "unwatch", pluginId);
} }
private void handlePluginInfo(CommandContext<C> context) { private void handlePluginInfo(CommandContext<C> context) {

View file

@ -17,6 +17,7 @@ import net.frankheijden.serverutils.common.config.MessagesResource;
import net.frankheijden.serverutils.common.managers.AbstractPluginManager; import net.frankheijden.serverutils.common.managers.AbstractPluginManager;
import net.frankheijden.serverutils.common.managers.AbstractTaskManager; import net.frankheijden.serverutils.common.managers.AbstractTaskManager;
import net.frankheijden.serverutils.common.managers.UpdateManager; import net.frankheijden.serverutils.common.managers.UpdateManager;
import net.frankheijden.serverutils.common.managers.WatchManager;
import net.frankheijden.serverutils.common.providers.ChatProvider; import net.frankheijden.serverutils.common.providers.ChatProvider;
import net.frankheijden.serverutils.common.providers.ResourceProvider; import net.frankheijden.serverutils.common.providers.ResourceProvider;
import net.frankheijden.serverutils.common.utils.FileUtils; import net.frankheijden.serverutils.common.utils.FileUtils;
@ -24,6 +25,7 @@ import net.frankheijden.serverutils.common.utils.FileUtils;
public abstract class ServerUtilsPlugin<P, T, C extends ServerCommandSender<S>, S, D extends ServerUtilsPluginDescription> { public abstract class ServerUtilsPlugin<P, T, C extends ServerCommandSender<S>, S, D extends ServerUtilsPluginDescription> {
private final UpdateManager updateManager = new UpdateManager(); private final UpdateManager updateManager = new UpdateManager();
private final WatchManager<P, T> watchManager = new WatchManager<>(this);
private CommandsResource commandsResource; private CommandsResource commandsResource;
private ConfigResource configResource; private ConfigResource configResource;
private MessagesResource messagesResource; private MessagesResource messagesResource;
@ -57,6 +59,10 @@ public abstract class ServerUtilsPlugin<P, T, C extends ServerCommandSender<S>,
return updateManager; return updateManager;
} }
public WatchManager<P, T> getWatchManager() {
return watchManager;
}
public abstract Logger getLogger(); public abstract Logger getLogger();
public abstract File getDataFolder(); public abstract File getDataFolder();

View file

@ -1,28 +1,60 @@
package net.frankheijden.serverutils.common.entities.results; 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.ServerUtilsApp;
import net.frankheijden.serverutils.common.config.MessagesResource;
import net.frankheijden.serverutils.common.entities.ServerCommandSender; import net.frankheijden.serverutils.common.entities.ServerCommandSender;
public enum WatchResult implements AbstractResult { public enum WatchResult implements AbstractResult {
START, START,
CHANGE, CHANGE,
ALREADY_WATCHING,
NOT_WATCHING, NOT_WATCHING,
FILE_DELETED,
DELETED_FILE_IS_CREATED,
STOPPED; STOPPED;
/** private List<String> args = null;
* Retrieves the associated message of the result
* and sends it to a CommandSender. public WatchResult arg(String arg) {
* @param sender The receiver. return args(Collections.singletonList(arg));
* @param action The action which let to the result. }
* @param what An associated variable.
*/ public WatchResult args(List<String> args) {
this.args = args;
return this;
}
@Override @Override
public void sendTo(ServerCommandSender<?> sender, String action, String what) { public void sendTo(ServerCommandSender<?> sender, String action, String what) {
ServerUtilsApp.getPlugin().getMessagesResource().sendMessage( arg(what);
sender, sendTo(sender);
"serverutils.watcher." + this.name().toLowerCase(), }
"%what%", what
); /**
* Sends the result(s) to the console and specified sender.
*/
public void sendTo(ServerCommandSender<?> sender) {
String path = "serverutils.watchplugin." + this.name().toLowerCase();
List<String[]> sendArguments = new ArrayList<>();
if (args == null || args.isEmpty()) {
sendArguments.add(new String[0]);
} else {
for (String what : args) {
sendArguments.add(new String[] { "%what%", what });
}
}
MessagesResource messages = ServerUtilsApp.getPlugin().getMessagesResource();
ServerCommandSender<?> console = ServerUtilsApp.getPlugin().getChatProvider().getConsoleSender();
for (String[] replacements : sendArguments) {
messages.sendMessage(sender, path, replacements);
if (sender.isPlayer()) {
messages.sendMessage(console, path, replacements);
}
}
} }
} }

View file

@ -10,19 +10,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import net.frankheijden.serverutils.common.ServerUtilsApp;
import net.frankheijden.serverutils.common.entities.results.AbstractResult;
import net.frankheijden.serverutils.common.entities.results.CloseablePluginResult; import net.frankheijden.serverutils.common.entities.results.CloseablePluginResult;
import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults; import net.frankheijden.serverutils.common.entities.results.CloseablePluginResults;
import net.frankheijden.serverutils.common.entities.results.PluginResult; import net.frankheijden.serverutils.common.entities.results.PluginResult;
import net.frankheijden.serverutils.common.entities.results.PluginResults; import net.frankheijden.serverutils.common.entities.results.PluginResults;
import net.frankheijden.serverutils.common.entities.results.Result; import net.frankheijden.serverutils.common.entities.results.Result;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription; import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription;
import net.frankheijden.serverutils.common.entities.results.WatchResult;
import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException; import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException;
import net.frankheijden.serverutils.common.providers.PluginProvider; import net.frankheijden.serverutils.common.providers.PluginProvider;
import net.frankheijden.serverutils.common.tasks.PluginWatcherTask;
import net.frankheijden.serverutils.common.utils.DependencyUtils; import net.frankheijden.serverutils.common.utils.DependencyUtils;
public abstract class AbstractPluginManager<P, D extends ServerUtilsPluginDescription> implements PluginProvider<P, D> { public abstract class AbstractPluginManager<P, D extends ServerUtilsPluginDescription> implements PluginProvider<P, D> {
@ -294,23 +289,4 @@ public abstract class AbstractPluginManager<P, D extends ServerUtilsPluginDescri
return DependencyUtils.determineOrder(dependencyMap); return DependencyUtils.determineOrder(dependencyMap);
} }
/**
* Starts watching the specified plugin for changes.
* Reloads the plugin if a change is detected.
*/
public AbstractResult watchPlugin(ServerCommandSender<?> 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;
}
} }

View file

@ -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<P, T> {
private final ServerUtilsPlugin<P, T, ?, ?, ?> plugin;
private final Map<String, WatchTask> watchTasks;
public WatchManager(ServerUtilsPlugin<P, T, ?, ?, ?> 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<P> plugins) {
List<String> pluginIds = new ArrayList<>(plugins.size());
for (P watchPlugin : plugins) {
String pluginId = plugin.getPluginManager().getPluginId(watchPlugin);
if (watchTasks.containsKey(pluginId)) {
return WatchResult.ALREADY_WATCHING.arg(pluginId);
}
pluginIds.add(plugin.getPluginManager().getPluginId(watchPlugin));
}
UUID key = UUID.randomUUID();
plugin.getTaskManager().runTaskAsynchronously(
key.toString(),
new PluginWatcherTask<>(plugin, sender, plugins)
);
WatchTask watchTask = new WatchTask(key, pluginIds);
for (String pluginId : pluginIds) {
watchTasks.put(pluginId, watchTask);
}
return WatchResult.START.args(pluginIds);
}
/**
* Stops watching plugins for changes.
*/
public WatchResult unwatchPluginsAssociatedWith(String pluginId) {
WatchTask task = watchTasks.get(pluginId);
if (task != null && plugin.getTaskManager().cancelTask(task.key.toString())) {
task.pluginIds.forEach(watchTasks::remove);
return WatchResult.STOPPED.args(task.pluginIds);
}
return WatchResult.NOT_WATCHING.arg(pluginId);
}
private static final class WatchTask {
private final UUID key;
private final List<String> pluginIds;
private WatchTask(UUID key, List<String> pluginIds) {
this.key = key;
this.pluginIds = pluginIds;
}
}
}

View file

@ -1,26 +1,34 @@
package net.frankheijden.serverutils.common.tasks; package net.frankheijden.serverutils.common.tasks;
import com.sun.nio.file.SensitivityWatchEventModifier; import com.sun.nio.file.SensitivityWatchEventModifier;
import net.frankheijden.serverutils.common.ServerUtilsApp;
import net.frankheijden.serverutils.common.entities.AbstractTask;
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.managers.AbstractTaskManager;
import net.frankheijden.serverutils.common.providers.ChatProvider;
import net.frankheijden.serverutils.common.utils.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.ClosedWatchServiceException; import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds; import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent; import java.nio.file.WatchEvent;
import java.nio.file.WatchKey; import java.nio.file.WatchKey;
import java.nio.file.WatchService; import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import net.frankheijden.serverutils.common.entities.AbstractTask;
import net.frankheijden.serverutils.common.entities.ServerCommandSender;
import net.frankheijden.serverutils.common.entities.ServerUtilsPlugin;
import net.frankheijden.serverutils.common.entities.ServerUtilsPluginDescription;
import net.frankheijden.serverutils.common.entities.exceptions.InvalidPluginDescriptionException;
import net.frankheijden.serverutils.common.entities.results.PluginResult;
import net.frankheijden.serverutils.common.entities.results.PluginResults;
import net.frankheijden.serverutils.common.entities.results.WatchResult;
import net.frankheijden.serverutils.common.managers.AbstractPluginManager;
import net.frankheijden.serverutils.common.utils.FileUtils;
public class PluginWatcherTask extends AbstractTask { public class PluginWatcherTask<P, T> extends AbstractTask {
private static final WatchEvent.Kind<?>[] EVENTS = new WatchEvent.Kind[]{ private static final WatchEvent.Kind<?>[] EVENTS = new WatchEvent.Kind[]{
StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_CREATE,
@ -28,32 +36,33 @@ public class PluginWatcherTask extends AbstractTask {
StandardWatchEventKinds.ENTRY_DELETE StandardWatchEventKinds.ENTRY_DELETE
}; };
private final ServerUtilsPlugin<?, ?, ?, ?, ?> plugin = ServerUtilsApp.getPlugin(); private final ServerUtilsPlugin<P, T, ?, ?, ?> plugin;
private final AbstractPluginManager<?, ?> pluginManager = plugin.getPluginManager();
private final ChatProvider<?, ?> chatProvider = plugin.getChatProvider();
@SuppressWarnings("rawtypes")
private final AbstractTaskManager taskManager = plugin.getTaskManager();
private final ServerCommandSender<?> sender; private final ServerCommandSender<?> sender;
private final String pluginName; private final Map<String, WatchEntry> fileNameToWatchEntryMap;
private final AtomicBoolean run; private final Map<String, WatchEntry> pluginIdToWatchEntryMap;
private File file;
private String hash;
private long hashTimestamp = 0;
private final AtomicBoolean run = new AtomicBoolean(true);
private WatchService watchService; private WatchService watchService;
private Object task = null; private T task = null;
/** /**
* Constructs a new PluginWatcherTask for the specified plugin. * Constructs a new PluginWatcherTask for the specified plugin.
*
* @param pluginName The name of the plugin.
*/ */
public PluginWatcherTask(ServerCommandSender<?> sender, String pluginName) { public PluginWatcherTask(ServerUtilsPlugin<P, T, ?, ?, ?> plugin, ServerCommandSender<?> sender, List<P> plugins) {
this.plugin = plugin;
this.sender = sender; this.sender = sender;
this.pluginName = pluginName; this.fileNameToWatchEntryMap = new HashMap<>();
this.file = pluginManager.getPluginFile(pluginName).orElse(null); this.pluginIdToWatchEntryMap = new HashMap<>();
this.run = new AtomicBoolean(true);
AbstractPluginManager<P, ?> 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 @Override
@ -61,34 +70,21 @@ public class PluginWatcherTask extends AbstractTask {
try (WatchService watchService = FileSystems.getDefault().newWatchService()) { try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
this.watchService = watchService; this.watchService = watchService;
File folder = pluginManager.getPluginsFolder(); AbstractPluginManager<P, ?> pluginManager = plugin.getPluginManager();
folder.toPath().register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH); Path basePath = pluginManager.getPluginsFolder().toPath();
basePath.register(watchService, EVENTS, SensitivityWatchEventModifier.HIGH);
while (run.get()) { while (run.get()) {
WatchKey key = watchService.take(); WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) { for (WatchEvent<?> event : key.pollEvents()) {
if (file.getName().equals(event.context().toString())) { Path path = basePath.resolve((Path) event.context());
if (task != null) {
taskManager.cancelTask(task);
}
String previousHash = hash; if (!Files.isDirectory(path)) {
long previousHashTimestamp = hashTimestamp; handleWatchEvent(path);
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 (file == null || !key.reset()) { if ((fileNameToWatchEntryMap.isEmpty() && pluginIdToWatchEntryMap.isEmpty()) || !key.reset()) {
send(WatchResult.STOPPED); send(WatchResult.STOPPED);
break; 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<P, ?> pluginManager = plugin.getPluginManager();
Optional<File> fileOptional = pluginManager.getPluginFile(entry.pluginId);
if (!fileOptional.isPresent()) {
send(WatchResult.FILE_DELETED.arg(entry.pluginId));
fileNameToWatchEntryMap.remove(fileName);
pluginIdToWatchEntryMap.put(entry.pluginId, entry);
return;
}
String previousHash = entry.hash;
long previousTimestamp = entry.timestamp;
entry.update(fileOptional.get());
task = plugin.getTaskManager().runTaskLater(() -> {
if (entry.hash.equals(previousHash) || previousTimestamp < entry.timestamp - 1000L) {
send(WatchResult.CHANGE);
List<P> plugins = new ArrayList<>(fileNameToWatchEntryMap.size());
Map<String, WatchEntry> retainedWatchEntries = new HashMap<>();
for (WatchEntry oldEntry : fileNameToWatchEntryMap.values()) {
Optional<P> pluginOptional = pluginManager.getPlugin(oldEntry.pluginId);
if (!pluginOptional.isPresent()) continue;
plugins.add(pluginOptional.get());
retainedWatchEntries.put(oldEntry.pluginId, oldEntry);
}
fileNameToWatchEntryMap.clear();
PluginResults<P> reloadResults = pluginManager.reloadPlugins(plugins);
reloadResults.sendTo(sender, "reload");
for (PluginResult<P> 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) { private void send(WatchResult result) {
result.sendTo(sender, null, pluginName); result.sendTo(sender);
if (sender.isPlayer()) { 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(); 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();
}
}
} }

View file

@ -80,8 +80,17 @@
"main": "watchplugin", "main": "watchplugin",
"aliases": ["wp"], "aliases": ["wp"],
"permission": "serverutils.watchplugin", "permission": "serverutils.watchplugin",
"description": "Watches the specified plugin for changes.", "description": "Watches the specified plugin(s) for changes.",
"display-in-help": true "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": { "unwatchplugin": {
"main": "unwatchplugin", "main": "unwatchplugin",

View file

@ -20,11 +20,14 @@
"last_separator": " &cand ", "last_separator": " &cand ",
"override": "&cUse \"&4/%command%&c\" to force command execution." "override": "&cUse \"&4/%command%&c\" to force command execution."
}, },
"watcher": { "watchplugin": {
"start": "&3Started watching &b%what%&3!", "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!", "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": { "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-------------------------------------------------", "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-------------------------------------------------",