Automatically load particles that are added to or updated in the particles folder
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
package com.alttd.storage;
|
||||
|
||||
import com.alttd.config.ParticleConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class AutoReload {
|
||||
private final WatchService watchService;
|
||||
private final Map<WatchKey, Path> keys;
|
||||
private final Path rootDirectory;
|
||||
private volatile boolean running = true;
|
||||
|
||||
public AutoReload(Path directory) throws IOException {
|
||||
this.watchService = FileSystems.getDefault().newWatchService();
|
||||
this.keys = new HashMap<>();
|
||||
this.rootDirectory = directory;
|
||||
register(directory);
|
||||
registerAll(directory);
|
||||
}
|
||||
|
||||
private void registerAll(Path start) throws IOException {
|
||||
Files.walkFileTree(start, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public @NotNull FileVisitResult preVisitDirectory(@NotNull Path path, @NotNull BasicFileAttributes attrs) throws IOException {
|
||||
if (path.toFile().isDirectory()) {
|
||||
register(path);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void register(@NotNull Path dir) throws IOException {
|
||||
WatchKey key = dir.register(watchService,
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_DELETE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY);
|
||||
keys.put(key, dir);
|
||||
}
|
||||
|
||||
public void startWatching() {
|
||||
log.info("Starting watch thread.");
|
||||
Thread watchThread = new Thread(() -> {
|
||||
log.info("Watch thread started.");
|
||||
while (running) {
|
||||
log.info("Watch thread loop start");
|
||||
WatchKey key;
|
||||
try {
|
||||
key = watchService.take();
|
||||
log.info("Watch thread loop key {}", key.toString());
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted while waiting for key", e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!running) {
|
||||
log.info("Exiting watch thread.");
|
||||
return;
|
||||
}
|
||||
|
||||
Path dir = keys.get(key);
|
||||
if (dir == null) {
|
||||
log.warn("Detected unknown key: {}. Ignoring.", key.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
detectChanges(key, dir);
|
||||
|
||||
if (!key.reset()) {
|
||||
keys.remove(key);
|
||||
if (keys.isEmpty()) {
|
||||
log.info("No longer watching any directories. Exiting.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
watchThread.start();
|
||||
}
|
||||
|
||||
private void detectChanges(@NotNull WatchKey key, Path dir) {
|
||||
for (WatchEvent<?> event : key.pollEvents()) {
|
||||
WatchEvent.Kind<?> kind = event.kind();
|
||||
|
||||
if (kind == StandardWatchEventKinds.OVERFLOW) {
|
||||
log.warn("Detected overflow event. Ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Path child = resolveEventPath(event, dir);
|
||||
boolean isDirectory = Files.isDirectory(child);
|
||||
|
||||
if (shouldIgnoreDirectoryEvent(isDirectory, dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kind == StandardWatchEventKinds.ENTRY_CREATE && isDirectory) {
|
||||
handleNewDirectoryCreation(child);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDirectory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handleFileEvent(kind, child);
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull Path resolveEventPath(@NotNull WatchEvent<?> event, Path dir) {
|
||||
Object context = event.context();
|
||||
if (!(context instanceof Path path)) {
|
||||
throw new IllegalArgumentException("Expected event context to be a Path, but got: " + context);
|
||||
}
|
||||
|
||||
return dir.resolve(path);
|
||||
}
|
||||
|
||||
|
||||
private boolean shouldIgnoreDirectoryEvent(boolean isDirectory, Path dir) {
|
||||
if (isDirectory && !dir.equals(rootDirectory)) {
|
||||
log.warn("Detected directory {} outside of root directory. Ignoring.", dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleNewDirectoryCreation(Path child) {
|
||||
try {
|
||||
log.info("Registering new directory: {}", child);
|
||||
registerAll(child);
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to register directory: {}", child);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFileEvent(WatchEvent.Kind<?> kind, Path child) {
|
||||
if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
|
||||
log.debug("Detected file modification: {}", child);
|
||||
reloadFile(child);
|
||||
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
|
||||
log.debug("Detected file deletion: {}", child);
|
||||
handleFileDeletion();
|
||||
} else if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
|
||||
log.debug("Detected file creation: {}", child);
|
||||
reloadFile(child);
|
||||
} else {
|
||||
log.warn("Unknown event kind: {}", kind);
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadFile(Path child) {
|
||||
ParticleConfig.loadParticleFromFile(child.toFile());
|
||||
}
|
||||
|
||||
private void handleFileDeletion() {
|
||||
log.info("Detected file deletion. Reloading all particles.");
|
||||
ParticleConfig.reload();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user