Update the DependencyHandler to support circular references

This commit is contained in:
James Seibel
2022-03-05 17:34:19 -06:00
parent 2149da59df
commit 58392a8ac1
20 changed files with 208 additions and 60 deletions
@@ -20,6 +20,7 @@
package com.seibel.lod.core.handlers;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* A singleton used to get variables from methods
@@ -34,9 +35,9 @@ import com.seibel.lod.core.enums.rendering.FogDrawMode;
* different MC versions.
*
* @author James Seibel
* @version 12-14-2021
* @version 3-5-2022
*/
public interface IReflectionHandler
public interface IReflectionHandler extends IBindable
{
/** @returns Whether Optifine is set to render fog or not. */
FogDrawMode getFogDrawMode();
@@ -23,15 +23,15 @@ import java.util.HashMap;
import java.util.Map;
/**
* This class takes care of dependency injection
* for dependencies.
* This class takes care of tracking objects used in dependency injection.
*
* @author James Seibel
* @version 3-1-2022
* @version 3-4-2022
*/
public class DependencyHandler
{
private static final Map<Class<?>, Object> dependencies = new HashMap<Class<?>, Object>();
private final Map<Class<?>, Object> dependencies = new HashMap<Class<?>, Object>();
private boolean bindingFinished = false;
/**
@@ -44,6 +44,13 @@ public class DependencyHandler
*/
public void bind(Class<?> depenencyInterface, Object dependencyImplementation) throws IllegalStateException
{
// only allow binding before the finishBinding method is called
if (bindingFinished)
{
throw new IllegalStateException("The dependency [" + depenencyInterface.getSimpleName() + "] cannot be bound, Binding is finished for [" + this.getClass().getSimpleName() + "]. Make sure your bindings are happening before the [bindingFinished] method is being called.");
}
// make sure we haven't already bound this dependency
if (dependencies.containsKey(depenencyInterface))
{
@@ -51,24 +58,60 @@ public class DependencyHandler
}
// make sure the given dependency implements the interface
boolean dependencyImplementsInterface = false;
for (Class<?> implementationInterface : dependencyImplementation.getClass().getInterfaces())
{
if (implementationInterface.equals(depenencyInterface))
{
dependencyImplementsInterface = true;
break;
}
}
if (!dependencyImplementsInterface)
// make sure the given dependency implements the necessary interfaces
boolean implementsInterface = checkIfClassImplements(dependencyImplementation.getClass(), depenencyInterface);
boolean implementsBindable = checkIfClassImplements(dependencyImplementation.getClass(), IBindable.class);
// display any errors
if (!implementsInterface)
{
throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface [" + depenencyInterface.getSimpleName() + "].");
}
if (!implementsBindable)
{
throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface [" + IBindable.class.getSimpleName() + "].");
}
dependencies.put(depenencyInterface, dependencyImplementation);
}
/**
* Checks if classToTest (or one of its ancestors)
* implements the given interface.
*/
private boolean checkIfClassImplements(Class<?> classToTest, Class<?> interfaceToLookFor)
{
// check the parent class (if applicable)
if (classToTest.getSuperclass() != Object.class && classToTest.getSuperclass() != null)
{
if (checkIfClassImplements(classToTest.getSuperclass(), interfaceToLookFor))
{
return true;
}
}
// check interfaces
for (Class<?> implementationInterface : classToTest.getInterfaces())
{
// recurse to check interface parents if necessary
if (implementationInterface.getInterfaces().length != 0)
{
if (checkIfClassImplements(implementationInterface, interfaceToLookFor))
{
return true;
}
}
if (implementationInterface.equals(interfaceToLookFor))
{
return true;
}
}
return false;
}
/**
* Returns a dependency of type T if one has been bound.
@@ -82,9 +125,40 @@ public class DependencyHandler
* (this shouldn't normally happen, unless the bound object changed somehow)
*/
@SuppressWarnings("unchecked")
public <T> T get(Class<T> interfaceClass) throws ClassCastException
public <T extends IBindable> T get(Class<?> interfaceClass) throws ClassCastException
{
// getting dependencies should only happen after everything has been bound
if (!bindingFinished)
{
throw new IllegalStateException("Binding hasn't been finished for [" + this.getClass().getSimpleName() + "]. Make sure you are calling the [bindingFinished] method before calling [get].");
}
return (T) dependencies.get(interfaceClass);
}
/**
* Should only be called after all Binds have been done.
* Calls the delayedSetup method for each dependency. <br> <br>
*
* This is done so we can have circular dependencies.
*/
public void finishBinding()
{
// (yes technically the binding isn't finished,
// but this needs to be set to "true" so we can use "get")
bindingFinished = true;
for (Class<?> interfaceKey : dependencies.keySet())
{
IBindable concreteObject = get(interfaceKey);
concreteObject.finishDelayedSetup();
}
}
/** returns whether the finishBinding method has been called */
public boolean getBindingFinished()
{
return bindingFinished;
}
}
@@ -0,0 +1,25 @@
package com.seibel.lod.core.handlers.dependencyInjection;
/**
* Necessary for all singletons that can be dependency injected.
*
* @author James Seibel
* @version 3-4-2022
*/
public interface IBindable
{
/**
* Finish initializing this object. <br> <br>
*
* Generally this should just used for getting other objects through
* dependency injection and is specifically designed to allow
* for circular references. <br><br>
*
* If no circular dependencies are required this method
* doesn't have to be implemented.
*/
public default void finishDelayedSetup()
{
}
}
@@ -70,4 +70,22 @@ public class ModAccessorHandler
return dependencyHandler.get(objectClass);
}
/**
* Should only be called after all Binds have been done.
* Calls the delayedSetup method for each dependency. <br> <br>
*
* This is done so we can have circular dependencies.
*/
public static void finishBinding()
{
dependencyHandler.finishBinding();
}
/** returns whether the finishBinding method has been called */
public static boolean bindingFinished()
{
return dependencyHandler.getBindingFinished();
}
}
@@ -24,7 +24,7 @@ package com.seibel.lod.core.handlers.dependencyInjection;
* for singletons.
*
* @author James Seibel
* @version 3-1-2022
* @version 3-5-2022
*/
public class SingletonHandler
{
@@ -69,4 +69,22 @@ public class SingletonHandler
return foundObject;
}
/**
* Should only be called after all Binds have been done.
* Calls the delayedSetup method for each dependency. <br> <br>
*
* This is done so we can have circular dependencies.
*/
public static void finishBinding()
{
dependencyHandler.finishBinding();
}
/** returns whether the finishBinding method has been called */
public static boolean getBindingFinished()
{
return dependencyHandler.getBindingFinished();
}
}
@@ -1,6 +1,7 @@
package com.seibel.lod.core.wrapperInterfaces;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* A singleton that contains variables specific to each version of Minecraft
@@ -8,9 +9,9 @@ import com.seibel.lod.core.enums.config.DistanceGenerationMode;
* blocks can be negative, which changes how we generate LODs.
*
* @author James Seibel
* @version 12-11-2021
* @version 3-5-2022
*/
public interface IVersionConstants {
public interface IVersionConstants extends IBindable {
/** @returns the minimum height blocks can be generated */
int getMinimumWorldHeight();
@@ -20,33 +20,29 @@
package com.seibel.lod.core.wrapperInterfaces;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractExperimentalWorldGeneratorWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
/**
* This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 12-14-2021
* @version 3-5-2022
*/
public interface IWrapperFactory {
public interface IWrapperFactory extends IBindable
{
AbstractBlockPosWrapper createBlockPos();
AbstractBlockPosWrapper createBlockPos(int x, int y, int z);
AbstractChunkPosWrapper createChunkPos();
AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined);
AbstractChunkPosWrapper createChunkPos(int x, int z);
AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos);
AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos);
AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension,
@@ -19,20 +19,15 @@
package com.seibel.lod.core.wrapperInterfaces.chunk;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
import com.seibel.lod.core.wrapperInterfaces.block.BlockDetail;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
/**
* @author James Seibel
* @version 11-17-2021
*/
public interface IChunkWrapper
public interface IChunkWrapper extends IBindable
{
default int getHeight() {
return getMaxBuildHeight()-getMinBuildHeight();
@@ -34,6 +34,7 @@ import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.enums.rendering.FogColorMode;
import com.seibel.lod.core.enums.rendering.FogDistance;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.MinDefaultMax;
import com.seibel.lod.core.util.LodUtil;
@@ -46,9 +47,9 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
* the options that should be implemented in a configWrapperSingleton.
*
* @author James Seibel
* @version 12-14-2021
* @version 3-5-2022
*/
public interface ILodConfigWrapperSingleton
public interface ILodConfigWrapperSingleton extends IBindable
{
IClient client();
@@ -22,6 +22,7 @@ package com.seibel.lod.core.wrapperInterfaces.minecraft;
import java.awt.Color;
import java.util.HashSet;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.math.Mat4f;
@@ -38,12 +39,10 @@ import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
* rendering in Minecraft.
*
* @author James Seibel
* @version 12-14-2021
* @version 3-5-2022
*/
public interface IMinecraftRenderWrapper
public interface IMinecraftRenderWrapper extends IBindable
{
static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class);
Vec3f getLookAtVector();
AbstractBlockPosWrapper getCameraBlockPosition();
@@ -92,6 +91,7 @@ public interface IMinecraftRenderWrapper
{
IMinecraftWrapper mcWrapper = SingletonHandler.get(IMinecraftWrapper.class);
IWrapperFactory factory = SingletonHandler.get(IWrapperFactory.class);
IVersionConstants versionConstants = SingletonHandler.get(IVersionConstants.class);
int chunkDist = this.getRenderDistance();
@@ -106,8 +106,8 @@ public interface IMinecraftRenderWrapper
{
for(int deltaChunkZ = -chunkDist; deltaChunkZ <= chunkDist; deltaChunkZ++)
{
if (!VERSION_CONSTANTS.isVanillaRenderedChunkSquare() &&
deltaChunkX*deltaChunkX+deltaChunkZ*deltaChunkZ>chunkDist2) {
if (!versionConstants.isVanillaRenderedChunkSquare() &&
deltaChunkX*deltaChunkX+deltaChunkZ*deltaChunkZ > chunkDist2) {
continue;
}
renderedPos.add(factory.createChunkPos(centerChunkX + deltaChunkX, centerChunkZ + deltaChunkZ));
@@ -24,6 +24,7 @@ import java.io.File;
import java.util.ArrayList;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
@@ -36,7 +37,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
* @author James Seibel
* @version 12-8-2021
*/
public interface IMinecraftWrapper
public interface IMinecraftWrapper extends IBindable
{
//================//
// helper methods //
@@ -19,11 +19,13 @@
package com.seibel.lod.core.wrapperInterfaces.minecraft;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* @author James Seibel
* @version 11-20-2021
*/
public interface IProfilerWrapper
public interface IProfilerWrapper extends IBindable
{
void push(String newSection);
@@ -19,11 +19,13 @@
package com.seibel.lod.core.wrapperInterfaces.misc;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* @author James Seibel
* @version 11-20-2021
* @version 3-5-2022
*/
public interface ILightMapWrapper
public interface ILightMapWrapper extends IBindable
{
int getLightValue(int skyLight, int blockLight);
}
@@ -1,5 +1,11 @@
package com.seibel.lod.core.wrapperInterfaces.modAccessor;
public interface IModAccessor {
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* @author Leetom
* @version 3-5-2022
*/
public interface IModAccessor extends IBindable {
String getModName();
}
@@ -1,12 +1,14 @@
package com.seibel.lod.core.wrapperInterfaces.modAccessor;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* Checks if a mod is loaded
*
* @author coolGi2007
* @version 1-11-2022
* @version 3-5-2022
*/
public interface IModChecker {
public interface IModChecker extends IBindable {
/** Checks if a mod is loaded */
boolean isModLoaded(String modid);
}
@@ -7,6 +7,6 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
public interface IOptifineAccessor extends IModAccessor
{
// Can be null
/** Can be null */
HashSet<AbstractChunkPosWrapper> getNormalRenderedChunks();
}
@@ -19,6 +19,7 @@
package com.seibel.lod.core.wrapperInterfaces.world;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
@@ -26,9 +27,9 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
* Contains everything related to biome colors.
*
* @author James Seibel
* @version 11-15-2021
* @version 3-5-2022
*/
public interface IBiomeColorWrapperSingleton
public interface IBiomeColorWrapperSingleton extends IBindable
{
IBiomeColorWrapperSingleton getInstance();
@@ -19,11 +19,13 @@
package com.seibel.lod.core.wrapperInterfaces.world;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* @author James Seibel
* @version 11-15-2021
* @version 3-5-2022
*/
public interface IBiomeWrapper
public interface IBiomeWrapper extends IBindable
{
/** Returns a color int for the given biome. */
int getColorForBiome(int x, int z);
@@ -19,11 +19,13 @@
package com.seibel.lod.core.wrapperInterfaces.world;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
/**
* @author James Seibel
* @version 11-15-2021
* @version 3-5-2022
*/
public interface IDimensionTypeWrapper
public interface IDimensionTypeWrapper extends IBindable
{
String getDimensionName();
@@ -22,6 +22,7 @@ package com.seibel.lod.core.wrapperInterfaces.world;
import java.io.File;
import com.seibel.lod.core.enums.WorldType;
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -29,9 +30,9 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
* Can be either a Server world or a Client world.
*
* @author James Seibel
* @version 11-20-2021
* @version 3-5-2022
*/
public interface IWorldWrapper
public interface IWorldWrapper extends IBindable
{
IDimensionTypeWrapper getDimensionType();