diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java index f3e78e8c5..3380970d8 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterDhInitEvent.java @@ -1,17 +1,19 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiOneTimeEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; /** + * Fired after Distant Horizons finishes running its setup. + * * @author James Seibel - * @version 2022-11-21 + * @version 2023-6-23 */ -public abstract class DhApiAfterDhInitEvent implements IDhApiEvent +public abstract class DhApiAfterDhInitEvent implements IDhApiEvent, IDhApiOneTimeEvent { /** Fired after Distant Horizons finishes its initial setup on Minecraft startup. */ - public abstract void afterDistantHorizonsInit(); + public abstract void afterDistantHorizonsInit(DhApiEventParam input); //=========================// @@ -19,18 +21,6 @@ public abstract class DhApiAfterDhInitEvent implements IDhApiEvent //=========================// @Override - public final boolean fireEvent(Void ignoredParam) - { - this.afterDistantHorizonsInit(); - return false; - } - - /** - * Note: when creating new events, make sure to bind this definition in {@link ApiEventDefinitionHandler} - * Otherwise a bunch of runtime errors will be thrown. - */ - public final static DhApiEventDefinition EVENT_DEFINITION = new DhApiEventDefinition(false, true); - @Override - public final DhApiEventDefinition getEventDefinition() { return EVENT_DEFINITION; } + public final void fireEvent(DhApiEventParam input) { this.afterDistantHorizonsInit(input); } } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java index 3d8e5f66f..7d4846a52 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java @@ -1,18 +1,22 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; /** + * Fired after Distant Horizons finishes rendering a frame.
+ * At this point DH will have also finished cleaning up any modifications it + * did to the OpenGL state, so the state should be back to Minecraft's defaults. + * + * @see DhApiRenderParam * @author James Seibel - * @version 2022-11-21 + * @version 2023-6-23 */ public abstract class DhApiAfterRenderEvent implements IDhApiEvent { /** Fired after Distant Horizons finishes rendering fake chunks. */ - public abstract void afterRender(EventParam input); + public abstract void afterRender(DhApiEventParam input); //=========================// @@ -20,19 +24,7 @@ public abstract class DhApiAfterRenderEvent implements IDhApiEvent input) { this.afterRender(input); } //==================// diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java index 4258ddcb1..2a6063e40 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeDhInitEvent.java @@ -1,19 +1,19 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; - - import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; /** + * Fired before Distant Horizons starts running its mod loader setup.
+ * IE this is called before Forge's initClient/initServer or Fabric's init method. + * * @author James Seibel - * @version 2022-11-21 + * @version 2023-6-23 */ public abstract class DhApiBeforeDhInitEvent implements IDhApiEvent { /** Fired before Distant Horizons starts its initial setup on Minecraft startup. */ - public abstract void beforeDistantHorizonsInit(); + public abstract void beforeDistantHorizonsInit(DhApiEventParam input); //=========================// @@ -21,18 +21,6 @@ public abstract class DhApiBeforeDhInitEvent implements IDhApiEvent //=========================// @Override - public final boolean fireEvent(Void ignoredParam) - { - this.beforeDistantHorizonsInit(); - return false; - } - - /** - * Note: when creating new events, make sure to bind this definition in {@link ApiEventDefinitionHandler} - * Otherwise a bunch of runtime errors will be thrown. - */ - public final static DhApiEventDefinition EVENT_DEFINITION = new DhApiEventDefinition(false, true); - @Override - public final DhApiEventDefinition getEventDefinition() { return EVENT_DEFINITION; } + public final void fireEvent(DhApiEventParam input) { this.beforeDistantHorizonsInit(input); } } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java index 658291d68..9998bd4c0 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderEvent.java @@ -1,22 +1,20 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; /** + * Called before Distant Horizons starts rendering a frame.
+ * Canceling the event will prevent DH from rendering that frame. + * * @author James Seibel - * @version 2022-11-21 + * @version 2023-6-23 */ -public abstract class DhApiBeforeRenderEvent implements IDhApiEvent +public abstract class DhApiBeforeRenderEvent implements IDhApiCancelableEvent { - /** - * Fired before Distant Horizons renders fake chunks. - * - * @return whether the event should be canceled or not. - */ - public abstract boolean beforeRender(EventParam input); + /** Fired before Distant Horizons renders LODs. */ + public abstract void beforeRender(DhApiCancelableEventParam input); //=========================// @@ -24,15 +22,7 @@ public abstract class DhApiBeforeRenderEvent implements IDhApiEvent input) { this.beforeRender(input); } //==================// diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java new file mode 100644 index 000000000..6684a6167 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java @@ -0,0 +1,55 @@ +package com.seibel.distanthorizons.api.methods.events.abstractEvents; + +import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataRepo; +import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; + +/** + * Fired whenever Distant Horizons has been notified + * that a Minecraft chunk has been modified.
+ * By the time this event has been fired, the chunk modification should have propagated + * to DH's full data source, but may not have been updated in the render data source. + * + * @see IDhApiTerrainDataRepo + * @author James Seibel + * @version 2023-6-23 + */ +public abstract class DhApiChunkModifiedEvent implements IDhApiEvent +{ + /** Fired after Distant Horizons saves LOD data for the server. */ + public abstract void onChunkModified(DhApiEventParam input); + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final void fireEvent(DhApiEventParam input) { this.onChunkModified(input); } + + + //==================// + // parameter object // + //==================// + + public static class EventParam + { + /** The saved level. */ + public final IDhApiLevelWrapper levelWrapper; + + /** the modified chunk's X pos in chunk coordinates */ + public final int chunkX; + /** the modified chunk's Z pos in chunk coordinates */ + public final int chunkZ; + + + public EventParam(IDhApiLevelWrapper newLevelWrapper, int chunkX, int chunkZ) + { + this.levelWrapper = newLevelWrapper; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + } + } + +} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiDataFileChangedEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiDataFileChangedEvent.java deleted file mode 100644 index 1f78d01b8..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiDataFileChangedEvent.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.seibel.distanthorizons.api.methods.events.abstractEvents; - -import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; -import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataRepo; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; - -/** - * @author James Seibel - * @version 2023-6-19 - */ -public abstract class DhApiDataFileChangedEvent implements IDhApiEvent -{ - /** - * Fired after any data files handled by Distant Horizons are modified.
- * Note: this event may not fire immediately after a change happens, - * this event is only fired after the data is saved to disk. - */ - public abstract void onDhDataFileChanged(EventParam input); - - - //=========================// - // internal DH API methods // - //=========================// - - @Override - public final boolean fireEvent(EventParam input) - { - this.onDhDataFileChanged(input); - return false; - } - - /** - * Note: when creating new events, make sure to bind this definition in {@link ApiEventDefinitionHandler} - * Otherwise a bunch of runtime errors will be thrown. - */ - public final static DhApiEventDefinition EVENT_DEFINITION = new DhApiEventDefinition(false, false); - @Override - public final DhApiEventDefinition getEventDefinition() { return EVENT_DEFINITION; } - - - //==================// - // parameter object // - //==================// - - /** in order to access the modified data, please use the {@link IDhApiTerrainDataRepo}. */ - public static class EventParam - { - /** defines what type of data was modified */ - public final EDataType dataTypeEnum; - - /** See {@link EDhApiDetailLevel} for more information on detail levels. */ - public final byte detailLevel; - public final int posX; - public final int posZ; - - - public EventParam(EDataType dataTypeEnum, byte detailLevel, int posX, int posZ) - { - this.dataTypeEnum = dataTypeEnum; - - this.detailLevel = detailLevel; - this.posX = posX; - this.posZ = posZ; - } - } - - /** when in doubt, use {@link EDataType#Full}. */ - public enum EDataType - { - /** color data, based on the currently selected resource packs */ - Render, - /** ID based data */ - Full; - } - -} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java index ce22739b9..afc6787a7 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelLoadEvent.java @@ -2,17 +2,19 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; /** + * Called after Distant Horizons finishes loading a new level.
+ * Note: this may be fired before Minecraft has loaded in the player. + * * @author James Seibel - * @version 2022-11-21 + * @version 2023-6-23 */ public abstract class DhApiLevelLoadEvent implements IDhApiEvent { /** Fired after Distant Horizons loads a new level. */ - public abstract void onLevelLoad(EventParam input); + public abstract void onLevelLoad(DhApiEventParam input); //=========================// @@ -20,19 +22,7 @@ public abstract class DhApiLevelLoadEvent implements IDhApiEvent input) { this.onLevelLoad(input); } //==================// @@ -44,7 +34,6 @@ public abstract class DhApiLevelLoadEvent implements IDhApiEvent -{ - /** Fired after Distant Horizons saves LOD data for the server. */ - public abstract void onLevelSave(EventParam input); - - - //=========================// - // internal DH API methods // - //=========================// - - @Override - public final boolean fireEvent(EventParam input) - { - this.onLevelSave(input); - return false; - } - - /** - * Note: when creating new events, make sure to bind this definition in {@link ApiEventDefinitionHandler} - * Otherwise a bunch of runtime errors will be thrown. - */ - public final static DhApiEventDefinition EVENT_DEFINITION = new DhApiEventDefinition(false, false); - @Override - public final DhApiEventDefinition getEventDefinition() { return EVENT_DEFINITION; } - - - //==================// - // parameter object // - //==================// - - public static class EventParam - { - /** The saved level. */ - public final IDhApiLevelWrapper levelWrapper; - - - public EventParam(IDhApiLevelWrapper newLevelWrapper) { this.levelWrapper = newLevelWrapper; } - } - -} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java index 95eff7a08..1746f3696 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiLevelUnloadEvent.java @@ -1,18 +1,20 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; +import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; /** + * Called after Distant Horizons has finished unloading a level. + * * @author James Seibel - * @version 2022-11-21 + * @version 2023-6-23 */ public abstract class DhApiLevelUnloadEvent implements IDhApiEvent { /** Fired before Distant Horizons unloads a level. */ - public abstract void onLevelUnload(EventParam input); + public abstract void onLevelUnload(DhApiEventParam input); //=========================// @@ -20,19 +22,7 @@ public abstract class DhApiLevelUnloadEvent implements IDhApiEvent input) { this.onLevelUnload(input); } //==================// diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiCancelableEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiCancelableEvent.java new file mode 100644 index 000000000..4168f699a --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiCancelableEvent.java @@ -0,0 +1,39 @@ +package com.seibel.distanthorizons.api.methods.events.interfaces; + +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; + +/** + * If a {@link IDhApiEvent} implements this interface than the event + * can be canceled via the {@link DhApiCancelableEventParam#cancelEvent()} method. + * + * @author James Seibel + * @version 2023-6-23 + */ +public interface IDhApiCancelableEvent extends IDhApiEvent +{ + + void fireEvent(DhApiCancelableEventParam input); + + /** + * Shouldn't be called.

+ * + * The {@link IDhApiCancelableEvent#fireEvent(DhApiCancelableEventParam)} method should be used instead. + * This override method is present to prevent API users from having to implement it themselves. + * + * @implNote Is there a better way to format the {@link IDhApiEvent} classes so we don't need this method? + * It would be better to completely hide this method so it isn't possible to accidentally call. + */ + @Deprecated + @Override + default void fireEvent(DhApiEventParam input) + { + if (!input.getClass().isAssignableFrom(DhApiCancelableEventParam.class)) + { + throw new IllegalArgumentException("Programmer error. ["+IDhApiCancelableEvent.class.getSimpleName()+"] was given a ["+DhApiEventParam.class.getSimpleName()+"] when it should only be given a ["+DhApiCancelableEventParam.class.getSimpleName()+"]."); + } + + this.fireEvent((DhApiCancelableEventParam) input); + } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiEvent.java index cae76f9ca..0f811c326 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiEvent.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.api.methods.events.interfaces; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; /** @@ -9,7 +10,7 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab * @param This is the datatype that will be passed into the event handler's method. * * @author James Seibel - * @version 2022-11-20 + * @version 2023-6-23 */ public interface IDhApiEvent extends IBindable { @@ -25,28 +26,20 @@ public interface IDhApiEvent extends IBindable * Defaults to False * IE: The event will not be removed after firing and will continue firing until removed. */ - default boolean removeAfterFiring() { return false; }; + default boolean removeAfterFiring() { return false; } //==========// // internal // //==========// - /** - * 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. * This method shouldn't directly be overridden and * should call a more specific method instead. * * @param input the parameter object passed in from the event source. Can be null. - * @return whether the event should be canceled or not. - * A canceled event will still fire the other event handlers that are queued. */ - boolean fireEvent(T input); + void fireEvent(DhApiEventParam input); } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiOneTimeEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiOneTimeEvent.java new file mode 100644 index 000000000..627c6e4ef --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiOneTimeEvent.java @@ -0,0 +1,12 @@ +package com.seibel.distanthorizons.api.methods.events.interfaces; + +/** + * If a {@link IDhApiEvent} implements this interface then 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 to a one time event after the event has been fired, the handler will immediately fire. + */ +public interface IDhApiOneTimeEvent extends IDhApiEvent +{ + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiCancelableEventParam.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiCancelableEventParam.java new file mode 100644 index 000000000..d1dcf2400 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiCancelableEventParam.java @@ -0,0 +1,14 @@ +package com.seibel.distanthorizons.api.methods.events.sharedParameterObjects; + +/** Extension of {@link DhApiEventParam} that allows the event to be canceled. */ +public class DhApiCancelableEventParam extends DhApiEventParam +{ + public DhApiCancelableEventParam(T value) { super(value); } + + private boolean eventCanceled = false; + /** Prevents the DH event from completing after all bound event handlers have been fired. */ + public void cancelEvent() { this.eventCanceled = true; } + /** @return if this DH event has been canceled, either by this event handler or a previous one. */ + public boolean isEventCanceled() { return this.eventCanceled; } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiEventParam.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiEventParam.java new file mode 100644 index 000000000..61b5bfc3f --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiEventParam.java @@ -0,0 +1,14 @@ +package com.seibel.distanthorizons.api.methods.events.sharedParameterObjects; + +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; + +/** Wraps the event parameter to allow for additional control over the event */ +public class DhApiEventParam +{ + /** Depending on the {@link IDhApiEvent} this can be null. */ + public final T value; + + + public DhApiEventParam(T value) { this.value = value; } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java index 21981bf0c..131eef5b8 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/sharedParameterObjects/DhApiRenderParam.java @@ -3,7 +3,7 @@ package com.seibel.distanthorizons.api.methods.events.sharedParameterObjects; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; /** - * Parameter passed into Render events. + * Contains information relevant to Distant Horizons and Minecraft rendering. * * @author James Seibel * @version 2022-9-5 diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/events/DhApiEventDefinition.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/events/DhApiEventDefinition.java deleted file mode 100644 index 4f3d0c48c..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/events/DhApiEventDefinition.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.distanthorizons.api.objects.events; - -/** - * 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/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java index dbd129c3d..cca942e6d 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java @@ -20,8 +20,11 @@ package com.seibel.distanthorizons.coreapi.DependencyInjection; import com.seibel.distanthorizons.api.interfaces.events.IDhApiEventInjector; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiOneTimeEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -29,12 +32,7 @@ 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-11-24 - */ +/** This class takes care of dependency injection for API events. */ public class ApiEventInjector extends DependencyInjector implements IDhApiEventInjector // Note to self: Don't try adding a generic type to IDhApiEvent, the constructor won't accept it { public static final ApiEventInjector INSTANCE = new ApiEventInjector(); @@ -54,7 +52,7 @@ public class ApiEventInjector extends DependencyInjector implements public void bind(Class abstractEvent, IDhApiEvent eventImplementation) throws IllegalStateException, IllegalArgumentException { // is this a one time event? - if (ApiEventDefinitionHandler.INSTANCE.getEventDefinition(abstractEvent).isOneTimeEvent) + if (IDhApiOneTimeEvent.class.isAssignableFrom(abstractEvent)) { // has this one time event been fired yet? if (this.firedOneTimeEventParamsByEventInterface.containsKey(abstractEvent)) @@ -63,7 +61,9 @@ public class ApiEventInjector extends DependencyInjector implements // this has to be an unsafe cast since the hash map can't hold the generic objects Object parameter = this.firedOneTimeEventParamsByEventInterface.get(abstractEvent); - eventImplementation.fireEvent(parameter); + // one time events probably aren't cancelable, but just in case + DhApiEventParam eventParam = createEventParamWrapper(eventImplementation, parameter); + eventImplementation.fireEvent(eventParam); } } @@ -116,31 +116,65 @@ public class ApiEventInjector extends DependencyInjector implements } @Override - public > boolean fireAllEvents(Class abstractEvent, T eventParameterObject) + public > boolean fireAllEvents(Class abstractEventClass, T eventInput) { - boolean cancelEvent = false; - // if this is a one time event, record that it was called - if (ApiEventDefinitionHandler.INSTANCE.getEventDefinition(abstractEvent).isOneTimeEvent && - !this.firedOneTimeEventParamsByEventInterface.containsKey(abstractEvent)) { - this.firedOneTimeEventParamsByEventInterface.put(abstractEvent, eventParameterObject); + if (IDhApiOneTimeEvent.class.isAssignableFrom(abstractEventClass) && + !this.firedOneTimeEventParamsByEventInterface.containsKey(abstractEventClass)) + { + this.firedOneTimeEventParamsByEventInterface.put(abstractEventClass, eventInput); } // fire each bound event - ArrayList eventList = this.getAll(abstractEvent); - for (IDhApiEvent event : eventList) { - if (event != null) { - try { + boolean cancelEvent = false; + ArrayList eventList = this.getAll(abstractEventClass); + ArrayList> eventsToRemove = new ArrayList<>(); + + for (IDhApiEvent event : eventList) + { + if (event != null) + { + try + { // fire each event and record if any of them // request to cancel the event. - cancelEvent |= event.fireEvent(eventParameterObject); - } catch (Exception e) { - LOGGER.error("Exception thrown by event handler [" + event.getClass().getSimpleName() + "] for event type [" + abstractEvent.getSimpleName() + "], error:" + e.getMessage(), e); + + DhApiEventParam eventParam = createEventParamWrapper(event, eventInput); + event.fireEvent(eventParam); + + if (eventParam instanceof DhApiCancelableEventParam) + { + DhApiCancelableEventParam cancelableEventParam = (DhApiCancelableEventParam) eventParam; + cancelEvent |= cancelableEventParam.isEventCanceled(); + } + + if (event.removeAfterFiring()) + { + eventsToRemove.add(event); + } + } + catch (Exception e) + { + LOGGER.error("Exception thrown by event handler [" + event.getClass().getSimpleName() + "] for event type [" + abstractEventClass.getSimpleName() + "], error:" + e.getMessage(), e); } } } + + + // remove any removeAfterFire events + for (IDhApiEvent eventToRemove : eventsToRemove) + { + this.unbind(abstractEventClass, eventToRemove.getClass()); + } + return cancelEvent; } + + public static DhApiEventParam createEventParamWrapper(IDhApiEvent event, T parameter) + { + return (event instanceof IDhApiCancelableEvent) ? new DhApiCancelableEventParam<>(parameter) : new DhApiEventParam<>(parameter); + } + } diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/events/ApiEventDefinitionHandler.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/events/ApiEventDefinitionHandler.java deleted file mode 100644 index d4764e986..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/events/ApiEventDefinitionHandler.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.distanthorizons.coreapi.events; - -import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; -import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.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-24 - */ -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 final HashMap, DhApiEventDefinition> DEFINITIONS_BY_EVENT_INTERFACE = new HashMap<>(); - - public static final ApiEventDefinitionHandler INSTANCE = new ApiEventDefinitionHandler(); - - - - private ApiEventDefinitionHandler() { this.addInitialBindings(); } - - /** This must include all available events */ - public void addInitialBindings() - { - this.setEventDefinition(DhApiAfterDhInitEvent.class, DhApiAfterDhInitEvent.EVENT_DEFINITION); - this.setEventDefinition(DhApiAfterRenderEvent.class, DhApiAfterRenderEvent.EVENT_DEFINITION); - this.setEventDefinition(DhApiBeforeDhInitEvent.class, DhApiBeforeDhInitEvent.EVENT_DEFINITION); - this.setEventDefinition(DhApiBeforeRenderEvent.class, DhApiBeforeRenderEvent.EVENT_DEFINITION); - this.setEventDefinition(DhApiLevelLoadEvent.class, DhApiLevelLoadEvent.EVENT_DEFINITION); - this.setEventDefinition(DhApiLevelSaveEvent.class, DhApiLevelSaveEvent.EVENT_DEFINITION); - this.setEventDefinition(DhApiLevelUnloadEvent.class, DhApiLevelUnloadEvent.EVENT_DEFINITION); - this.setEventDefinition(DhApiDataFileChangedEvent.class, DhApiDataFileChangedEvent.EVENT_DEFINITION); - - } - - - /** - * This should only be used for unit testing. - * Under normal circumstances there isn't any reason to clear the event definitions. - */ - public void clear() { this.DEFINITIONS_BY_EVENT_INTERFACE.clear(); } - - - - public void setEventDefinition(Class eventInterface, DhApiEventDefinition definition) - { - if (this.DEFINITIONS_BY_EVENT_INTERFACE.containsKey(eventInterface)) - { - LOGGER.warn("duplicate key added [" + eventInterface.getSimpleName() + "]"); - } - - this.DEFINITIONS_BY_EVENT_INTERFACE.put(eventInterface, definition); - } - - public DhApiEventDefinition getEventDefinition(Class eventInterface) - { - if (!this.DEFINITIONS_BY_EVENT_INTERFACE.containsKey(eventInterface)) - { - throw new NullPointerException("event definition missing for: [" + eventInterface.getSimpleName() + "]"); - } - - return this.DEFINITIONS_BY_EVENT_INTERFACE.get(eventInterface); - } - -} diff --git a/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiCancelableOneTimeTestEvent.java b/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiCancelableOneTimeTestEvent.java new file mode 100644 index 000000000..1b69f07af --- /dev/null +++ b/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiCancelableOneTimeTestEvent.java @@ -0,0 +1,37 @@ +package testItems.events.abstractObjects; + +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiOneTimeEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; + +/** + * A dummy event implementation used for unit testing. + * + * @author James Seibel + * @version 2023-6-23 + */ +public abstract class AbstractDhApiCancelableOneTimeTestEvent implements IDhApiCancelableEvent, IDhApiOneTimeEvent +{ + public abstract void onTestEvent(DhApiCancelableEventParam input); + + /** just used for testing */ + public abstract Boolean getTestValue(); + + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final void fireEvent(DhApiCancelableEventParam input) + { + this.onTestEvent(input); + if (input.value) + { + input.cancelEvent(); + } + } + +} \ No newline at end of file diff --git a/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiRemoveAfterFireTestEvent.java b/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiRemoveAfterFireTestEvent.java new file mode 100644 index 000000000..6d164c3a1 --- /dev/null +++ b/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiRemoveAfterFireTestEvent.java @@ -0,0 +1,31 @@ +package testItems.events.abstractObjects; + +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; + +/** + * A dummy event implementation used for unit testing. + * + * @author James Seibel + * @version 2023-6-23 + */ +public abstract class AbstractDhApiRemoveAfterFireTestEvent implements IDhApiEvent +{ + + public abstract void onTestEvent(DhApiEventParam input); + + /** just used for testing */ + public abstract Boolean getTestValue(); + + @Override + public boolean removeAfterFiring() { return true; } + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final void fireEvent(DhApiEventParam input) { this.onTestEvent(input); } + +} \ No newline at end of file diff --git a/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiTestEvent.java b/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiTestEvent.java new file mode 100644 index 000000000..a912a3ef1 --- /dev/null +++ b/api/src/test/java/testItems/events/abstractObjects/AbstractDhApiTestEvent.java @@ -0,0 +1,29 @@ +package testItems.events.abstractObjects; + +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; + +/** + * A dummy event implementation used for unit testing. + * + * @author James Seibel + * @version 2023-6-23 + */ +public abstract class AbstractDhApiTestEvent implements IDhApiEvent +{ + + public abstract void onTestEvent(DhApiEventParam input); + + /** just used for testing */ + public abstract Boolean getTestValue(); + + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final void fireEvent(DhApiEventParam input) { this.onTestEvent(input); } + +} \ No newline at end of file diff --git a/api/src/test/java/testItems/events/abstractObjects/DhApiOneTimeTestEvent.java b/api/src/test/java/testItems/events/abstractObjects/DhApiOneTimeTestEvent.java deleted file mode 100644 index b13750116..000000000 --- a/api/src/test/java/testItems/events/abstractObjects/DhApiOneTimeTestEvent.java +++ /dev/null @@ -1,36 +0,0 @@ -package testItems.events.abstractObjects; - -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; - -/** - * 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; - } - - public final static DhApiEventDefinition EVENT_DEFINITION = new DhApiEventDefinition(false, true); - @Override - public final DhApiEventDefinition getEventDefinition() { return EVENT_DEFINITION; } - -} \ 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 deleted file mode 100644 index 92ac1b4da..000000000 --- a/api/src/test/java/testItems/events/abstractObjects/DhApiTestEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -package testItems.events.abstractObjects; - -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; - -/** - * A dummy event implementation used for unit testing. - * - * @author James Seibel - * @version 2022-11-20 - */ -public abstract class DhApiTestEvent 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; - } - - public final static DhApiEventDefinition EVENT_DEFINITION = new DhApiEventDefinition(false, false); - @Override - public final DhApiEventDefinition getEventDefinition() { return EVENT_DEFINITION; } - -} \ No newline at end of file diff --git a/api/src/test/java/testItems/events/objects/DhCancelableOneTimeTestEventHandler.java b/api/src/test/java/testItems/events/objects/DhCancelableOneTimeTestEventHandler.java new file mode 100644 index 000000000..3aed71ef9 --- /dev/null +++ b/api/src/test/java/testItems/events/objects/DhCancelableOneTimeTestEventHandler.java @@ -0,0 +1,24 @@ +package testItems.events.objects; + +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; +import testItems.events.abstractObjects.AbstractDhApiCancelableOneTimeTestEvent; + +/** + * Dummy test event for unit tests. + * + * @author James Seibel + * @version 2023-6-23 + */ +public class DhCancelableOneTimeTestEventHandler extends AbstractDhApiCancelableOneTimeTestEvent +{ + public Boolean eventFiredValue = null; + + @Override + public void onTestEvent(DhApiCancelableEventParam input) { this.eventFiredValue = input.value; } + + + // 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/DhCancelableOneTimeTestEventHandlerAlt.java similarity index 58% rename from api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandlerAlt.java rename to api/src/test/java/testItems/events/objects/DhCancelableOneTimeTestEventHandlerAlt.java index 61b1b3ef2..6a3aaddeb 100644 --- a/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandlerAlt.java +++ b/api/src/test/java/testItems/events/objects/DhCancelableOneTimeTestEventHandlerAlt.java @@ -4,9 +4,9 @@ package testItems.events.objects; * Dummy test event for unit tests. * * @author James Seibel - * @version 2022-11-20 + * @version 2023-6-23 */ -public class DhOneTimeTestEventHandlerAlt extends DhOneTimeTestEventHandler +public class DhCancelableOneTimeTestEventHandlerAlt extends DhCancelableOneTimeTestEventHandler { // all other code should be a duplicate of the parent } diff --git a/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandler.java b/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandler.java deleted file mode 100644 index dae67e2fa..000000000 --- a/api/src/test/java/testItems/events/objects/DhOneTimeTestEventHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -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/DhRemoveAfterFireTestEventHandler.java b/api/src/test/java/testItems/events/objects/DhRemoveAfterFireTestEventHandler.java new file mode 100644 index 000000000..a91e4fa11 --- /dev/null +++ b/api/src/test/java/testItems/events/objects/DhRemoveAfterFireTestEventHandler.java @@ -0,0 +1,25 @@ +package testItems.events.objects; + +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; +import testItems.events.abstractObjects.AbstractDhApiRemoveAfterFireTestEvent; +import testItems.events.abstractObjects.AbstractDhApiTestEvent; + +/** + * Dummy test event for unit tests. + * + * @author James Seibel + * @version 2023-6-23 + */ +public class DhRemoveAfterFireTestEventHandler extends AbstractDhApiRemoveAfterFireTestEvent +{ + public Boolean eventFiredValue = null; + + @Override + public void onTestEvent(DhApiEventParam input) { this.eventFiredValue = input.value; } + + + // test (non standard) methods // + @Override + public Boolean getTestValue() { return this.eventFiredValue; } + +} diff --git a/api/src/test/java/testItems/events/objects/DhTestEventHandler.java b/api/src/test/java/testItems/events/objects/DhTestEventHandler.java index 6caae58a1..b8ae86244 100644 --- a/api/src/test/java/testItems/events/objects/DhTestEventHandler.java +++ b/api/src/test/java/testItems/events/objects/DhTestEventHandler.java @@ -1,30 +1,24 @@ package testItems.events.objects; -import testItems.events.abstractObjects.DhApiTestEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; +import testItems.events.abstractObjects.AbstractDhApiTestEvent; /** * Dummy test event for unit tests. * * @author James Seibel - * @version 2022-9-11 + * @version 2023-6-23 */ -public class DhTestEventHandler extends DhApiTestEvent +public class DhTestEventHandler extends AbstractDhApiTestEvent { public Boolean eventFiredValue = null; @Override - public void onTestEvent(Boolean input) - { - this.eventFiredValue = input; - } - - @Override - public boolean removeAfterFiring() { return false; } - + public void onTestEvent(DhApiEventParam input) { this.eventFiredValue = input.value; } // test (non standard) methods // @Override - public Boolean getTestValue() { return eventFiredValue; } + public Boolean getTestValue() { return this.eventFiredValue; } } diff --git a/api/src/test/java/testItems/events/objects/DhTestEventHandlerAlt.java b/api/src/test/java/testItems/events/objects/DhTestEventHandlerAlt.java index 8604a5542..0fa87436d 100644 --- a/api/src/test/java/testItems/events/objects/DhTestEventHandlerAlt.java +++ b/api/src/test/java/testItems/events/objects/DhTestEventHandlerAlt.java @@ -1,30 +1,25 @@ package testItems.events.objects; -import testItems.events.abstractObjects.DhApiTestEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; +import testItems.events.abstractObjects.AbstractDhApiTestEvent; /** * Dummy test event for unit tests. * * @author James Seibel - * @version 2022-9-11 + * @version 2023-6-23 */ -public class DhTestEventHandlerAlt extends DhApiTestEvent +public class DhTestEventHandlerAlt extends AbstractDhApiTestEvent { public Boolean eventFiredValue = null; @Override - public void onTestEvent(Boolean input) - { - this.eventFiredValue = input; - } - - @Override - public boolean removeAfterFiring() { return false; } + public void onTestEvent(DhApiEventParam input) { this.eventFiredValue = input.value; } // test (non standard) methods // @Override - public Boolean getTestValue() { return eventFiredValue; } + public Boolean getTestValue() { return this.eventFiredValue; } } diff --git a/api/src/test/java/tests/EventInjectorTest.java b/api/src/test/java/tests/EventInjectorTest.java index b0f40b87b..70ad89118 100644 --- a/api/src/test/java/tests/EventInjectorTest.java +++ b/api/src/test/java/tests/EventInjectorTest.java @@ -1,73 +1,54 @@ package tests; -import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent; -import com.seibel.distanthorizons.api.objects.events.DhApiEventDefinition; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.coreapi.events.ApiEventDefinitionHandler; import org.junit.Assert; import org.junit.Before; 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.DhTestEventHandler; -import testItems.events.objects.DhTestEventHandlerAlt; +import testItems.events.abstractObjects.AbstractDhApiCancelableOneTimeTestEvent; +import testItems.events.abstractObjects.AbstractDhApiRemoveAfterFireTestEvent; +import testItems.events.objects.*; +import testItems.events.abstractObjects.AbstractDhApiTestEvent; import java.util.ArrayList; /** * @author James Seibel - * @version 2022-11-25 + * @version 2023-6-23 */ public class EventInjectorTest { @Before public void testSetup() { - ApiEventDefinitionHandler definitionHandler = ApiEventDefinitionHandler.INSTANCE; - - - // reset the injectors and event definitions + // reset the injectors ApiEventInjector.INSTANCE.clear(); - definitionHandler.clear(); - - - // register test events - definitionHandler.setEventDefinition(DhApiTestEvent.class, DhApiTestEvent.EVENT_DEFINITION); - definitionHandler.setEventDefinition(DhApiOneTimeTestEvent.class, DhApiOneTimeTestEvent.EVENT_DEFINITION); - definitionHandler.addInitialBindings(); - } - - - @Test public void testGeneralAndRecurringEvents() // this also tests list dependencies since there can be more than one event handler bound per event { // Injector setup ApiEventInjector TEST_EVENT_HANDLER = ApiEventInjector.INSTANCE; - + // pre-dependency setup - Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(DhApiTestEvent.class)); - - + Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(AbstractDhApiTestEvent.class)); + + // dependency setup - TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandler()); - TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandlerAlt()); + TEST_EVENT_HANDLER.bind(AbstractDhApiTestEvent.class, new DhTestEventHandler()); + TEST_EVENT_HANDLER.bind(AbstractDhApiTestEvent.class, new DhTestEventHandlerAlt()); TEST_EVENT_HANDLER.runDelayedSetup(); - - + + // get first - DhApiTestEvent afterRenderEvent = TEST_EVENT_HANDLER.get(DhApiTestEvent.class); + AbstractDhApiTestEvent afterRenderEvent = TEST_EVENT_HANDLER.get(AbstractDhApiTestEvent.class); Assert.assertNotNull("Event not bound.", afterRenderEvent); - - + + // get list - ArrayList afterRenderEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class); + ArrayList afterRenderEventList = TEST_EVENT_HANDLER.getAll(AbstractDhApiTestEvent.class); Assert.assertEquals("Bound list doesn't contain the correct number of items.", 2, afterRenderEventList.size()); // object one Assert.assertNotNull("Event not bound.", afterRenderEventList.get(0)); @@ -75,29 +56,29 @@ public class EventInjectorTest // object two Assert.assertNotNull("Event not bound.", afterRenderEventList.get(1)); Assert.assertEquals("First event object setup incorrectly.", null, afterRenderEventList.get(1).getTestValue()); - - + + // event firing - Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", true, TEST_EVENT_HANDLER.fireAllEvents(DhApiTestEvent.class, true)); + Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", false, TEST_EVENT_HANDLER.fireAllEvents(AbstractDhApiTestEvent.class, true)); // object one Assert.assertEquals("Event not fired for first object.", true, afterRenderEventList.get(0).getTestValue()); // object two Assert.assertEquals("Event not fired for second object.", true, afterRenderEventList.get(1).getTestValue()); - - + + // unbind - DhApiTestEvent unboundEvent = afterRenderEventList.get(0); - 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)); - + AbstractDhApiTestEvent unboundEvent = afterRenderEventList.get(0); + Assert.assertTrue("Unbind should've removed item.", TEST_EVENT_HANDLER.unbind(AbstractDhApiTestEvent.class, DhTestEventHandler.class)); + Assert.assertFalse("Unbind should've already removed item.", TEST_EVENT_HANDLER.unbind(AbstractDhApiTestEvent.class, DhTestEventHandler.class)); + // check unbinding - afterRenderEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class); + afterRenderEventList = TEST_EVENT_HANDLER.getAll(AbstractDhApiTestEvent.class); Assert.assertEquals("Unbound list doesn't contain the correct number of items.", 1, afterRenderEventList.size()); Assert.assertNotNull("Unbinding removed all items.", afterRenderEventList.get(0)); - - + + // check unbound event firing - Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", false, TEST_EVENT_HANDLER.fireAllEvents(DhApiTestEvent.class, false)); + Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", false, TEST_EVENT_HANDLER.fireAllEvents(AbstractDhApiTestEvent.class, false)); // remaining event Assert.assertEquals("Event not fired for remaining object.", false, ((DhTestEventHandlerAlt) afterRenderEventList.get(0)).eventFiredValue); // unbound event @@ -105,22 +86,11 @@ public class EventInjectorTest // 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()); }); + Assert.assertThrows("Event bound to a non-implementing interface.", IllegalArgumentException.class, () -> { TEST_EVENT_HANDLER.bind(AbstractDhApiCancelableOneTimeTestEvent.class, new DhTestEventHandler()); }); + Assert.assertThrows("Event bound to a non-implementing interface.", IllegalArgumentException.class, () -> { TEST_EVENT_HANDLER.bind(AbstractDhApiTestEvent.class, new DhCancelableOneTimeTestEventHandler()); }); } - @Test - public void testEventDefinition() - { - ApiEventDefinitionHandler definitionHandler = ApiEventDefinitionHandler.INSTANCE; - - String errorMessagePrefix = "Missing " + DhApiEventDefinition.class.getSimpleName() + " for event class ["; - Assert.assertNotNull(errorMessagePrefix + DhApiTestEvent.class.getSimpleName() + "]", definitionHandler.getEventDefinition(DhApiTestEvent.class)); - Assert.assertNotNull(errorMessagePrefix + DhApiOneTimeTestEvent.class.getSimpleName() + "]", definitionHandler.getEventDefinition(DhApiOneTimeTestEvent.class)); - Assert.assertNotNull(errorMessagePrefix + DhApiAfterDhInitEvent.class.getSimpleName() + "]", definitionHandler.getEventDefinition(DhApiAfterDhInitEvent.class)); - } - @Test public void oneTimeEventTestPreFireBinding() { this.oneTimeEventTest(false); } @@ -134,33 +104,33 @@ public class EventInjectorTest // pre-dependency setup - Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(DhApiOneTimeTestEvent.class)); + Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(AbstractDhApiCancelableOneTimeTestEvent.class)); if (bindEventBeforeOneTimeFiring) { - TEST_EVENT_HANDLER.bind(DhApiOneTimeTestEvent.class, new DhOneTimeTestEventHandler()); + TEST_EVENT_HANDLER.bind(AbstractDhApiCancelableOneTimeTestEvent.class, new DhCancelableOneTimeTestEventHandler()); } TEST_EVENT_HANDLER.runDelayedSetup(); // pre-bound event firing - Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", bindEventBeforeOneTimeFiring, TEST_EVENT_HANDLER.fireAllEvents(DhApiOneTimeTestEvent.class, true)); + Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", bindEventBeforeOneTimeFiring, TEST_EVENT_HANDLER.fireAllEvents(AbstractDhApiCancelableOneTimeTestEvent.class, true)); // validate pre-bound events fired correctly - ArrayList oneTimeEventList; + ArrayList oneTimeEventList; if (bindEventBeforeOneTimeFiring) { - oneTimeEventList = TEST_EVENT_HANDLER.getAll(DhApiOneTimeTestEvent.class); + oneTimeEventList = TEST_EVENT_HANDLER.getAll(AbstractDhApiCancelableOneTimeTestEvent.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()); + TEST_EVENT_HANDLER.bind(AbstractDhApiCancelableOneTimeTestEvent.class, new DhCancelableOneTimeTestEventHandlerAlt()); // validate both events have fired - oneTimeEventList = TEST_EVENT_HANDLER.getAll(DhApiOneTimeTestEvent.class); + oneTimeEventList = TEST_EVENT_HANDLER.getAll(AbstractDhApiCancelableOneTimeTestEvent.class); for (int i = 0; i < oneTimeEventList.size(); i++) { Assert.assertEquals("Event not fired for object ["+i+"].", true, oneTimeEventList.get(i).getTestValue()); @@ -169,12 +139,55 @@ public class EventInjectorTest // recurring event test - TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandler()); - ArrayList recurringEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class); + TEST_EVENT_HANDLER.bind(AbstractDhApiTestEvent.class, new DhTestEventHandler()); + ArrayList recurringEventList = TEST_EVENT_HANDLER.getAll(AbstractDhApiTestEvent.class); Assert.assertNull("This unrelated recurring event shouldn't have been fired.", recurringEventList.get(0).getTestValue()); } + + @Test + public void testRemoveAfterFireEvents() + { + // Injector setup + ApiEventInjector TEST_EVENT_HANDLER = ApiEventInjector.INSTANCE; + + + // pre-dependency setup + Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(AbstractDhApiRemoveAfterFireTestEvent.class)); + + + // dependency setup + TEST_EVENT_HANDLER.bind(AbstractDhApiRemoveAfterFireTestEvent.class, new DhRemoveAfterFireTestEventHandler()); + TEST_EVENT_HANDLER.runDelayedSetup(); + + + // get first + AbstractDhApiRemoveAfterFireTestEvent event = TEST_EVENT_HANDLER.get(AbstractDhApiRemoveAfterFireTestEvent.class); + Assert.assertNotNull("Event not bound.", event); + + + // get list + ArrayList eventList = TEST_EVENT_HANDLER.getAll(AbstractDhApiRemoveAfterFireTestEvent.class); + Assert.assertEquals("Bound list doesn't contain the correct number of items.", 1, eventList.size()); + // object one + Assert.assertNotNull("Event not bound.", eventList.get(0)); + Assert.assertEquals("First event object setup incorrectly.", null, eventList.get(0).getTestValue()); + + + // event firing + Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", false, TEST_EVENT_HANDLER.fireAllEvents(AbstractDhApiRemoveAfterFireTestEvent.class, true)); + // object one + Assert.assertEquals("Event not fired for first object.", true, event.getTestValue()); + + + // check that the event was automatically unbound + eventList = TEST_EVENT_HANDLER.getAll(AbstractDhApiRemoveAfterFireTestEvent.class); + Assert.assertEquals("Unbound list doesn't contain the correct number of items.", 1, eventList.size()); + Assert.assertNull("Event wasn't automatically unbound after firing.", eventList.get(0)); + + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index f1f4cbb34..cef0c1e8f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -147,9 +147,6 @@ public class ClientApi { dhLevel.updateChunkAsync(chunk); } - - // TODO: potentially add a list of chunks that were updated during the save - ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelSaveEvent.class, new DhApiLevelSaveEvent.EventParam(level)); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 1058d1763..b6b8b8040 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.api.internal; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent; -import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelSaveEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.core.level.IDhLevel; @@ -132,11 +131,6 @@ public class ServerApi if (serverWorld != null) { serverWorld.saveAndFlush(); - - for (IDhLevel level : serverWorld.getAllLoadedLevels()) - { - ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelSaveEvent.class, new DhApiLevelSaveEvent.EventParam(level.getLevelWrapper())); - } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 2386eb636..c5b33de06 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -1,7 +1,6 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.google.common.collect.HashMultimap; -import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiDataFileChangedEvent; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; @@ -17,7 +16,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFull import com.seibel.distanthorizons.core.util.FileUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; -import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import org.apache.logging.log4j.Logger; import java.io.File; @@ -289,7 +287,6 @@ public class FullDataFileHandler implements IFullDataSourceProvider chunkPos = chunkPos.convertToDetailLevel((byte) this.minDetailLevel); this.writeChunkDataToMetaFile(new DhSectionPos(chunkPos.detailLevel, chunkPos.x, chunkPos.z), chunkDataView); - ApiEventInjector.INSTANCE.fireAllEvents(DhApiDataFileChangedEvent.class, new DhApiDataFileChangedEvent.EventParam(DhApiDataFileChangedEvent.EDataType.Full, (byte)(sectionPos.sectionDetailLevel - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), sectionPos.sectionX, sectionPos.sectionZ)); } private void writeChunkDataToMetaFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkData) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java index 81a01d119..10478093c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java @@ -1,7 +1,6 @@ package com.seibel.distanthorizons.core.file.renderfile; import com.google.common.collect.HashMultimap; -import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiDataFileChangedEvent; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -16,7 +15,6 @@ import com.seibel.distanthorizons.core.util.FileUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import org.apache.logging.log4j.Logger; import java.awt.*; @@ -248,9 +246,8 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider public void writeChunkDataToFile(DhSectionPos sectionPos, ChunkSizedFullDataAccessor chunkDataView) { // convert to the lowest detail level so all detail levels are updated - fastWriteDataToSourceRecursively(chunkDataView, DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); + this.fastWriteDataToSourceRecursively(chunkDataView, DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); this.fullDataSourceProvider.write(sectionPos, chunkDataView); - ApiEventInjector.INSTANCE.fireAllEvents(DhApiDataFileChangedEvent.class, new DhApiDataFileChangedEvent.EventParam(DhApiDataFileChangedEvent.EDataType.Render, (byte)(sectionPos.sectionDetailLevel - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), sectionPos.sectionX, sectionPos.sectionZ)); } private void fastWriteDataToSourceRecursively(ChunkSizedFullDataAccessor chunk, byte sectionDetailLevel) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java index 1f4f3301a..2c5dcef77 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java @@ -1,8 +1,10 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import java.util.concurrent.CompletableFuture; @@ -10,9 +12,7 @@ public abstract class DhLevel implements IDhLevel { public final ChunkToLodBuilder chunkToLodBuilder; - protected DhLevel() { - this.chunkToLodBuilder = new ChunkToLodBuilder(); - } + protected DhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); } public abstract void saveWrites(ChunkSizedFullDataAccessor data); @@ -28,12 +28,17 @@ public abstract class DhLevel implements IDhLevel { CompletableFuture future = this.chunkToLodBuilder.tryGenerateData(chunk); if (future != null) { - future.thenAccept(this::saveWrites); + future.thenAccept((chunkSizedFullDataAccessor) -> + { + this.saveWrites(chunkSizedFullDataAccessor); + ApiEventInjector.INSTANCE.fireAllEvents( + DhApiChunkModifiedEvent.class, + new DhApiChunkModifiedEvent.EventParam(this.getLevelWrapper(), chunk.getChunkPos().x, chunk.getChunkPos().z)); + }); } } @Override - public void close() { - chunkToLodBuilder.close(); - } + public void close() { this.chunkToLodBuilder.close(); } + }