Skip to content

Commit edb97ac

Browse files
committed
feat: defer config saving and use namespaced item IDs
Introduces a deferred saving mechanism for dynamic skills and classes during startup, queuing file writes to execute asynchronously after initial registration. This reduces synchronous I/O and improves loading performance. Additionally, updates item data serialization to use namespaced IDs, ensuring consistent and robust identification of items.
1 parent cdab0d7 commit edb97ac

2 files changed

Lines changed: 107 additions & 57 deletions

File tree

src/main/java/studio/magemonkey/fabled/api/util/Data.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public static void serializeIcon(ItemStack item, DataSection config) {
9898
CodexItemManager itemManager = CodexEngine.get().getItemManager();
9999
ItemType itemType = itemManager.getMainItemType(item);
100100
if (itemType != null) {
101-
config.set(MAT, itemType.getID());
101+
config.set(MAT, itemType.getNamespacedID());
102102
} else {
103103
config.set(MAT, item.getType().name());
104104
}

src/main/java/studio/magemonkey/fabled/manager/RegistrationManager.java

Lines changed: 106 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
import java.io.IOException;
4747
import java.nio.file.Files;
4848
import java.nio.file.Path;
49+
import java.util.ArrayList;
50+
import java.util.List;
4951

5052
/**
5153
* <p>Skill API Registration Manager.</p>
@@ -61,6 +63,7 @@ public class RegistrationManager {
6163
private final Fabled api;
6264
private final CommentedConfig skillConfig;
6365
private final CommentedConfig classConfig;
66+
private final List<DeferredSave> deferredSaves = new ArrayList<>();
6467
private Mode mode = Mode.STARTUP;
6568

6669
/**
@@ -171,21 +174,17 @@ public void initialize() {
171174
continue;
172175
}
173176
try {
174-
DynamicSkill skill = new DynamicSkill(key);
175-
skill.load(skillConfig.getConfig().getSection(key));
176-
if (!Fabled.isSkillRegistered(skill.getName())) {
177-
api.addDynamicSkill(skill);
178-
skill.registerEvents(api);
179-
String path = getPath(Fabled.inst().getDataFolder() + File.separator + SKILL_DIR, key);
180-
if (path == null) path = key;
181-
CommentedConfig sConfig = new CommentedConfig(api, SKILL_DIR + path);
182-
sConfig.clear();
183-
skill.save(sConfig.getConfig().createSection(key));
184-
skill.save(skillConfig.getConfig().createSection(key));
185-
sConfig.save();
186-
Logger.log(LogType.REGISTRATION, 2, "Loaded the dynamic skill: " + key);
187-
} else {
188-
Logger.invalid("Duplicate skill detected: " + key);
177+
DynamicSkill skill = new DynamicSkill(key);
178+
skill.load(skillConfig.getConfig().getSection(key));
179+
if (!Fabled.isSkillRegistered(skill.getName())) {
180+
api.addDynamicSkill(skill);
181+
skill.registerEvents(api);
182+
String path = getPath(Fabled.inst().getDataFolder() + File.separator + SKILL_DIR, key);
183+
if (path == null) path = key;
184+
queueDynamicSkillSave(skill, path, key);
185+
Logger.log(LogType.REGISTRATION, 2, "Loaded the dynamic skill: " + key);
186+
} else {
187+
Logger.invalid("Duplicate skill detected: " + key);
189188
}
190189
} catch (Exception ex) {
191190
Logger.invalid("Failed to load skill: " + key + " - " + ex.getMessage());
@@ -214,10 +213,7 @@ public void initialize() {
214213
if (!Fabled.isSkillRegistered(skill.getName())) {
215214
api.addDynamicSkill(skill);
216215
skill.registerEvents(api);
217-
sConfig.clear();
218-
skill.save(sConfig.getConfig().createSection(name));
219-
skill.save(skillConfig.getConfig().createSection(name));
220-
sConfig.save();
216+
queueDynamicSkillSave(skill, longName, name);
221217
Logger.log(LogType.REGISTRATION, 2, "Loaded the dynamic skill: " + name);
222218
} else if (Fabled.getSkill(name) instanceof DynamicSkill) {
223219
Logger.log(LogType.REGISTRATION, 3, name + " is already loaded, skipping it");
@@ -263,20 +259,16 @@ public void initialize() {
263259
continue;
264260
}
265261
try {
266-
DynamicClass tree = new DynamicClass(api, key);
267-
tree.load(classConfig.getConfig().getSection(key));
268-
if (!Fabled.isClassRegistered(tree.getName())) {
269-
api.addDynamicClass(tree);
270-
String path = getPath(Fabled.inst().getDataFolder() + File.separator + CLASS_DIR, key);
271-
if (path == null) path = key;
272-
CommentedConfig cConfig = new CommentedConfig(api, CLASS_DIR + path);
273-
cConfig.clear();
274-
tree.save(cConfig.getConfig().createSection(key));
275-
tree.save(classConfig.getConfig().createSection(key));
276-
cConfig.save();
277-
Logger.log(LogType.REGISTRATION, 2, "Loaded the dynamic class: " + key);
278-
} else {
279-
Logger.invalid("Duplicate class detected: " + key);
262+
DynamicClass tree = new DynamicClass(api, key);
263+
tree.load(classConfig.getConfig().getSection(key));
264+
if (!Fabled.isClassRegistered(tree.getName())) {
265+
api.addDynamicClass(tree);
266+
String path = getPath(Fabled.inst().getDataFolder() + File.separator + CLASS_DIR, key);
267+
if (path == null) path = key;
268+
queueDynamicClassSave(tree, path, key);
269+
Logger.log(LogType.REGISTRATION, 2, "Loaded the dynamic class: " + key);
270+
} else {
271+
Logger.invalid("Duplicate class detected: " + key);
280272
}
281273
} catch (Exception ex) {
282274
Logger.invalid("Failed to load class \"" + key + "\"");
@@ -304,10 +296,7 @@ public void initialize() {
304296
tree.load(section);
305297
if (!Fabled.isClassRegistered(tree.getName())) {
306298
api.addDynamicClass(tree);
307-
cConfig.clear();
308-
tree.save(cConfig.getConfig().createSection(name));
309-
tree.save(classConfig.getConfig().createSection(name));
310-
cConfig.save();
299+
queueDynamicClassSave(tree, longName, name);
311300
Logger.log(LogType.REGISTRATION, 2, "Loaded the dynamic class: " + name);
312301
} else if (Fabled.getClass(name) instanceof DynamicClass) {
313302
Logger.log(LogType.REGISTRATION, 3, name + " is already loaded, skipping it");
@@ -338,6 +327,68 @@ public void initialize() {
338327
Logger.log(LogType.REGISTRATION, 0, " - " + Fabled.getSkills().size() + " skills");
339328
Logger.log(LogType.REGISTRATION, 0, " - " + Fabled.getClasses().size() + " classes");
340329
Logger.log(LogType.REGISTRATION, 0, " Took " + (System.currentTimeMillis() - start) + "ms");
330+
Bukkit.getScheduler().runTask(api, this::flushDeferredSaves);
331+
}
332+
333+
private void queueDeferredSave(String description, Runnable saveTask) {
334+
deferredSaves.add(new DeferredSave(description, saveTask));
335+
}
336+
337+
private void flushDeferredSaves() {
338+
if (deferredSaves.isEmpty()) return;
339+
340+
List<DeferredSave> savesToRun = new ArrayList<>(deferredSaves);
341+
deferredSaves.clear();
342+
343+
for (DeferredSave deferredSave : savesToRun) {
344+
try {
345+
deferredSave.saveTask.run();
346+
} catch (Exception ex) {
347+
Logger.bug("Failed to save deferred " + deferredSave.description);
348+
ex.printStackTrace();
349+
}
350+
}
351+
352+
skillConfig.save();
353+
classConfig.save();
354+
}
355+
356+
private void queueSkillFileSave(Skill skill) {
357+
String skillName = skill.getName();
358+
queueDeferredSave("skill \"" + skillName + "\"", () -> {
359+
CommentedConfig singleFile = new CommentedConfig(api, "skill" + File.separator + skillName);
360+
skill.save(singleFile.getConfig());
361+
singleFile.save();
362+
});
363+
}
364+
365+
private void queueClassFileSave(FabledClass fabledClass) {
366+
String className = fabledClass.getName();
367+
queueDeferredSave("class \"" + className + "\"", () -> {
368+
CommentedConfig singleFile = new CommentedConfig(api, "class" + File.separator + className);
369+
fabledClass.save(singleFile.getConfig());
370+
singleFile.save();
371+
});
372+
}
373+
374+
private void queueDynamicSkillSave(DynamicSkill skill, String path, String key) {
375+
queueDeferredSave("dynamic skill \"" + key + "\"", () -> {
376+
CommentedConfig singleFile = new CommentedConfig(api, SKILL_DIR + path);
377+
singleFile.clear();
378+
skill.save(singleFile.getConfig().createSection(key));
379+
skill.save(skillConfig.getConfig().createSection(key));
380+
singleFile.save();
381+
});
382+
}
383+
384+
private void queueDynamicClassSave(DynamicClass tree, String path, String key) {
385+
queueDeferredSave("dynamic class \"" + key + "\"", () -> {
386+
CommentedConfig singleFile = new CommentedConfig(api, CLASS_DIR + path);
387+
singleFile.clear();
388+
tree.save(singleFile.getConfig().createSection(key));
389+
tree.save(classConfig.getConfig().createSection(key));
390+
singleFile.save();
391+
});
341392
}
342393

343394
private String getQualifiedFileName(Path root, Path path) {
@@ -405,15 +456,10 @@ else if (Fabled.isSkillRegistered(skill.getName())) {
405456
DataSection config = singleFile.getConfig();
406457

407458
try {
408-
// Soft save to ensure optional data starts off in the config
409-
skill.softSave(config);
410-
411-
// Load the config data to apply any previous data
412-
skill.load(config);
413-
414-
// Finally, do a full save to make sure the config is up to date
415-
skill.save(config);
416-
singleFile.save();
459+
if (!config.keys().isEmpty()) {
460+
skill.load(config);
461+
}
462+
queueSkillFileSave(skill);
417463

418464
if (skill instanceof Listener) {
419465
Bukkit.getServer().getPluginManager().registerEvents((Listener) skill, api);
@@ -463,16 +509,10 @@ else if (Fabled.isClassRegistered(fabledClass.getName())) {
463509
DataSection config = singleFile.getConfig();
464510

465511
try {
466-
467-
// Soft save to ensure optional data starts off in the config
468-
fabledClass.softSave(config);
469-
470-
// Load the config data to apply any previous data
471-
fabledClass.load(config);
472-
473-
// Finally, do a full save to make sure the config is up to date
474-
fabledClass.save(config);
475-
singleFile.save();
512+
if (!config.keys().isEmpty()) {
513+
fabledClass.load(config);
514+
}
515+
queueClassFileSave(fabledClass);
476516

477517
// Skill is ready to be registered
478518
return fabledClass;
@@ -504,4 +544,14 @@ public enum Mode {
504544
DYNAMIC,
505545
DONE
506546
}
547+
548+
private static class DeferredSave {
549+
private final String description;
550+
private final Runnable saveTask;
551+
552+
private DeferredSave(String description, Runnable saveTask) {
553+
this.description = description;
554+
this.saveTask = saveTask;
555+
}
556+
}
507557
}

0 commit comments

Comments
 (0)