4646import java .io .IOException ;
4747import java .nio .file .Files ;
4848import 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