diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java index ba576877d..c4b503282 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java @@ -1,15 +1,40 @@ package com.seibel.lod.api.methods.events.abstractEvents; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * @author James Seibel - * @version 2022-9-6 + * @version 2022-11-21 */ -public abstract class DhApiAfterDhInitEvent - implements IDhApiEvent +public abstract class DhApiAfterDhInitEvent implements IDhApiEvent { /** Fired after Distant Horizons finishes its initial setup on Minecraft startup. */ public abstract void afterDistantHorizonsInit(); + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final boolean fireEvent(Void ignoredParam) + { + this.afterDistantHorizonsInit(); + return false; + } + + private static boolean firstTimeSetupComplete = false; + public DhApiAfterDhInitEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiAfterDhInitEvent.class, new DhApiEventDefinition(false, true)); + } + } + @Override + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiAfterDhInitEvent.class); } + } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java index 700dd9065..371f0210e 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java @@ -2,14 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; import com.seibel.lod.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.lod.core.util.math.Mat4f; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * @author James Seibel - * @version 2022-9-6 + * @version 2022-11-21 */ -public abstract class DhApiAfterRenderEvent - implements IDhApiEvent +public abstract class DhApiAfterRenderEvent implements IDhApiEvent { /** Fired after Distant Horizons finishes rendering fake chunks. */ public abstract void afterRender(EventParam input); @@ -22,12 +22,21 @@ public abstract class DhApiAfterRenderEvent @Override public final boolean fireEvent(EventParam input) { - afterRender(input); + this.afterRender(input); return false; } + private static boolean firstTimeSetupComplete = false; + public DhApiAfterRenderEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiAfterRenderEvent.class, new DhApiEventDefinition(false, false)); + } + } @Override - public final boolean getCancelable() { return false; } + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiAfterRenderEvent.class); } //==================// diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java index c50f3fdc8..7ed87187d 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java @@ -3,13 +3,14 @@ package com.seibel.lod.api.methods.events.abstractEvents; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * @author James Seibel - * @version 2022-9-6 + * @version 2022-11-21 */ -public abstract class DhApiBeforeDhInitEvent - implements IDhApiEvent +public abstract class DhApiBeforeDhInitEvent implements IDhApiEvent { /** Fired before Distant Horizons starts its initial setup on Minecraft startup. */ public abstract void beforeDistantHorizonsInit(); @@ -22,11 +23,20 @@ public abstract class DhApiBeforeDhInitEvent @Override public final boolean fireEvent(Void ignoredParam) { - beforeDistantHorizonsInit(); + this.beforeDistantHorizonsInit(); return false; } + private static boolean firstTimeSetupComplete = false; + public DhApiBeforeDhInitEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiBeforeDhInitEvent.class, new DhApiEventDefinition(false, true)); + } + } @Override - public final boolean getCancelable() { return false; } + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiBeforeDhInitEvent.class); } } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java index 8bdb4698a..76df6697c 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java @@ -2,14 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; import com.seibel.lod.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.lod.core.util.math.Mat4f; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * @author James Seibel - * @version 2022-9-6 + * @version 2022-11-21 */ -public abstract class DhApiBeforeRenderEvent - implements IDhApiEvent +public abstract class DhApiBeforeRenderEvent implements IDhApiEvent { /** * Fired before Distant Horizons renders fake chunks. @@ -24,10 +24,19 @@ public abstract class DhApiBeforeRenderEvent //=========================// @Override - public final boolean fireEvent(EventParam input) { return beforeRender(input); } + public final boolean fireEvent(EventParam input) { return this.beforeRender(input); } + private static boolean firstTimeSetupComplete = false; + public DhApiBeforeRenderEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiBeforeRenderEvent.class, new DhApiEventDefinition(true, false)); + } + } @Override - public final boolean getCancelable() { return true; } + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiBeforeRenderEvent.class); } //==================// diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java index 549cb0303..7a55affa4 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java @@ -2,13 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents; import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * @author James Seibel - * @version 2022-9-10 + * @version 2022-11-21 */ -public abstract class DhApiLevelLoadEvent - implements IDhApiEvent +public abstract class DhApiLevelLoadEvent implements IDhApiEvent { /** Fired after Distant Horizons loads a new level. */ public abstract void onLevelLoad(EventParam input); @@ -21,12 +22,21 @@ public abstract class DhApiLevelLoadEvent @Override public final boolean fireEvent(EventParam input) { - onLevelLoad(input); + this.onLevelLoad(input); return false; } + private static boolean firstTimeSetupComplete = false; + public DhApiLevelLoadEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiLevelLoadEvent.class, new DhApiEventDefinition(false, false)); + } + } @Override - public final boolean getCancelable() { return false; } + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiLevelLoadEvent.class); } //==================// diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelSaveEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelSaveEvent.java index f304cb703..141af117a 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelSaveEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelSaveEvent.java @@ -2,13 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents; import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * @author James Seibel - * @version 2022-9-10 + * @version 2022-11-21 */ -public abstract class DhApiLevelSaveEvent - implements IDhApiEvent +public abstract class DhApiLevelSaveEvent implements IDhApiEvent { /** Fired after Distant Horizons saves LOD data for the server. */ public abstract void onLevelSave(EventParam input); @@ -21,12 +22,21 @@ public abstract class DhApiLevelSaveEvent @Override public final boolean fireEvent(EventParam input) { - onLevelSave(input); + this.onLevelSave(input); return false; } + private static boolean firstTimeSetupComplete = false; + public DhApiLevelSaveEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiLevelSaveEvent.class, new DhApiEventDefinition(false, false)); + } + } @Override - public final boolean getCancelable() { return false; } + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiLevelSaveEvent.class); } //==================// diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java index ddd3a87c4..19fd671e3 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java @@ -2,13 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents; import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * @author James Seibel - * @version 2022-9-10 + * @version 2022-11-21 */ -public abstract class DhApiLevelUnloadEvent - implements IDhApiEvent +public abstract class DhApiLevelUnloadEvent implements IDhApiEvent { /** Fired before Distant Horizons unloads a level. */ public abstract void onLevelUnload(EventParam input); @@ -21,12 +22,21 @@ public abstract class DhApiLevelUnloadEvent @Override public final boolean fireEvent(EventParam input) { - onLevelUnload(input); + this.onLevelUnload(input); return false; } + private static boolean firstTimeSetupComplete = false; + public DhApiLevelUnloadEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiLevelUnloadEvent.class, new DhApiEventDefinition(false, false)); + } + } @Override - public final boolean getCancelable() { return false; } + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiLevelUnloadEvent.class); } //==================// diff --git a/api/src/main/java/com/seibel/lod/api/methods/events/interfaces/IDhApiEvent.java b/api/src/main/java/com/seibel/lod/api/methods/events/interfaces/IDhApiEvent.java index fee05d831..aa875d326 100644 --- a/api/src/main/java/com/seibel/lod/api/methods/events/interfaces/IDhApiEvent.java +++ b/api/src/main/java/com/seibel/lod/api/methods/events/interfaces/IDhApiEvent.java @@ -1,15 +1,15 @@ package com.seibel.lod.api.methods.events.interfaces; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; import com.seibel.lod.core.interfaces.dependencyInjection.IBindable; /** - * A combination of all interfaces required by all - * DH Api events. - * + * The interface used by all DH Api events. + * * @param This is the datatype that will be passed into the event handler's method. - * + * * @author James Seibel - * @version 2022-9-6 + * @version 2022-11-20 */ public interface IDhApiEvent extends IBindable { @@ -18,11 +18,12 @@ public interface IDhApiEvent extends IBindable //==========// /** - * Returns if the event should be automatically unbound + * Returns true if the event should be automatically unbound * after firing.
* Can be useful for one time setup events or waiting for a specific game state.

