Overhaul Dh API event definition system

Whether an event is a one time event or a cancelable event is now defined via their interface.
Also replace DhApiLevelSaveEvent with DhApiChunkModifiedEvent
This commit is contained in:
James Seibel
2023-06-23 23:07:41 -05:00
parent be38d82b26
commit 1dad6fd83f
35 changed files with 502 additions and 635 deletions
@@ -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<Void>
public abstract class DhApiAfterDhInitEvent implements IDhApiEvent<Void>, IDhApiOneTimeEvent<Void>
{
/** Fired after Distant Horizons finishes its initial setup on Minecraft startup. */
public abstract void afterDistantHorizonsInit();
public abstract void afterDistantHorizonsInit(DhApiEventParam<Void> input);
//=========================//
@@ -19,18 +21,6 @@ public abstract class DhApiAfterDhInitEvent implements IDhApiEvent<Void>
//=========================//
@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<Void> input) { this.afterDistantHorizonsInit(input); }
}
@@ -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. <br>
* 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<DhApiAfterRenderEvent.EventParam>
{
/** Fired after Distant Horizons finishes rendering fake chunks. */
public abstract void afterRender(EventParam input);
public abstract void afterRender(DhApiEventParam<EventParam> input);
//=========================//
@@ -20,19 +24,7 @@ public abstract class DhApiAfterRenderEvent implements IDhApiEvent<DhApiAfterRen
//=========================//
@Override
public final boolean fireEvent(EventParam input)
{
this.afterRender(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; }
public final void fireEvent(DhApiEventParam<EventParam> input) { this.afterRender(input); }
//==================//
@@ -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. <br>
* 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<Void>
{
/** Fired before Distant Horizons starts its initial setup on Minecraft startup. */
public abstract void beforeDistantHorizonsInit();
public abstract void beforeDistantHorizonsInit(DhApiEventParam<Void> input);
//=========================//
@@ -21,18 +21,6 @@ public abstract class DhApiBeforeDhInitEvent implements IDhApiEvent<Void>
//=========================//
@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<Void> input) { this.beforeDistantHorizonsInit(input); }
}
@@ -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. <br>
* 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<DhApiBeforeRenderEvent.EventParam>
public abstract class DhApiBeforeRenderEvent implements IDhApiCancelableEvent<DhApiBeforeRenderEvent.EventParam>
{
/**
* 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<EventParam> input);
//=========================//
@@ -24,15 +22,7 @@ public abstract class DhApiBeforeRenderEvent implements IDhApiEvent<DhApiBeforeR
//=========================//
@Override
public final boolean fireEvent(EventParam input) { return this.beforeRender(input); }
/**
* 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(true, false);
@Override
public final DhApiEventDefinition getEventDefinition() { return EVENT_DEFINITION; }
public final void fireEvent(DhApiCancelableEventParam<EventParam> input) { this.beforeRender(input); }
//==================//
@@ -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. <br>
* 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<DhApiChunkModifiedEvent.EventParam>
{
/** Fired after Distant Horizons saves LOD data for the server. */
public abstract void onChunkModified(DhApiEventParam<EventParam> input);
//=========================//
// internal DH API methods //
//=========================//
@Override
public final void fireEvent(DhApiEventParam<EventParam> 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;
}
}
}
@@ -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<DhApiDataFileChangedEvent.EventParam>
{
/**
* Fired after any data files handled by Distant Horizons are modified. <br>
* 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;
}
}
@@ -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. <br>
* 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<DhApiLevelLoadEvent.EventParam>
{
/** Fired after Distant Horizons loads a new level. */
public abstract void onLevelLoad(EventParam input);
public abstract void onLevelLoad(DhApiEventParam<EventParam> input);
//=========================//
@@ -20,19 +22,7 @@ public abstract class DhApiLevelLoadEvent implements IDhApiEvent<DhApiLevelLoadE
//=========================//
@Override
public final boolean fireEvent(EventParam input)
{
this.onLevelLoad(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; }
public final void fireEvent(DhApiEventParam<EventParam> input) { this.onLevelLoad(input); }
//==================//
@@ -44,7 +34,6 @@ public abstract class DhApiLevelLoadEvent implements IDhApiEvent<DhApiLevelLoadE
/** The newly loaded level. */
public final IDhApiLevelWrapper levelWrapper;
public EventParam(IDhApiLevelWrapper newLevelWrapper) { this.levelWrapper = newLevelWrapper; }
}
@@ -1,51 +0,0 @@
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;
/**
* @author James Seibel
* @version 2022-11-21
*/
public abstract class DhApiLevelSaveEvent implements IDhApiEvent<DhApiLevelSaveEvent.EventParam>
{
/** 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; }
}
}
@@ -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<DhApiLevelUnloadEvent.EventParam>
{
/** Fired before Distant Horizons unloads a level. */
public abstract void onLevelUnload(EventParam input);
public abstract void onLevelUnload(DhApiEventParam<EventParam> input);
//=========================//
@@ -20,19 +22,7 @@ public abstract class DhApiLevelUnloadEvent implements IDhApiEvent<DhApiLevelUnl
//=========================//
@Override
public final boolean fireEvent(EventParam input)
{
this.onLevelUnload(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; }
public final void fireEvent(DhApiEventParam<EventParam> input) { this.onLevelUnload(input); }
//==================//
@@ -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<T> extends IDhApiEvent<T>
{
void fireEvent(DhApiCancelableEventParam<T> input);
/**
* <strong> Shouldn't be called. </strong> <br><br>
*
* 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<T> 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<T>) input);
}
}
@@ -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 <T> 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<T> extends IBindable
{
@@ -25,28 +26,20 @@ public interface IDhApiEvent<T> 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. <br>
* 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<T> input);
}
@@ -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. <Br>
* An example of this would be initial setup methods, DH won't run its initial setup more than once. <br><br>
*
* If a handler is bound to a one time event after the event has been fired, the handler will immediately fire.
*/
public interface IDhApiOneTimeEvent<T> extends IDhApiEvent<T>
{
}
@@ -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<T> extends DhApiEventParam<T>
{
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; }
}
@@ -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<T>
{
/** Depending on the {@link IDhApiEvent} this can be null. */
public final T value;
public DhApiEventParam(T value) { this.value = value; }
}
@@ -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
@@ -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 <https://www.gnu.org/licenses/>.
*/
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. <Br>
* An example of this would be initial setup methods, DH won't run its initial setup more than once. <br><br>
*
* 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;
}
}
@@ -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<IDhApiEvent> 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<IDhApiEvent> implements
public void bind(Class<? extends IDhApiEvent> 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<IDhApiEvent> 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<IDhApiEvent> implements
}
@Override
public <T, U extends IDhApiEvent<T>> boolean fireAllEvents(Class<U> abstractEvent, T eventParameterObject)
public <T, U extends IDhApiEvent<T>> boolean fireAllEvents(Class<U> 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<U> eventList = this.getAll(abstractEvent);
for (IDhApiEvent<T> event : eventList) {
if (event != null) {
try {
boolean cancelEvent = false;
ArrayList<U> eventList = this.getAll(abstractEventClass);
ArrayList<IDhApiEvent<T>> eventsToRemove = new ArrayList<>();
for (IDhApiEvent<T> 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<T> eventParam = createEventParamWrapper(event, eventInput);
event.fireEvent(eventParam);
if (eventParam instanceof DhApiCancelableEventParam)
{
DhApiCancelableEventParam<T> cancelableEventParam = (DhApiCancelableEventParam<T>) 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<T> eventToRemove : eventsToRemove)
{
this.unbind(abstractEventClass, eventToRemove.getClass());
}
return cancelEvent;
}
public static <T> DhApiEventParam<T> createEventParamWrapper(IDhApiEvent<T> event, T parameter)
{
return (event instanceof IDhApiCancelableEvent) ? new DhApiCancelableEventParam<>(parameter) : new DhApiEventParam<>(parameter);
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
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<Class<? extends IDhApiEvent>, 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<? extends IDhApiEvent> 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<? extends IDhApiEvent> 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);
}
}
@@ -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<Boolean>, IDhApiOneTimeEvent<Boolean>
{
public abstract void onTestEvent(DhApiCancelableEventParam<Boolean> input);
/** just used for testing */
public abstract Boolean getTestValue();
//=========================//
// internal DH API methods //
//=========================//
@Override
public final void fireEvent(DhApiCancelableEventParam<Boolean> input)
{
this.onTestEvent(input);
if (input.value)
{
input.cancelEvent();
}
}
}
@@ -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<Boolean>
{
public abstract void onTestEvent(DhApiEventParam<Boolean> 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<Boolean> input) { this.onTestEvent(input); }
}
@@ -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<Boolean>
{
public abstract void onTestEvent(DhApiEventParam<Boolean> input);
/** just used for testing */
public abstract Boolean getTestValue();
//=========================//
// internal DH API methods //
//=========================//
@Override
public final void fireEvent(DhApiEventParam<Boolean> input) { this.onTestEvent(input); }
}
@@ -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<Boolean>
{
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; }
}
@@ -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<Boolean>
{
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; }
}
@@ -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<Boolean> input) { this.eventFiredValue = input.value; }
// test (non standard) methods //
@Override
public Boolean getTestValue() { return this.eventFiredValue; }
}
@@ -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
}
@@ -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; }
}
@@ -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<Boolean> input) { this.eventFiredValue = input.value; }
// test (non standard) methods //
@Override
public Boolean getTestValue() { return this.eventFiredValue; }
}
@@ -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<Boolean> input) { this.eventFiredValue = input.value; }
// test (non standard) methods //
@Override
public Boolean getTestValue() { return eventFiredValue; }
public Boolean getTestValue() { return this.eventFiredValue; }
}
@@ -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<Boolean> input) { this.eventFiredValue = input.value; }
// test (non standard) methods //
@Override
public Boolean getTestValue() { return eventFiredValue; }
public Boolean getTestValue() { return this.eventFiredValue; }
}
+85 -72
View File
@@ -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<DhApiTestEvent> afterRenderEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class);
ArrayList<AbstractDhApiTestEvent> 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<DhApiOneTimeTestEvent> oneTimeEventList;
ArrayList<AbstractDhApiCancelableOneTimeTestEvent> 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<DhApiTestEvent> recurringEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class);
TEST_EVENT_HANDLER.bind(AbstractDhApiTestEvent.class, new DhTestEventHandler());
ArrayList<AbstractDhApiTestEvent> 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<AbstractDhApiRemoveAfterFireTestEvent> 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));
}
}
@@ -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));
}
}
@@ -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()));
}
}
}
@@ -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)
{
@@ -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)
@@ -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<ChunkSizedFullDataAccessor> 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(); }
}