Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schematic caching system for suggestions #2542

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

me4502
Copy link
Member

@me4502 me4502 commented Jun 1, 2024

This is a continuation of #2212, as the PR is abandoned by its original author

@me4502 me4502 force-pushed the feature/schematic-manager branch from cf27d60 to ff6393e Compare June 1, 2024 04:45
@me4502 me4502 added this to the 7.4.0 milestone Jun 8, 2024
Comment on lines +410 to +413
private static final java.util.regex.Pattern SAFE_FILENAME_REGEX = java.util.regex.Pattern.compile("^[A-Za-z0-9_\\- \\./\\\\'\\$@~!%\\^\\*\\(\\)\\[\\]\\+\\{\\},\\?]+\\.[A-Za-z0-9]+$");

private boolean checkFilename(String filename) {
return filename.matches("^[A-Za-z0-9_\\- \\./\\\\'\\$@~!%\\^\\*\\(\\)\\[\\]\\+\\{\\},\\?]+\\.[A-Za-z0-9]+$");
return SAFE_FILENAME_REGEX.matcher(filename).matches();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This set of characters doesn't make sense, it's not safe for windows (contains ?, *, doesn't ban COM and others), and POSIX will generally take anything. It also excludes extended Unicode characters for some reason, despite those being perfectly fine.

Can we just drop checking this aside from "you must have an extension", i.e. ^.+\..+$?

Comment on lines +101 to +108
public static Optional<RecursiveDirectoryWatcher> create(Path root) throws IOException {
try {
WatchService watchService = root.getFileSystem().newWatchService();
return Optional.of(new RecursiveDirectoryWatcher(root, watchService));
} catch (UnsupportedOperationException ignored) {
return Optional.empty();
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not bother with the Optional path here. We're not currently dealing with non-default Path providers, and I don't expect to any time soon. Quote from the Javadocs of newWatchService:

This exception is not thrown by FileSystems created by the default provider.

So, let's remove the Optional and just throw.

Suggested change
public static Optional<RecursiveDirectoryWatcher> create(Path root) throws IOException {
try {
WatchService watchService = root.getFileSystem().newWatchService();
return Optional.of(new RecursiveDirectoryWatcher(root, watchService));
} catch (UnsupportedOperationException ignored) {
return Optional.empty();
}
}
public static RecursiveDirectoryWatcher create(Path root) throws IOException {
WatchService watchService;
try {
watchService = root.getFileSystem().newWatchService();
} catch (UnsupportedOperationException e) {
throw new IllegalArgumentException("Root must support file watching", new RecursiveDirectoryWatcher(root, watchService));
}
return new RecursiveDirectoryWatcher(root, watchService);
}

Comment on lines +57 to +73
private void createFallbackBackend() {
LOGGER.warn("Failed to initialize file-monitoring based schematics backend. Falling back to polling.");
backend = PollingSchematicsBackend.create(schematicsDir);
}

private void setupBackend() {
try {
var fileWatcherBackend = FileWatcherSchematicsBackend.create(schematicsDir);
if (fileWatcherBackend.isPresent()) {
backend = fileWatcherBackend.get();
} else {
createFallbackBackend();
}
} catch (IOException e) {
createFallbackBackend();
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that my Optional-removing suggestion is accepted, let's clean this up too:

Suggested change
private void createFallbackBackend() {
LOGGER.warn("Failed to initialize file-monitoring based schematics backend. Falling back to polling.");
backend = PollingSchematicsBackend.create(schematicsDir);
}
private void setupBackend() {
try {
var fileWatcherBackend = FileWatcherSchematicsBackend.create(schematicsDir);
if (fileWatcherBackend.isPresent()) {
backend = fileWatcherBackend.get();
} else {
createFallbackBackend();
}
} catch (IOException e) {
createFallbackBackend();
}
}
private void setupBackend() {
try {
backend = FileWatcherSchematicsBackend.create(schematicsDir);
} catch (IOException e) {
LOGGER.warn("Failed to initialize file-monitoring based schematics backend. Falling back to polling.", e);
backend = PollingSchematicsBackend.create(schematicsDir);
}
}

setupBackend();
} catch (IOException e) {
LOGGER.warn("Failed to create schematics directory", e);
backend = new DummySchematicsBackend(); //fallback to dummy backend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment isn't needed. It's obvious.

Suggested change
backend = new DummySchematicsBackend(); //fallback to dummy backend
backend = new DummySchematicsBackend();

Comment on lines +81 to +83
schematicsDir = worldEdit.getWorkingDirectoryPath(worldEdit.getConfiguration().saveDir);
Files.createDirectories(schematicsDir);
schematicsDir = schematicsDir.toRealPath();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not set this twice.

Suggested change
schematicsDir = worldEdit.getWorkingDirectoryPath(worldEdit.getConfiguration().saveDir);
Files.createDirectories(schematicsDir);
schematicsDir = schematicsDir.toRealPath();
Path schematicsDirByConfig = worldEdit.getWorkingDirectoryPath(worldEdit.getConfiguration().saveDir);
Files.createDirectories(schematicsDirByConfig);
schematicsDir = schematicsDirByConfig.toRealPath();

/**
* A backend that never lists any files.
*/
public class DummySchematicsBackend implements SchematicsBackend {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this backend should throw instead. We're using it as the driving force behind //schem list now, so if it fails, people shouldn't just get an empty list out, they should get an error. Or possibly, we shouldn't have this at all and should crash out if we have an error setting up.

Comment on lines +39 to +40
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Set<Path> schematics = new HashSet<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not using a Sets.newConcurrentHashSet()? It would work much better since it doesn't need to lock the whole set.

Path schematicRoot = WorldEdit.getInstance().getSchematicsManager().getRoot();

this.eventConsumer = eventConsumer;
watchThread = new Thread(() -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just in case, we should check for an existing watchThread and kill it or throw. Killing requires us to have a means of communicating to it.

}
LOGGER.debug("RecursiveDirectoryWatcher::EventConsumer exited");
});
watchThread.setName("RecursiveDirectoryWatcher");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very least we should identify ourselves fully. Perhaps we should also use a global counter.

Suggested change
watchThread.setName("RecursiveDirectoryWatcher");
watchThread.setName("WorldEdit-RecursiveDirectoryWatcher");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants