Skip to content

Commit 802815d

Browse files
Load schematic cache async (#443)
* Load schematic cache async * Prevent reloading cache while reload is running * Bump version
1 parent 05046b4 commit 802815d

File tree

11 files changed

+81
-22
lines changed

11 files changed

+81
-22
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ plugins {
99
}
1010

1111
group = "de.eldoria"
12-
version = "2.7.9"
12+
version = "2.7.10"
1313

1414
var publishModules = setOf(
1515
"schematicbrushreborn-api",

schematicbrushreborn-api/src/main/java/de/eldoria/schematicbrush/schematics/SchematicCache.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import java.util.List;
1313
import java.util.Set;
14+
import java.util.concurrent.CompletableFuture;
1415

1516
/**
1617
* A cache which provides schematics based on filters or other factors.
@@ -23,13 +24,17 @@ public interface SchematicCache {
2324

2425
/**
2526
* Init method which will be executed when registered via {@link SchematicRegistry#register(Nameable, Object)}
27+
*
28+
* @return A future which completes when the cache is ready.
2629
*/
27-
void init();
30+
CompletableFuture<Void> init();
2831

2932
/**
30-
* Reload all schematics of the cache.
33+
* Reload the currently loaded schematics. This overrides the cache when the schematics are loaded.
34+
*
35+
* @return A future which completes when the reload is finished.
3136
*/
32-
void reload();
37+
CompletableFuture<Void> reload();
3338

3439
/**
3540
* Get a list of schematics which match a name or regex
@@ -84,6 +89,15 @@ public interface SchematicCache {
8489
*/
8590
int directoryCount();
8691

92+
/**
93+
* Returns whether the cache is ready to be used.
94+
* A cache might not be ready to be used if it is still loading, for example.
95+
* A cache that is not ready might, however, be used already and can return schematics.
96+
*
97+
* @return true if the cache is ready
98+
*/
99+
boolean isReady();
100+
87101
/**
88102
* Called when the plugin gets shutdown and the cache is unregistered.
89103
*/

schematicbrushreborn-api/src/main/java/de/eldoria/schematicbrush/schematics/SchematicRegistry.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import de.eldoria.schematicbrush.brush.config.util.Nameable;
1010
import de.eldoria.schematicbrush.registry.Registry;
1111

12+
import java.util.concurrent.CompletableFuture;
13+
1214
/**
1315
* A registry to register, manage and retrieve a {@link SchematicCache}.
1416
*/
@@ -30,7 +32,7 @@ default SchematicCache getCache(Nameable key) {
3032
/**
3133
* Reloads all registered caches.
3234
*/
33-
void reload();
35+
CompletableFuture<Void> reload();
3436

3537
/**
3638
* Returns the total amount of registered schematics

schematicbrushreborn-core/src/main/java/de/eldoria/schematicbrush/SchematicBrushRebornImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public void onPluginLoad() throws Throwable {
165165
}
166166

167167
@Override
168-
public void onPluginEnable() {
168+
public void onPluginEnable(boolean reload) {
169169
var localizer = Localizer.builder(this, configuration.general().language())
170170
.setIncludedLocales(LANGUAGES)
171171
.build();
@@ -193,7 +193,7 @@ public void onPluginEnable() {
193193
var notifyListener = new NotifyListener(this, configuration);
194194
renderService = new RenderService(this, configuration);
195195

196-
reload();
196+
if (reload) reload();
197197

198198
MessageBlocker messageBlocker;
199199
if (ServerVersion.CURRENT_VERSION.isOlder(Version.of(1, 19))) {

schematicbrushreborn-core/src/main/java/de/eldoria/schematicbrush/commands/admin/ReloadCache.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ public ReloadCache(SchematicBrushRebornImpl plugin, SchematicRegistry cache) {
2828

2929
@Override
3030
public void onCommand(@NotNull CommandSender sender, @NotNull String alias, @NotNull Arguments args) {
31-
cache.reload();
32-
messageSender().sendMessage(sender, "commands.admin.reloadCache.done");
31+
messageSender().sendMessage(sender, "commands.admin.reloadCache.start");
32+
cache.reload()
33+
.thenRun(() -> messageSender().sendMessage(sender, "commands.admin.reloadCache.done"));
3334
}
3435
}

schematicbrushreborn-core/src/main/java/de/eldoria/schematicbrush/schematics/SchematicBrushCache.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package de.eldoria.schematicbrush.schematics;
88

9+
import de.eldoria.eldoutilities.messages.MessageSender;
910
import de.eldoria.eldoutilities.utils.TextUtil;
1011
import de.eldoria.schematicbrush.SchematicBrushRebornImpl;
1112
import de.eldoria.schematicbrush.config.Configuration;
@@ -31,6 +32,8 @@
3132
import java.util.Queue;
3233
import java.util.Set;
3334
import java.util.UUID;
35+
import java.util.concurrent.CompletableFuture;
36+
import java.util.concurrent.ConcurrentHashMap;
3437
import java.util.logging.Level;
3538
import java.util.logging.Logger;
3639
import java.util.regex.Pattern;
@@ -42,26 +45,46 @@ public class SchematicBrushCache implements SchematicCache {
4245
private static final Logger logger = SchematicBrushRebornImpl.logger();
4346
private final JavaPlugin plugin;
4447
private final Configuration configuration;
45-
private final Map<String, Set<Schematic>> schematicsCache = new HashMap<>();
46-
private final Map<UUID, Map<String, Set<Schematic>>> userCache = new HashMap<>();
48+
private final Map<String, Set<Schematic>> schematicsCache = new ConcurrentHashMap<>();
49+
private final Map<UUID, Map<String, Set<Schematic>>> userCache = new ConcurrentHashMap<>();
4750
private SchematicWatchService watchService;
51+
private boolean ready;
52+
private final MessageSender messageSender;
4853

4954
public SchematicBrushCache(JavaPlugin plugin, Configuration configuration) {
5055
this.plugin = plugin;
5156
this.configuration = configuration;
57+
this.messageSender = MessageSender.getPluginMessageSender(plugin);
5258
}
5359

5460
@Override
55-
public void init() {
56-
reload();
61+
public CompletableFuture<Void> init() {
5762
watchService = SchematicWatchService.of(plugin, configuration, this);
63+
return reload();
5864
}
5965

60-
/**
61-
* Reload the current loaded schematics. This overrides the cache, when the schematics are loaded.
62-
*/
6366
@Override
64-
public void reload() {
67+
public CompletableFuture<Void> reload() {
68+
return refreshAsync();
69+
}
70+
71+
private void assertReady(Player player) {
72+
if (!ready) {
73+
messageSender.sendError(player, "error.cacheNotReady");
74+
}
75+
}
76+
77+
private CompletableFuture<Void> refreshAsync() {
78+
return CompletableFuture.runAsync(this::refreshSync)
79+
.exceptionally(ex -> {
80+
logger.log(Level.SEVERE, "Failed to reload schematics.", ex);
81+
return null;
82+
});
83+
}
84+
85+
private void refreshSync() {
86+
ready = false;
87+
6588
plugin.getLogger().log(Level.CONFIG, "Reloading schematics.");
6689

6790
var root = plugin.getDataFolder().toPath().getParent().toString();
@@ -82,6 +105,7 @@ public void reload() {
82105
var load = source.isRelative() ? Paths.get(root, path) : Paths.get(path);
83106
loadSchematics(load, source);
84107
}
108+
ready = true;
85109
}
86110

87111
private void loadSchematics(Path schematicFolder, SchematicSource source) {
@@ -199,8 +223,8 @@ private void importSchematic(Path path, SchematicSource source) {
199223

200224
if (playerUid != null) {
201225
userCache.computeIfAbsent(playerUid, key -> new HashMap<>())
202-
.computeIfAbsent(cleanKey, key -> new HashSet<>())
203-
.add(schematic);
226+
.computeIfAbsent(cleanKey, key -> new HashSet<>())
227+
.add(schematic);
204228
} else {
205229
schematicsCache.computeIfAbsent(cleanKey, key -> new HashSet<>()).add(schematic);
206230
}
@@ -261,6 +285,7 @@ private Set<Schematic> filterSchematics(Set<Schematic> schematics, String filter
261285
*/
262286
@Override
263287
public Set<Schematic> getSchematicsByDirectory(Player player, String name, String filter) {
288+
assertReady(player);
264289
// if folder name ends with a '*' perform a deep search and return every schematic in folder and sub folders.
265290
if (name.endsWith("*")) {
266291
var pureName = name.replace("*", "").toLowerCase();
@@ -346,6 +371,7 @@ private Pattern buildRegex(String name) throws PatternSyntaxException {
346371
*/
347372
@Override
348373
public List<String> getMatchingDirectories(Player player, String dir, int count) {
374+
assertReady(player);
349375
Set<String> matches = new HashSet<>();
350376
var separator = configuration.schematicConfig().pathSeparator().charAt(0);
351377
var deep = TextUtil.countChars(dir, separator);
@@ -386,6 +412,7 @@ private String trimPath(String input, char separator, int deep) {
386412
*/
387413
@Override
388414
public List<String> getMatchingSchematics(Player player, String name, int count) {
415+
assertReady(player);
389416
List<String> matches = new ArrayList<>();
390417
for (var entry : schematicsCache.entrySet()) {
391418
for (var schematic : entry.getValue()) {
@@ -420,6 +447,11 @@ public int directoryCount() {
420447
return schematicsCache.keySet().size();
421448
}
422449

450+
@Override
451+
public boolean isReady() {
452+
return ready;
453+
}
454+
423455
@Override
424456
public void shutdown() {
425457
watchService.shutdown();

schematicbrushreborn-core/src/main/java/de/eldoria/schematicbrush/schematics/SchematicRegistryImpl.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import de.eldoria.schematicbrush.registry.BaseRegistry;
1212

1313
import java.util.HashSet;
14+
import java.util.concurrent.CompletableFuture;
1415

1516
@SuppressWarnings("unused")
1617
public class SchematicRegistryImpl extends BaseRegistry<Nameable, SchematicCache> implements SchematicRegistry {
18+
boolean reload = false;
1719

1820

1921
@Override
@@ -26,8 +28,10 @@ public void register(Nameable key, SchematicCache entry) {
2628
* Reloads all registered caches.
2729
*/
2830
@Override
29-
public void reload() {
30-
registry().values().forEach(SchematicCache::reload);
31+
public CompletableFuture<Void> reload() {
32+
if (reload) {return CompletableFuture.completedFuture(null);}
33+
var reloads = registry().values().stream().map(SchematicCache::reload).toArray(CompletableFuture[]::new);
34+
return CompletableFuture.allOf(reloads).thenRun(() -> reload = true);
3135
}
3236

3337
/**

schematicbrushreborn-core/src/main/resources/messages.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ error.unknownModifierType=
162162
error.unknownPlayer=
163163
error.unknownPreset=
164164
error.unknownWorld=
165+
error.cacheNotReady=
165166

166167
service.renderService.error.sizeExceeded=
167168

@@ -184,3 +185,4 @@ words.schematics=
184185
words.selector=
185186
words.sets=
186187
words.weight=
188+
commands.admin.reloadCache.start=

schematicbrushreborn-core/src/main/resources/messages_de_DE.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,5 @@ words.weight=Gewichtung
182182
components.brush.information=Informationen über Brush
183183
components.preset.information=Informationen über Vorlage
184184
words.description=Beschreibung
185+
commands.admin.reloadCache.start=Lade Schematics neu. Das kann eine Weile dauern.
186+
error.cacheNotReady=Der Cache lädt derzeit. Ergebnisse sind eventuell unvollständig.

schematicbrushreborn-core/src/main/resources/messages_en_US.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,5 @@ components.brush.information=Information about brush
190190
components.preset.information=Information about preset
191191

192192
words.description=Description
193+
commands.admin.reloadCache.start=Reloading schematics. This might take a while.
194+
error.cacheNotReady=The cache is currently loading. Results might be incomplete.

0 commit comments

Comments
 (0)