From d792031c5781b05026fd2f5dd337ad1823728e62 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 10 Dec 2022 11:17:50 -0600 Subject: [PATCH] Use bound API World Generators --- .../override/IDhApiOverrideable.java | 2 +- .../IDhApiWorldGeneratorOverrideRegister.java | 12 +- .../DhApiWorldGeneratorOverrideRegister.java | 16 +- .../WorldGeneratorInjector.java | 114 +++++-------- .../lod/core/generation/BatchGenerator.java | 47 ++++-- .../lod/core/level/DhClientServerLevel.java | 13 +- .../java/tests/DependencyInjectorTest.java | 151 ++++++------------ 7 files changed, 141 insertions(+), 214 deletions(-) diff --git a/api/src/main/java/com/seibel/lod/api/interfaces/override/IDhApiOverrideable.java b/api/src/main/java/com/seibel/lod/api/interfaces/override/IDhApiOverrideable.java index 083053a18..56fd42ca3 100644 --- a/api/src/main/java/com/seibel/lod/api/interfaces/override/IDhApiOverrideable.java +++ b/api/src/main/java/com/seibel/lod/api/interfaces/override/IDhApiOverrideable.java @@ -12,7 +12,7 @@ import com.seibel.lod.core.interfaces.dependencyInjection.IOverrideInjector; public interface IDhApiOverrideable extends IBindable { /** - * Returns when this Override should be used.
+ * Higher (larger numerical) priorities override lower (smaller numerical) priorities .
* For most developers this can be left at the default. */ default int getPriority() { return IOverrideInjector.DEFAULT_NON_CORE_OVERRIDE_PRIORITY; } diff --git a/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGeneratorOverrideRegister.java b/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGeneratorOverrideRegister.java index 6e56212ed..d22a6fd67 100644 --- a/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGeneratorOverrideRegister.java +++ b/api/src/main/java/com/seibel/lod/api/interfaces/override/worldGenerator/IDhApiWorldGeneratorOverrideRegister.java @@ -7,20 +7,10 @@ import com.seibel.lod.api.objects.DhApiResult; * Handles adding world generator overrides. * * @author James Seibel - * @version 2022-9-16 + * @version 2022-12-10 */ public interface IDhApiWorldGeneratorOverrideRegister { - /** - * Registers the given world generator.

- * - * This registers a backup world generator for all levels and will be overridden if there - * is a world generator for the specific level.
- * If another world generator has already been registered, DhApiResult will return - * the name of the previously registered generator and success = false. - */ - DhApiResult registerWorldGeneratorOverride(IDhApiWorldGenerator worldGenerator); - /** * Registers the given world generator for the given level.

* diff --git a/api/src/main/java/com/seibel/lod/api/methods/override/DhApiWorldGeneratorOverrideRegister.java b/api/src/main/java/com/seibel/lod/api/methods/override/DhApiWorldGeneratorOverrideRegister.java index 2d45f7aa9..b823251bb 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/override/DhApiWorldGeneratorOverrideRegister.java +++ b/api/src/main/java/com/seibel/lod/api/methods/override/DhApiWorldGeneratorOverrideRegister.java @@ -10,7 +10,7 @@ import com.seibel.lod.core.DependencyInjection.WorldGeneratorInjector; * Handles adding world generator overrides. * * @author James Seibel - * @version 2022-9-16 + * @version 2022-12-10 */ public class DhApiWorldGeneratorOverrideRegister implements IDhApiWorldGeneratorOverrideRegister { @@ -20,20 +20,6 @@ public class DhApiWorldGeneratorOverrideRegister implements IDhApiWorldGenerator - @Override - public DhApiResult registerWorldGeneratorOverride(IDhApiWorldGenerator worldGenerator) - { - try - { - WorldGeneratorInjector.INSTANCE.bind(worldGenerator); - return DhApiResult.createSuccess(); - } - catch (Exception e) - { - return DhApiResult.createFail(e.getMessage()); - } - } - @Override public DhApiResult registerWorldGeneratorOverride(IDhApiLevelWrapper levelWrapper, IDhApiWorldGenerator worldGenerator) { diff --git a/api/src/main/java/com/seibel/lod/core/DependencyInjection/WorldGeneratorInjector.java b/api/src/main/java/com/seibel/lod/core/DependencyInjection/WorldGeneratorInjector.java index e39a875f4..62f31130c 100644 --- a/api/src/main/java/com/seibel/lod/core/DependencyInjection/WorldGeneratorInjector.java +++ b/api/src/main/java/com/seibel/lod/core/DependencyInjection/WorldGeneratorInjector.java @@ -29,102 +29,77 @@ import java.util.HashMap; /** * This class takes care of dependency injection for world generators.
* This is done so other mods can override our world generator(s) to improve or replace them. - * * @author James Seibel - * @version 2022-11-25 + * @version 2022-12-10 */ public class WorldGeneratorInjector { public static final WorldGeneratorInjector INSTANCE = new WorldGeneratorInjector(); - + private final HashMap worldGeneratorByLevelWrapper = new HashMap<>(); - /** World generators that aren't bound to a specific level and are used if no other world generators are bound. */ - private final OverrideInjector backupUniversalWorldGenerators; - + /** * This is used to determine if an override is part of Distant Horizons' * Core or not. * This probably isn't the best way of going about this, but it works for now. */ private final String corePackagePath; - - - + + + public WorldGeneratorInjector() { String thisPackageName = this.getClass().getPackage().getName(); int secondPackageEndingIndex = StringUtil.nthIndexOf(thisPackageName, ".", 3); this.corePackagePath = thisPackageName.substring(0, secondPackageEndingIndex); // this should be "com.seibel.lod" - - this.backupUniversalWorldGenerators = new OverrideInjector(this.corePackagePath); } - + /** This constructor should only be used for testing different corePackagePaths. */ public WorldGeneratorInjector(String newCorePackagePath) { this.corePackagePath = newCorePackagePath; - - this.backupUniversalWorldGenerators = new OverrideInjector(this.corePackagePath); } - - - + + + /** - * Binds the backup world generator.
+ * Binds a world generator to the given level.
* See {@link DependencyInjector#bind(Class, IBindable) bind(Class, IBindable)} for full documentation. - * + * + * @throws NullPointerException if any parameter is null * @throws IllegalArgumentException if a non-Distant Horizons world generator with the priority CORE is passed in + * * @see DependencyInjector#bind(Class, IBindable) */ - public void bind(IDhApiWorldGenerator worldGeneratorImplementation) throws IllegalStateException, IllegalArgumentException + public void bind(IDhApiLevelWrapper levelForWorldGenerator, IDhApiWorldGenerator worldGeneratorImplementation) throws NullPointerException, IllegalArgumentException { - this.bind(null, worldGeneratorImplementation); - } - - /** - * Binds the world generator to the given level.
- * See {@link DependencyInjector#bind(Class, IBindable) bind(Class, IBindable)} for full documentation. - * - * @throws IllegalArgumentException if a non-Distant Horizons world generator with the priority CORE is passed in - * @see DependencyInjector#bind(Class, IBindable) - */ - public void bind(IDhApiLevelWrapper levelForWorldGenerator, IDhApiWorldGenerator worldGeneratorImplementation) throws IllegalStateException, IllegalArgumentException - { - if (levelForWorldGenerator != null) + // validate inputs + if (levelForWorldGenerator == null) { - // bind this generator to a specific level - if (!this.worldGeneratorByLevelWrapper.containsKey(levelForWorldGenerator)) - { - this.worldGeneratorByLevelWrapper.put(levelForWorldGenerator, new OverrideInjector(this.corePackagePath)); - } - - this.worldGeneratorByLevelWrapper.get(levelForWorldGenerator).bind(IDhApiWorldGenerator.class, worldGeneratorImplementation); + throw new NullPointerException("A [" + IDhApiLevelWrapper.class.getSimpleName() + "] is required when binding a world generator."); } - else + + if (worldGeneratorImplementation == null) { - // a null level wrapper binds the generator to all levels - this.backupUniversalWorldGenerators.bind(IDhApiWorldGenerator.class, worldGeneratorImplementation); + throw new NullPointerException("No [" + IDhApiWorldGenerator.class.getSimpleName() + "] given."); } + + + // bind this generator to the given level + if (!this.worldGeneratorByLevelWrapper.containsKey(levelForWorldGenerator)) + { + this.worldGeneratorByLevelWrapper.put(levelForWorldGenerator, new OverrideInjector(this.corePackagePath)); + } + this.worldGeneratorByLevelWrapper.get(levelForWorldGenerator).bind(IDhApiWorldGenerator.class, worldGeneratorImplementation); } - - - - /** - * Returns the backup world generator with the highest priority.
- * See {@link OverrideInjector#get(Class) get(Class)} for more documentation. - * - * @see OverrideInjector#get(Class) - */ - public IDhApiWorldGenerator get() throws ClassCastException - { - return this.backupUniversalWorldGenerators.get(IDhApiWorldGenerator.class); - } - + + + /** * Returns the bound world generator with the highest priority.
- * (Returns a backup world generator if no world generators have been bound for this specific level.)
- * See {@link OverrideInjector#get(Class) get(Class)} for more documentation. - * + * Returns null if no world generators have been bound for this specific level.

+ * + * See {@link OverrideInjector#get(Class) get(Class)} for full documentation. * @see OverrideInjector#get(Class) */ public IDhApiWorldGenerator get(IDhApiLevelWrapper levelForWorldGenerator) throws ClassCastException @@ -132,21 +107,18 @@ public class WorldGeneratorInjector if (!this.worldGeneratorByLevelWrapper.containsKey(levelForWorldGenerator)) { // no generator exists for this specific level. - // check for a backup universal world generator - return this.backupUniversalWorldGenerators.get(IDhApiWorldGenerator.class); + return null; } - + // use the existing world generator return this.worldGeneratorByLevelWrapper.get(levelForWorldGenerator).get(IDhApiWorldGenerator.class); } - - - + + + /** Removes all bound world generators. */ - public void clear() - { - this.worldGeneratorByLevelWrapper.clear(); - this.backupUniversalWorldGenerators.clear(); - } + public void clear() { this.worldGeneratorByLevelWrapper.clear(); } + + } diff --git a/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java b/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java index 724869ecf..007212e61 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java +++ b/core/src/main/java/com/seibel/lod/core/generation/BatchGenerator.java @@ -21,6 +21,7 @@ package com.seibel.lod.core.generation; import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenThreadMode; import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGenerator; +import com.seibel.lod.core.interfaces.dependencyInjection.IOverrideInjector; import com.seibel.lod.core.level.IDhLevel; import com.seibel.lod.core.config.Config; import com.seibel.lod.api.enums.config.EDistanceGenerationMode; @@ -39,7 +40,7 @@ import java.util.function.Consumer; /** * @author Leetom - * @version 2022-11-25 + * @version 2022-12-10 */ public class BatchGenerator implements IDhApiWorldGenerator { @@ -61,6 +62,15 @@ public class BatchGenerator implements IDhApiWorldGenerator + //=====================// + // override parameters // + //=====================// + + @Override + public int getPriority() { return IOverrideInjector.CORE_PRIORITY; } + + + //======================// // generator parameters // //======================// @@ -85,20 +95,6 @@ public class BatchGenerator implements IDhApiWorldGenerator // generator methods // //===================// - @Override - public void close() { this.stop(true); } - public void stop(boolean blocking) - { - LOGGER.info("Batch Chunk Generator shutting down..."); - this.generationGroup.stop(blocking); - } - - @Override - public boolean isBusy() - { - return this.generationGroup.getEventCount() > Math.max(Config.Client.Advanced.Threading.numberOfWorldGenerationThreads.get().intValue(), 1) * 1.5; - } - @Override public CompletableFuture generateChunks(int chunkPosMinX, int chunkPosMinZ, byte granularity, byte targetDataDetail, Consumer resultConsumer) { @@ -138,4 +134,25 @@ public class BatchGenerator implements IDhApiWorldGenerator @Override public void preGeneratorTaskStart() { this.generationGroup.updateAllFutures(); } + @Override + public boolean isBusy() + { + return this.generationGroup.getEventCount() > Math.max(Config.Client.Advanced.Threading.numberOfWorldGenerationThreads.get().intValue(), 1) * 1.5; + } + + + + //=========// + // cleanup // + //=========// + + @Override + public void close() { this.stop(true); } + public void stop(boolean blocking) + { + LOGGER.info("Batch Chunk Generator shutting down..."); + this.generationGroup.stop(blocking); + } + + } diff --git a/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java index 04e3f6beb..adf36e468 100644 --- a/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java @@ -1,7 +1,6 @@ package com.seibel.lod.core.level; import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGenerator; -import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.lod.core.DependencyInjection.WorldGeneratorInjector; import com.seibel.lod.core.config.AppliedConfigState; import com.seibel.lod.core.datatype.full.ChunkSizedData; @@ -372,7 +371,17 @@ public class DhClientServerLevel implements IDhClientLevel, IDhServerLevel WorldGenState(IDhLevel level) { - this.chunkGenerator = new BatchGenerator(level); // WorldGeneratorInjector.INSTANCE.get(level); // + IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper()); + if (worldGenerator == null) + { + // no override generator is bound, use the Core world generator + worldGenerator = new BatchGenerator(level); + // binding the core generator won't prevent other mods from binding their own generators + // since core world generator's should have the lowest override priority + WorldGeneratorInjector.INSTANCE.bind(level.getLevelWrapper(), worldGenerator); + } + this.chunkGenerator = worldGenerator; + this.worldGenerationQueue = new WorldGenerationQueue(this.chunkGenerator); dataFileHandler.setGenerationQueue(this.worldGenerationQueue); } diff --git a/core/src/test/java/tests/DependencyInjectorTest.java b/core/src/test/java/tests/DependencyInjectorTest.java index a60c40102..6191fd056 100644 --- a/core/src/test/java/tests/DependencyInjectorTest.java +++ b/core/src/test/java/tests/DependencyInjectorTest.java @@ -24,7 +24,7 @@ import testItems.worldGeneratorInjection.objects.*; /** * @author James Seibel - * @version 2022-9-11 + * @version 2022-12-10 */ public class DependencyInjectorTest { @@ -154,114 +154,67 @@ public class DependencyInjectorTest } @Test - public void testBackupWorldGeneratorInjection() + public void testWorldGeneratorInjection() { WorldGeneratorInjector TEST_INJECTOR = new WorldGeneratorInjector(WorldGeneratorTestAssembly.getPackagePath(2)); - WorldGeneratorInjector CORE_INJECTOR = new WorldGeneratorInjector(); - - - // pre-dependency setup - Assert.assertNull("Nothing should have been bound.", TEST_INJECTOR.get()); - Assert.assertNull("Nothing should have been bound.", CORE_INJECTOR.get()); - - + + // variables to use later IDhApiWorldGenerator generator; - WorldGeneratorTestCore coreGenerator = new WorldGeneratorTestCore(); - WorldGeneratorTestSecondary secondaryGenerator = new WorldGeneratorTestSecondary(); - WorldGeneratorTestPrimary primaryGenerator = new WorldGeneratorTestPrimary(); - - - // core generator binding - try { TEST_INJECTOR.bind(coreGenerator); } catch (IllegalArgumentException e) { Assert.fail("Core generator should be bindable for test package injector."); } - - try - { - CORE_INJECTOR.bind(coreGenerator); - Assert.fail("Core generator should not be bindable for core package injector."); - } - catch (IllegalArgumentException e) { /* this exception should be thrown */ } - - - // core override - Assert.assertNotNull("Test injector should've bound core override.", TEST_INJECTOR.get()); - Assert.assertNull("Core injector should not have bound core override.", CORE_INJECTOR.get()); - // standard get - generator = TEST_INJECTOR.get(); - Assert.assertEquals("Override returned incorrect override type.", generator.getPriority(), OverrideInjector.CORE_PRIORITY); - Assert.assertEquals("Incorrect generator returned.", generator.getThreadingMode(), WorldGeneratorTestCore.THREAD_MODE); - - - // secondary override - TEST_INJECTOR.bind(secondaryGenerator); - // priority gets - generator = TEST_INJECTOR.get(); - Assert.assertEquals("Override returned incorrect override type.", generator.getPriority(), WorldGeneratorTestSecondary.PRIORITY); - Assert.assertEquals("Incorrect override object returned.", generator.getThreadingMode(), WorldGeneratorTestSecondary.THREAD_MODE); - - - // primary override - TEST_INJECTOR.bind(primaryGenerator); - // priority gets - generator = TEST_INJECTOR.get(); - Assert.assertEquals("Override returned incorrect override type.", generator.getPriority(), WorldGeneratorTestPrimary.PRIORITY); - Assert.assertEquals("Incorrect override object returned.", generator.getThreadingMode(), WorldGeneratorTestPrimary.THREAD_MODE); - - - - // in-line get - // (make sure the returned type is correct and compiles, the actual value doesn't matter) - EDhApiWorldGenThreadMode threadMode = TEST_INJECTOR.get().getThreadingMode(); - - } - - @Test - public void testSpecificLevelWorldGeneratorInjection() - { - WorldGeneratorInjector TEST_INJECTOR = new WorldGeneratorInjector(WorldGeneratorTestAssembly.getPackagePath(2)); - - - // pre-dependency setup - Assert.assertNull("Nothing should have been bound.", TEST_INJECTOR.get()); - - - // variables to use later - IDhApiWorldGenerator generator; - WorldGeneratorTestCore backupGenerator = new WorldGeneratorTestCore(); - WorldGeneratorTestPrimary levelGenerator = new WorldGeneratorTestPrimary(); - + WorldGeneratorTestCore coreLevelGenerator = new WorldGeneratorTestCore(); + WorldGeneratorTestPrimary primaryLevelGenerator = new WorldGeneratorTestPrimary(); + WorldGeneratorTestSecondary secondaryLevelGenerator = new WorldGeneratorTestSecondary(); + IDhApiLevelWrapper boundLevel = new LevelWrapperTest(); IDhApiLevelWrapper unboundLevel = new LevelWrapperTest(); - - - - // backup generator binding - try { TEST_INJECTOR.bind(backupGenerator); } catch (IllegalArgumentException e) { Assert.fail("Core generator should be bindable for test package injector."); } - - - // get backup generator - generator = TEST_INJECTOR.get(); - Assert.assertNotNull("Backup generator not bound.", generator); - Assert.assertEquals("Incorrect backup generator bound.", generator.getPriority(), OverrideInjector.CORE_PRIORITY); - Assert.assertEquals("Incorrect backup generator bound.", generator.getThreadingMode(), WorldGeneratorTestCore.THREAD_MODE); - - - // bind level specific - try { TEST_INJECTOR.bind(boundLevel, levelGenerator); } catch (IllegalArgumentException e) { Assert.fail("Core generator should be bindable for test package injector."); } - - - // get bound level generator + + + // validate nothing has been bound yet + Assert.assertNull("Nothing should have been bound yet.", TEST_INJECTOR.get(boundLevel)); + + + + // bind the core generator // + try { TEST_INJECTOR.bind(boundLevel, coreLevelGenerator); } catch (IllegalArgumentException e) { Assert.fail("[" + coreLevelGenerator.getClass().getSimpleName() + "] should be bindable for test package injector."); } + + // validate the core generator was bound + generator = TEST_INJECTOR.get(boundLevel); + Assert.assertNotNull("Level generator not bound.", generator); + Assert.assertEquals("Incorrect level generator bound.", generator.getPriority(), WorldGeneratorTestCore.PRIORITY); + Assert.assertEquals("Incorrect level generator bound.", generator.getThreadingMode(), WorldGeneratorTestCore.THREAD_MODE); + + // unbound level should still return null + Assert.assertNull("Nothing should have been bound to this level.", TEST_INJECTOR.get(unboundLevel)); + + + + // bind the secondary generator // + try { TEST_INJECTOR.bind(boundLevel, secondaryLevelGenerator); } catch (IllegalArgumentException e) { Assert.fail("[" + secondaryLevelGenerator.getClass().getSimpleName() + "] should be bindable for test package injector."); } + + // validate the secondary generator overrides the core generator + generator = TEST_INJECTOR.get(boundLevel); + Assert.assertNotNull("Level generator not bound.", generator); + Assert.assertEquals("Incorrect level generator bound.", generator.getPriority(), WorldGeneratorTestSecondary.PRIORITY); + Assert.assertEquals("Incorrect level generator bound.", generator.getThreadingMode(), WorldGeneratorTestSecondary.THREAD_MODE); + + // the unbound level should still return null + Assert.assertNull("Nothing should have been bound to this level.", TEST_INJECTOR.get(unboundLevel)); + + + + // bind the primary generator // + try { TEST_INJECTOR.bind(boundLevel, primaryLevelGenerator); } catch (IllegalArgumentException e) { Assert.fail("[" + primaryLevelGenerator.getClass().getSimpleName() + "] should be bindable for test package injector."); } + + // validate the primary generator overrides both the core and secondary generator generator = TEST_INJECTOR.get(boundLevel); Assert.assertNotNull("Level generator not bound.", generator); Assert.assertEquals("Incorrect level generator bound.", generator.getPriority(), WorldGeneratorTestPrimary.PRIORITY); Assert.assertEquals("Incorrect level generator bound.", generator.getThreadingMode(), WorldGeneratorTestPrimary.THREAD_MODE); - - // get unbound level generator - generator = TEST_INJECTOR.get(unboundLevel); - Assert.assertNotNull("Backup level generator not bound.", generator); - Assert.assertEquals("Incorrect level generator bound.", generator.getPriority(), OverrideInjector.CORE_PRIORITY); - Assert.assertEquals("Incorrect level generator bound.", generator.getThreadingMode(), WorldGeneratorTestCore.THREAD_MODE); - + + // the unbound level should still return null + Assert.assertNull("Nothing should have been bound to this level.", TEST_INJECTOR.get(unboundLevel)); + + } }