* - * Defaults to False (the event will not be removed after firing). + * Defaults to False + * IE: The event will not be removed after firing and will continue firing until removed. */ default boolean removeAfterFiring() { return false; }; @@ -31,8 +32,11 @@ public interface IDhApiEvent extends IBindable // internal // //==========// - /** Returns true if the event can be canceled. */ - boolean getCancelable(); + /** + * The event definition includes meta information about how the event will behave.
+ * For example: if the event is cancelable or not. + */ + DhApiEventDefinition getEventDefinition(); /** * Called internally by Distant Horizons when the event happens. diff --git a/api/src/main/java/com/seibel/lod/api/objects/events/DhApiEventDefinition.java b/api/src/main/java/com/seibel/lod/api/objects/events/DhApiEventDefinition.java new file mode 100644 index 000000000..bb5c712e3 --- /dev/null +++ b/api/src/main/java/com/seibel/lod/api/objects/events/DhApiEventDefinition.java @@ -0,0 +1,55 @@ +/* + * This file is part of the Distant Horizons mod (formerly the LOD Mod), + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2022 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.api.objects.events; + +import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.HashMap; + +/** + * The event definition includes meta information about how the event will behave. + * + * @author James Seibel + * @version 2022-11-20 + */ +public class DhApiEventDefinition +{ + /** True if the event can be canceled. */ + public final boolean isCancelable; + + /** + * True if the event will only ever be fired once.
+ * An example of this would be initial setup methods, DH won't run its initial setup more than once.

+ * + * If a handler is bound for a one time event after the event has been fired, the handler will be immediately fired. + */ + public final boolean isOneTimeEvent; + + + + public DhApiEventDefinition(boolean isCancelable, boolean isOneTimeEvent) + { + this.isCancelable = isCancelable; + this.isOneTimeEvent = isOneTimeEvent; + } + +} diff --git a/api/src/main/java/com/seibel/lod/core/DependencyInjection/DhApiEventInjector.java b/api/src/main/java/com/seibel/lod/core/DependencyInjection/DhApiEventInjector.java index 57b9ed3ec..f6a7b68cb 100644 --- a/api/src/main/java/com/seibel/lod/core/DependencyInjection/DhApiEventInjector.java +++ b/api/src/main/java/com/seibel/lod/core/DependencyInjection/DhApiEventInjector.java @@ -20,22 +20,25 @@ package com.seibel.lod.core.DependencyInjection; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; import com.seibel.lod.core.interfaces.dependencyInjection.IBindable; import com.seibel.lod.core.interfaces.dependencyInjection.IDhApiEventInjector; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.ArrayList; +import java.util.HashMap; /** * This class takes care of dependency injection for API events. * * @author James Seibel - * @version 2022-9-13 + * @version 2022-11-20 */ public class DhApiEventInjector extends DependencyInjector implements IDhApiEventInjector // Note to self: Don't try adding a generic type to IDhApiEvent, the consturctor won't accept it { private static final Logger LOGGER = LogManager.getLogger(DhApiEventInjector.class.getSimpleName()); + private static final HashMap, Object> FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE = new HashMap<>(); public static final DhApiEventInjector INSTANCE = new DhApiEventInjector(); @@ -44,34 +47,56 @@ public class DhApiEventInjector extends DependencyInjector implemen private DhApiEventInjector() { super(IDhApiEvent.class, true); } + @Override - public boolean unbind(Class dependencyInterface, Class dependencyClassToRemove) throws IllegalArgumentException + public void bind(Class eventInterface, IDhApiEvent eventImplementation) throws IllegalStateException, IllegalArgumentException + { + // is this a one time event? + if (ApiEventDefinitionHandler.getEventDefinition(eventInterface).isOneTimeEvent) + { + // has this one time event been fired yet? + if (FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.containsKey(eventInterface)) + { + // the one time event has happened, fire the handler + + // this has to be an unsafe cast since the hash map can't hold the generic objects + Object parameter = FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.get(eventInterface); + eventImplementation.fireEvent(parameter); + } + } + + // bind the event handler + super.bind(eventInterface, eventImplementation); + } + + @Override + public boolean unbind(Class eventInterface, Class eventClassToRemove) throws IllegalArgumentException { // make sure the given dependency implements the necessary interfaces - boolean implementsInterface = checkIfClassImplements(dependencyClassToRemove, dependencyInterface) - || checkIfClassExtends(dependencyClassToRemove, dependencyInterface); - boolean implementsBindable = checkIfClassImplements(dependencyClassToRemove, this.bindableInterface); + boolean implementsInterface = this.checkIfClassImplements(eventClassToRemove, eventInterface) || + this.checkIfClassExtends(eventClassToRemove, eventInterface); + boolean implementsBindable = this.checkIfClassImplements(eventClassToRemove, this.bindableInterface); // display any errors if (!implementsInterface) { - throw new IllegalArgumentException("The event handler [" + dependencyClassToRemove.getSimpleName() + "] doesn't implement or extend: [" + dependencyInterface.getSimpleName() + "]."); + throw new IllegalArgumentException("The event handler [" + eventClassToRemove.getSimpleName() + "] doesn't implement or extend: [" + eventInterface.getSimpleName() + "]."); } if (!implementsBindable) { - throw new IllegalArgumentException("The event handler [" + dependencyClassToRemove.getSimpleName() + "] doesn't implement the interface: [" + IBindable.class.getSimpleName() + "]."); + throw new IllegalArgumentException("The event handler [" + eventClassToRemove.getSimpleName() + "] doesn't implement the interface: [" + IBindable.class.getSimpleName() + "]."); } // actually remove the dependency - if (this.dependencies.containsKey(dependencyInterface)) + if (this.dependencies.containsKey(eventInterface)) { - ArrayList dependencyList = this.dependencies.get(dependencyInterface); + ArrayList dependencyList = this.dependencies.get(eventInterface); int indexToRemove = -1; for(int i = 0; i < dependencyList.size(); i++) { IBindable dependency = dependencyList.get(i); - if (dependency.getClass().equals(dependencyClassToRemove)) + if (dependency.getClass().equals(eventClassToRemove)) { indexToRemove = i; break; @@ -89,11 +114,20 @@ public class DhApiEventInjector extends DependencyInjector implemen } @Override - public > boolean fireAllEvents(Class dependencyInterface, T eventParameterObject) + public > boolean fireAllEvents(Class eventInterface, T eventParameterObject) { boolean cancelEvent = false; - ArrayList eventList = this.getAll(dependencyInterface); + // if this is a one time event, record it + if (ApiEventDefinitionHandler.getEventDefinition(eventInterface).isOneTimeEvent && + !FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.containsKey(eventInterface)) + { + FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.put(eventInterface, eventParameterObject); + } + + + // fire each bound event + ArrayList eventList = this.getAll(eventInterface); for (IDhApiEvent event : eventList) { if (event != null) @@ -106,8 +140,7 @@ public class DhApiEventInjector extends DependencyInjector implemen } catch (Exception e) { - // TODO log the failed event handler - LOGGER.error("Exception thrown by event handler :" + e.getMessage(), e); + LOGGER.error("Exception thrown by event handler [" + event.getClass().getSimpleName() + "] for event type [" + eventInterface.getSimpleName() + "], error:" + e.getMessage(), e); } } } diff --git a/api/src/main/java/com/seibel/lod/core/events/ApiEventDefinitionHandler.java b/api/src/main/java/com/seibel/lod/core/events/ApiEventDefinitionHandler.java new file mode 100644 index 000000000..84c29076b --- /dev/null +++ b/api/src/main/java/com/seibel/lod/core/events/ApiEventDefinitionHandler.java @@ -0,0 +1,71 @@ +/* + * This file is part of the Distant Horizons mod (formerly the LOD Mod), + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2022 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.core.events; + +import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.HashMap; + +/** + * Holds the API event definitions + * so, they can be accessed without an + * event instance. + * + * @author James Seibel + * @version 2022-11-21 + */ +public class ApiEventDefinitionHandler +{ + private static final Logger LOGGER = LogManager.getLogger(ApiEventDefinitionHandler.class.getSimpleName()); + // note to self: don't try adding a generic interface type here, the event dependency handler's constructor method won't accept it + private static final HashMap, DhApiEventDefinition> DEFINITIONS_BY_EVENT_INTERFACE = new HashMap<>(); + + public ApiEventDefinitionHandler INSTANCE = new ApiEventDefinitionHandler(); + + + + private ApiEventDefinitionHandler() { } + + + + public static void setEventDefinition(Class eventInterface, DhApiEventDefinition definition) + { + if (DEFINITIONS_BY_EVENT_INTERFACE.containsKey(eventInterface)) + { + LOGGER.warn("duplicate key added [" + eventInterface.getSimpleName() + "]"); + } + + DEFINITIONS_BY_EVENT_INTERFACE.put(eventInterface, definition); + } + + public static DhApiEventDefinition getEventDefinition(Class eventInterface) + { + if (!DEFINITIONS_BY_EVENT_INTERFACE.containsKey(eventInterface)) + { + throw new NullPointerException("event definition missing for: [" + eventInterface.getSimpleName() + "]"); + } + + return DEFINITIONS_BY_EVENT_INTERFACE.get(eventInterface); + } + +} diff --git a/api/src/test/java/testItems/events/abstractObjects/DhApiOneTimeTestEvent.java b/api/src/test/java/testItems/events/abstractObjects/DhApiOneTimeTestEvent.java new file mode 100644 index 000000000..4b82ab858 --- /dev/null +++ b/api/src/test/java/testItems/events/abstractObjects/DhApiOneTimeTestEvent.java @@ -0,0 +1,46 @@ +package testItems.events.abstractObjects; + +import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; + +/** + * A dummy event implementation used for unit testing. + * + * @author James Seibel + * @version 2022-11-20 + */ +public abstract class DhApiOneTimeTestEvent implements IDhApiEvent +{ + public abstract void onTestEvent(Boolean input); + + /** just used for testing */ + public abstract Boolean getTestValue(); + + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final boolean fireEvent(Boolean input) + { + this.onTestEvent(input); + return input; + } + + private static boolean firstTimeSetupComplete = false; + public DhApiOneTimeTestEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiOneTimeTestEvent.class, new DhApiEventDefinition(false, true)); + } + } + + @Override + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiOneTimeTestEvent.class); } + +} \ No newline at end of file diff --git a/api/src/test/java/testItems/events/abstractObjects/DhApiTestEvent.java b/api/src/test/java/testItems/events/abstractObjects/DhApiTestEvent.java index 49c9df103..e8eefba18 100644 --- a/api/src/test/java/testItems/events/abstractObjects/DhApiTestEvent.java +++ b/api/src/test/java/testItems/events/abstractObjects/DhApiTestEvent.java @@ -1,15 +1,16 @@ package testItems.events.abstractObjects; import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.lod.api.objects.events.DhApiEventDefinition; +import com.seibel.lod.core.events.ApiEventDefinitionHandler; /** * A dummy event implementation used for unit testing. * * @author James Seibel - * @version 2022-9-13 + * @version 2022-11-20 */ -public abstract class DhApiTestEvent - implements IDhApiEvent +public abstract class DhApiTestEvent implements IDhApiEvent { public abstract void onTestEvent(Boolean input); @@ -26,11 +27,21 @@ public abstract class DhApiTestEvent @Override public final boolean fireEvent(Boolean input) { - onTestEvent(input); + this.onTestEvent(input); return input; } + private static boolean firstTimeSetupComplete = false; + public DhApiTestEvent() + { + if (!firstTimeSetupComplete) + { + firstTimeSetupComplete = true; + ApiEventDefinitionHandler.setEventDefinition(DhApiTestEvent.class, new DhApiEventDefinition(false, false)); + } + } + @Override - public final boolean getCancelable() { return false; } + public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiTestEvent.class); } } \ No newline at end of file diff --git a/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandler.java b/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandler.java new file mode 100644 index 000000000..dae67e2fa --- /dev/null +++ b/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandler.java @@ -0,0 +1,26 @@ +package testItems.events.objects; + +import testItems.events.abstractObjects.DhApiOneTimeTestEvent; + +/** + * Dummy test event for unit tests. + * + * @author James Seibel + * @version 2022-11-20 + */ +public class DhOneTimeTestEventHandler extends DhApiOneTimeTestEvent +{ + public Boolean eventFiredValue = null; + + @Override + public void onTestEvent(Boolean input) { this.eventFiredValue = input; } + + @Override + public boolean removeAfterFiring() { return false; } + + + // test (non standard) methods // + @Override + public Boolean getTestValue() { return this.eventFiredValue; } + +} diff --git a/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandlerAlt.java b/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandlerAlt.java new file mode 100644 index 000000000..61b1b3ef2 --- /dev/null +++ b/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandlerAlt.java @@ -0,0 +1,12 @@ +package testItems.events.objects; + +/** + * Dummy test event for unit tests. + * + * @author James Seibel + * @version 2022-11-20 + */ +public class DhOneTimeTestEventHandlerAlt extends DhOneTimeTestEventHandler +{ + // all other code should be a duplicate of the parent +} diff --git a/api/src/test/java/testItems/events/objects/DhTestEvent.java b/api/src/test/java/testItems/events/objects/DhTestEventHandler.java similarity index 90% rename from api/src/test/java/testItems/events/objects/DhTestEvent.java rename to api/src/test/java/testItems/events/objects/DhTestEventHandler.java index cfe1d7015..6caae58a1 100644 --- a/api/src/test/java/testItems/events/objects/DhTestEvent.java +++ b/api/src/test/java/testItems/events/objects/DhTestEventHandler.java @@ -8,7 +8,7 @@ import testItems.events.abstractObjects.DhApiTestEvent; * @author James Seibel * @version 2022-9-11 */ -public class DhTestEvent extends DhApiTestEvent +public class DhTestEventHandler extends DhApiTestEvent { public Boolean eventFiredValue = null; diff --git a/api/src/test/java/testItems/events/objects/DhTestEventAlt.java b/api/src/test/java/testItems/events/objects/DhTestEventHandlerAlt.java similarity index 89% rename from api/src/test/java/testItems/events/objects/DhTestEventAlt.java rename to api/src/test/java/testItems/events/objects/DhTestEventHandlerAlt.java index c8db4ad4a..8604a5542 100644 --- a/api/src/test/java/testItems/events/objects/DhTestEventAlt.java +++ b/api/src/test/java/testItems/events/objects/DhTestEventHandlerAlt.java @@ -8,7 +8,7 @@ import testItems.events.abstractObjects.DhApiTestEvent; * @author James Seibel * @version 2022-9-11 */ -public class DhTestEventAlt extends DhApiTestEvent +public class DhTestEventHandlerAlt extends DhApiTestEvent { public Boolean eventFiredValue = null; diff --git a/api/src/test/java/tests/EventInjectorTest.java b/api/src/test/java/tests/EventInjectorTest.java index 804c74aa1..d51bdcb1f 100644 --- a/api/src/test/java/tests/EventInjectorTest.java +++ b/api/src/test/java/tests/EventInjectorTest.java @@ -3,34 +3,38 @@ package tests; import com.seibel.lod.core.DependencyInjection.DhApiEventInjector; import org.junit.Assert; import org.junit.Test; +import testItems.events.abstractObjects.DhApiOneTimeTestEvent; +import testItems.events.objects.DhOneTimeTestEventHandler; +import testItems.events.objects.DhOneTimeTestEventHandlerAlt; import testItems.events.abstractObjects.DhApiTestEvent; -import testItems.events.objects.DhTestEvent; -import testItems.events.objects.DhTestEventAlt; +import testItems.events.objects.DhTestEventHandler; +import testItems.events.objects.DhTestEventHandlerAlt; import java.util.ArrayList; /** * @author James Seibel - * @version 2022-9-13 + * @version 2022-11-21 */ public class EventInjectorTest { @Test - public void testEventDependencies() // this also tests list dependencies since there can be more than one event handler bound per event + public void testGeneralAndRecurringEvents() // this also tests list dependencies since there can be more than one event handler bound per event { // Injector setup DhApiEventInjector TEST_EVENT_HANDLER = DhApiEventInjector.INSTANCE; - + TEST_EVENT_HANDLER.clear(); + // pre-dependency setup Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(DhApiTestEvent.class)); // dependency setup - TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEvent()); - TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventAlt()); + TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandler()); + TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandlerAlt()); TEST_EVENT_HANDLER.runDelayedSetup(); @@ -60,8 +64,8 @@ public class EventInjectorTest // unbind DhApiTestEvent unboundEvent = afterRenderEventList.get(0); - Assert.assertTrue("Unbind should've removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEvent.class)); - Assert.assertFalse("Unbind should've already removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEvent.class)); + Assert.assertTrue("Unbind should've removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEventHandler.class)); + Assert.assertFalse("Unbind should've already removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEventHandler.class)); // check unbinding afterRenderEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class); @@ -72,10 +76,56 @@ public class EventInjectorTest // check unbound event firing Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", false, TEST_EVENT_HANDLER.fireAllEvents(DhApiTestEvent.class, false)); // remaining event - Assert.assertEquals("Event not fired for remaining object.", false, ((DhTestEventAlt) afterRenderEventList.get(0)).eventFiredValue); + Assert.assertEquals("Event not fired for remaining object.", false, ((DhTestEventHandlerAlt) afterRenderEventList.get(0)).eventFiredValue); // unbound event Assert.assertEquals("Event fired for unbound object.", true, unboundEvent.getTestValue()); - + + + // prevent event handlers from being bound to the wrong event interface + Assert.assertThrows("Event bound to a non-implementing interface.", IllegalArgumentException.class, () -> { TEST_EVENT_HANDLER.bind(DhApiOneTimeTestEvent.class, new DhTestEventHandler()); }); + Assert.assertThrows("Event bound to a non-implementing interface.", IllegalArgumentException.class, () -> { TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhOneTimeTestEventHandler()); }); + + } + + @Test + public void testOneTimeEventFiring() + { + // Injector setup + DhApiEventInjector TEST_EVENT_HANDLER = DhApiEventInjector.INSTANCE; + TEST_EVENT_HANDLER.clear(); + + + // pre-dependency setup + Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(DhApiOneTimeTestEvent.class)); + + + // pre-event fire binding + TEST_EVENT_HANDLER.bind(DhApiOneTimeTestEvent.class, new DhOneTimeTestEventHandler()); + TEST_EVENT_HANDLER.runDelayedSetup(); + + + // pre-bound event firing + Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", true, TEST_EVENT_HANDLER.fireAllEvents(DhApiOneTimeTestEvent.class, true)); + // validate event fired correctly + ArrayList oneTimeEventList = TEST_EVENT_HANDLER.getAll(DhApiOneTimeTestEvent.class); + Assert.assertEquals("Event not fired for pre-fire object.", true, oneTimeEventList.get(0).getTestValue()); + + + // post-event fire binding + // the event should fire instantly + TEST_EVENT_HANDLER.bind(DhApiOneTimeTestEvent.class, new DhOneTimeTestEventHandlerAlt()); + // validate both events have fired + oneTimeEventList = TEST_EVENT_HANDLER.getAll(DhApiOneTimeTestEvent.class); + Assert.assertEquals("Event not fired for pre-fire object.", true, oneTimeEventList.get(0).getTestValue()); + Assert.assertEquals("Event not fired for post-fire object.", true, oneTimeEventList.get(1).getTestValue()); + + + + // recurring event test + TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandler()); + ArrayList recurringEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class); + Assert.assertNull("This unrealted recurring event shouldn't have been fired.", recurringEventList.get(0).getTestValue()); + } } diff --git a/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java index ded177a75..bfeccdec0 100644 --- a/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/lod/core/api/internal/ClientApi.java @@ -22,7 +22,6 @@ package com.seibel.lod.core.api.internal; import com.seibel.lod.api.methods.events.abstractEvents.*; import com.seibel.lod.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.lod.core.DependencyInjection.DhApiEventInjector; -import com.seibel.lod.core.api.external.methods.data.DhApiTerrainDataRepo; import com.seibel.lod.core.level.IDhClientLevel; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.ModInfo;