Add one time API events (IE setup events)

This allows users to bind to setup events after they've happened and still have the event fire. This is useful for setup since mod load order isn't defined.
This commit is contained in:
James Seibel
2022-11-21 19:26:14 -06:00
parent 1b5a10591b
commit 6ca2da3f7f
19 changed files with 467 additions and 77 deletions
@@ -1,15 +1,40 @@
package com.seibel.lod.api.methods.events.abstractEvents;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* @author James Seibel
* @version 2022-9-6
* @version 2022-11-21
*/
public abstract class DhApiAfterDhInitEvent
implements IDhApiEvent<Void>
public abstract class DhApiAfterDhInitEvent implements IDhApiEvent<Void>
{
/** Fired after Distant Horizons finishes its initial setup on Minecraft startup. */
public abstract void afterDistantHorizonsInit();
//=========================//
// internal DH API methods //
//=========================//
@Override
public final boolean fireEvent(Void ignoredParam)
{
this.afterDistantHorizonsInit();
return false;
}
private static boolean firstTimeSetupComplete = false;
public DhApiAfterDhInitEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiAfterDhInitEvent.class, new DhApiEventDefinition(false, true));
}
}
@Override
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiAfterDhInitEvent.class); }
}
@@ -2,14 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.lod.core.util.math.Mat4f;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* @author James Seibel
* @version 2022-9-6
* @version 2022-11-21
*/
public abstract class DhApiAfterRenderEvent
implements IDhApiEvent<DhApiAfterRenderEvent.EventParam>
public abstract class DhApiAfterRenderEvent implements IDhApiEvent<DhApiAfterRenderEvent.EventParam>
{
/** Fired after Distant Horizons finishes rendering fake chunks. */
public abstract void afterRender(EventParam input);
@@ -22,12 +22,21 @@ public abstract class DhApiAfterRenderEvent
@Override
public final boolean fireEvent(EventParam input)
{
afterRender(input);
this.afterRender(input);
return false;
}
private static boolean firstTimeSetupComplete = false;
public DhApiAfterRenderEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiAfterRenderEvent.class, new DhApiEventDefinition(false, false));
}
}
@Override
public final boolean getCancelable() { return false; }
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiAfterRenderEvent.class); }
//==================//
@@ -3,13 +3,14 @@ package com.seibel.lod.api.methods.events.abstractEvents;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* @author James Seibel
* @version 2022-9-6
* @version 2022-11-21
*/
public abstract class DhApiBeforeDhInitEvent
implements IDhApiEvent<Void>
public abstract class DhApiBeforeDhInitEvent implements IDhApiEvent<Void>
{
/** Fired before Distant Horizons starts its initial setup on Minecraft startup. */
public abstract void beforeDistantHorizonsInit();
@@ -22,11 +23,20 @@ public abstract class DhApiBeforeDhInitEvent
@Override
public final boolean fireEvent(Void ignoredParam)
{
beforeDistantHorizonsInit();
this.beforeDistantHorizonsInit();
return false;
}
private static boolean firstTimeSetupComplete = false;
public DhApiBeforeDhInitEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiBeforeDhInitEvent.class, new DhApiEventDefinition(false, true));
}
}
@Override
public final boolean getCancelable() { return false; }
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiBeforeDhInitEvent.class); }
}
@@ -2,14 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.lod.core.util.math.Mat4f;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* @author James Seibel
* @version 2022-9-6
* @version 2022-11-21
*/
public abstract class DhApiBeforeRenderEvent
implements IDhApiEvent<DhApiBeforeRenderEvent.EventParam>
public abstract class DhApiBeforeRenderEvent implements IDhApiEvent<DhApiBeforeRenderEvent.EventParam>
{
/**
* Fired before Distant Horizons renders fake chunks.
@@ -24,10 +24,19 @@ public abstract class DhApiBeforeRenderEvent
//=========================//
@Override
public final boolean fireEvent(EventParam input) { return beforeRender(input); }
public final boolean fireEvent(EventParam input) { return this.beforeRender(input); }
private static boolean firstTimeSetupComplete = false;
public DhApiBeforeRenderEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiBeforeRenderEvent.class, new DhApiEventDefinition(true, false));
}
}
@Override
public final boolean getCancelable() { return true; }
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiBeforeRenderEvent.class); }
//==================//
@@ -2,13 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents;
import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* @author James Seibel
* @version 2022-9-10
* @version 2022-11-21
*/
public abstract class DhApiLevelLoadEvent
implements IDhApiEvent<DhApiLevelLoadEvent.EventParam>
public abstract class DhApiLevelLoadEvent implements IDhApiEvent<DhApiLevelLoadEvent.EventParam>
{
/** Fired after Distant Horizons loads a new level. */
public abstract void onLevelLoad(EventParam input);
@@ -21,12 +22,21 @@ public abstract class DhApiLevelLoadEvent
@Override
public final boolean fireEvent(EventParam input)
{
onLevelLoad(input);
this.onLevelLoad(input);
return false;
}
private static boolean firstTimeSetupComplete = false;
public DhApiLevelLoadEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiLevelLoadEvent.class, new DhApiEventDefinition(false, false));
}
}
@Override
public final boolean getCancelable() { return false; }
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiLevelLoadEvent.class); }
//==================//
@@ -2,13 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents;
import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* @author James Seibel
* @version 2022-9-10
* @version 2022-11-21
*/
public abstract class DhApiLevelSaveEvent
implements IDhApiEvent<DhApiLevelSaveEvent.EventParam>
public abstract class DhApiLevelSaveEvent implements IDhApiEvent<DhApiLevelSaveEvent.EventParam>
{
/** Fired after Distant Horizons saves LOD data for the server. */
public abstract void onLevelSave(EventParam input);
@@ -21,12 +22,21 @@ public abstract class DhApiLevelSaveEvent
@Override
public final boolean fireEvent(EventParam input)
{
onLevelSave(input);
this.onLevelSave(input);
return false;
}
private static boolean firstTimeSetupComplete = false;
public DhApiLevelSaveEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiLevelSaveEvent.class, new DhApiEventDefinition(false, false));
}
}
@Override
public final boolean getCancelable() { return false; }
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiLevelSaveEvent.class); }
//==================//
@@ -2,13 +2,14 @@ package com.seibel.lod.api.methods.events.abstractEvents;
import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* @author James Seibel
* @version 2022-9-10
* @version 2022-11-21
*/
public abstract class DhApiLevelUnloadEvent
implements IDhApiEvent<DhApiLevelUnloadEvent.EventParam>
public abstract class DhApiLevelUnloadEvent implements IDhApiEvent<DhApiLevelUnloadEvent.EventParam>
{
/** Fired before Distant Horizons unloads a level. */
public abstract void onLevelUnload(EventParam input);
@@ -21,12 +22,21 @@ public abstract class DhApiLevelUnloadEvent
@Override
public final boolean fireEvent(EventParam input)
{
onLevelUnload(input);
this.onLevelUnload(input);
return false;
}
private static boolean firstTimeSetupComplete = false;
public DhApiLevelUnloadEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiLevelUnloadEvent.class, new DhApiEventDefinition(false, false));
}
}
@Override
public final boolean getCancelable() { return false; }
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiLevelUnloadEvent.class); }
//==================//
@@ -1,15 +1,15 @@
package com.seibel.lod.api.methods.events.interfaces;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.interfaces.dependencyInjection.IBindable;
/**
* A combination of all interfaces required by all
* DH Api events.
*
* The interface used by all DH Api events.
*
* @param <T> This is the datatype that will be passed into the event handler's method.
*
*
* @author James Seibel
* @version 2022-9-6
* @version 2022-11-20
*/
public interface IDhApiEvent<T> extends IBindable
{
@@ -18,11 +18,12 @@ public interface IDhApiEvent<T> extends IBindable
//==========//
/**
* Returns if the event should be automatically unbound
* Returns true if the event should be automatically unbound
* after firing. <br>
* Can be useful for one time setup events or waiting for a specific game state. <br> <Br>
*
* Defaults to False (the event will not be removed after firing).
* Defaults to False
* IE: The event will not be removed after firing and will continue firing until removed.
*/
default boolean removeAfterFiring() { return false; };
@@ -31,8 +32,11 @@ public interface IDhApiEvent<T> extends IBindable
// internal //
//==========//
/** Returns true if the event can be canceled. */
boolean getCancelable();
/**
* The event definition includes meta information about how the event will behave. <br>
* For example: if the event is cancelable or not.
*/
DhApiEventDefinition getEventDefinition();
/**
* Called internally by Distant Horizons when the event happens.
@@ -0,0 +1,55 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.api.objects.events;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
/**
* The event definition includes meta information about how the event will behave.
*
* @author James Seibel
* @version 2022-11-20
*/
public class DhApiEventDefinition
{
/** True if the event can be canceled. */
public final boolean isCancelable;
/**
* True if the event will only ever be fired once. <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,22 +20,25 @@
package com.seibel.lod.core.DependencyInjection;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
import com.seibel.lod.core.interfaces.dependencyInjection.IBindable;
import com.seibel.lod.core.interfaces.dependencyInjection.IDhApiEventInjector;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashMap;
/**
* This class takes care of dependency injection for API events.
*
* @author James Seibel
* @version 2022-9-13
* @version 2022-11-20
*/
public class DhApiEventInjector extends DependencyInjector<IDhApiEvent> implements IDhApiEventInjector // Note to self: Don't try adding a generic type to IDhApiEvent, the consturctor won't accept it
{
private static final Logger LOGGER = LogManager.getLogger(DhApiEventInjector.class.getSimpleName());
private static final HashMap<Class<? extends IDhApiEvent>, Object> FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE = new HashMap<>();
public static final DhApiEventInjector INSTANCE = new DhApiEventInjector();
@@ -44,34 +47,56 @@ public class DhApiEventInjector extends DependencyInjector<IDhApiEvent> implemen
private DhApiEventInjector() { super(IDhApiEvent.class, true); }
@Override
public boolean unbind(Class<? extends IDhApiEvent> dependencyInterface, Class<? extends IDhApiEvent> dependencyClassToRemove) throws IllegalArgumentException
public void bind(Class<? extends IDhApiEvent> eventInterface, IDhApiEvent eventImplementation) throws IllegalStateException, IllegalArgumentException
{
// is this a one time event?
if (ApiEventDefinitionHandler.getEventDefinition(eventInterface).isOneTimeEvent)
{
// has this one time event been fired yet?
if (FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.containsKey(eventInterface))
{
// the one time event has happened, fire the handler
// this has to be an unsafe cast since the hash map can't hold the generic objects
Object parameter = FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.get(eventInterface);
eventImplementation.fireEvent(parameter);
}
}
// bind the event handler
super.bind(eventInterface, eventImplementation);
}
@Override
public boolean unbind(Class<? extends IDhApiEvent> eventInterface, Class<? extends IDhApiEvent> eventClassToRemove) throws IllegalArgumentException
{
// make sure the given dependency implements the necessary interfaces
boolean implementsInterface = checkIfClassImplements(dependencyClassToRemove, dependencyInterface)
|| checkIfClassExtends(dependencyClassToRemove, dependencyInterface);
boolean implementsBindable = checkIfClassImplements(dependencyClassToRemove, this.bindableInterface);
boolean implementsInterface = this.checkIfClassImplements(eventClassToRemove, eventInterface) ||
this.checkIfClassExtends(eventClassToRemove, eventInterface);
boolean implementsBindable = this.checkIfClassImplements(eventClassToRemove, this.bindableInterface);
// display any errors
if (!implementsInterface)
{
throw new IllegalArgumentException("The event handler [" + dependencyClassToRemove.getSimpleName() + "] doesn't implement or extend: [" + dependencyInterface.getSimpleName() + "].");
throw new IllegalArgumentException("The event handler [" + eventClassToRemove.getSimpleName() + "] doesn't implement or extend: [" + eventInterface.getSimpleName() + "].");
}
if (!implementsBindable)
{
throw new IllegalArgumentException("The event handler [" + dependencyClassToRemove.getSimpleName() + "] doesn't implement the interface: [" + IBindable.class.getSimpleName() + "].");
throw new IllegalArgumentException("The event handler [" + eventClassToRemove.getSimpleName() + "] doesn't implement the interface: [" + IBindable.class.getSimpleName() + "].");
}
// actually remove the dependency
if (this.dependencies.containsKey(dependencyInterface))
if (this.dependencies.containsKey(eventInterface))
{
ArrayList<IDhApiEvent> dependencyList = this.dependencies.get(dependencyInterface);
ArrayList<IDhApiEvent> dependencyList = this.dependencies.get(eventInterface);
int indexToRemove = -1;
for(int i = 0; i < dependencyList.size(); i++)
{
IBindable dependency = dependencyList.get(i);
if (dependency.getClass().equals(dependencyClassToRemove))
if (dependency.getClass().equals(eventClassToRemove))
{
indexToRemove = i;
break;
@@ -89,11 +114,20 @@ public class DhApiEventInjector extends DependencyInjector<IDhApiEvent> implemen
}
@Override
public <T, U extends IDhApiEvent<T>> boolean fireAllEvents(Class<U> dependencyInterface, T eventParameterObject)
public <T, U extends IDhApiEvent<T>> boolean fireAllEvents(Class<U> eventInterface, T eventParameterObject)
{
boolean cancelEvent = false;
ArrayList<U> eventList = this.getAll(dependencyInterface);
// if this is a one time event, record it
if (ApiEventDefinitionHandler.getEventDefinition(eventInterface).isOneTimeEvent &&
!FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.containsKey(eventInterface))
{
FIRED_ONE_TIME_EVENT_PARAMETERS_BY_EVENT_INTERFACE.put(eventInterface, eventParameterObject);
}
// fire each bound event
ArrayList<U> eventList = this.getAll(eventInterface);
for (IDhApiEvent<T> event : eventList)
{
if (event != null)
@@ -106,8 +140,7 @@ public class DhApiEventInjector extends DependencyInjector<IDhApiEvent> implemen
}
catch (Exception e)
{
// TODO log the failed event handler
LOGGER.error("Exception thrown by event handler :" + e.getMessage(), e);
LOGGER.error("Exception thrown by event handler [" + event.getClass().getSimpleName() + "] for event type [" + eventInterface.getSimpleName() + "], error:" + e.getMessage(), e);
}
}
}
@@ -0,0 +1,71 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.core.events;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
/**
* Holds the API event definitions
* so, they can be accessed without an
* event instance.
*
* @author James Seibel
* @version 2022-11-21
*/
public class ApiEventDefinitionHandler
{
private static final Logger LOGGER = LogManager.getLogger(ApiEventDefinitionHandler.class.getSimpleName());
// note to self: don't try adding a generic interface type here, the event dependency handler's constructor method won't accept it
private static final HashMap<Class<? extends IDhApiEvent>, DhApiEventDefinition> DEFINITIONS_BY_EVENT_INTERFACE = new HashMap<>();
public ApiEventDefinitionHandler INSTANCE = new ApiEventDefinitionHandler();
private ApiEventDefinitionHandler() { }
public static void setEventDefinition(Class<? extends IDhApiEvent> eventInterface, DhApiEventDefinition definition)
{
if (DEFINITIONS_BY_EVENT_INTERFACE.containsKey(eventInterface))
{
LOGGER.warn("duplicate key added [" + eventInterface.getSimpleName() + "]");
}
DEFINITIONS_BY_EVENT_INTERFACE.put(eventInterface, definition);
}
public static DhApiEventDefinition getEventDefinition(Class<? extends IDhApiEvent> eventInterface)
{
if (!DEFINITIONS_BY_EVENT_INTERFACE.containsKey(eventInterface))
{
throw new NullPointerException("event definition missing for: [" + eventInterface.getSimpleName() + "]");
}
return DEFINITIONS_BY_EVENT_INTERFACE.get(eventInterface);
}
}
@@ -0,0 +1,46 @@
package testItems.events.abstractObjects;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* A dummy event implementation used for unit testing.
*
* @author James Seibel
* @version 2022-11-20
*/
public abstract class DhApiOneTimeTestEvent implements IDhApiEvent<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;
}
private static boolean firstTimeSetupComplete = false;
public DhApiOneTimeTestEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiOneTimeTestEvent.class, new DhApiEventDefinition(false, true));
}
}
@Override
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiOneTimeTestEvent.class); }
}
@@ -1,15 +1,16 @@
package testItems.events.abstractObjects;
import com.seibel.lod.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.lod.api.objects.events.DhApiEventDefinition;
import com.seibel.lod.core.events.ApiEventDefinitionHandler;
/**
* A dummy event implementation used for unit testing.
*
* @author James Seibel
* @version 2022-9-13
* @version 2022-11-20
*/
public abstract class DhApiTestEvent
implements IDhApiEvent<Boolean>
public abstract class DhApiTestEvent implements IDhApiEvent<Boolean>
{
public abstract void onTestEvent(Boolean input);
@@ -26,11 +27,21 @@ public abstract class DhApiTestEvent
@Override
public final boolean fireEvent(Boolean input)
{
onTestEvent(input);
this.onTestEvent(input);
return input;
}
private static boolean firstTimeSetupComplete = false;
public DhApiTestEvent()
{
if (!firstTimeSetupComplete)
{
firstTimeSetupComplete = true;
ApiEventDefinitionHandler.setEventDefinition(DhApiTestEvent.class, new DhApiEventDefinition(false, false));
}
}
@Override
public final boolean getCancelable() { return false; }
public final DhApiEventDefinition getEventDefinition() { return ApiEventDefinitionHandler.getEventDefinition(DhApiTestEvent.class); }
}
@@ -0,0 +1,26 @@
package testItems.events.objects;
import testItems.events.abstractObjects.DhApiOneTimeTestEvent;
/**
* Dummy test event for unit tests.
*
* @author James Seibel
* @version 2022-11-20
*/
public class DhOneTimeTestEventHandler extends DhApiOneTimeTestEvent
{
public Boolean eventFiredValue = null;
@Override
public void onTestEvent(Boolean input) { this.eventFiredValue = input; }
@Override
public boolean removeAfterFiring() { return false; }
// test (non standard) methods //
@Override
public Boolean getTestValue() { return this.eventFiredValue; }
}
@@ -0,0 +1,12 @@
package testItems.events.objects;
/**
* Dummy test event for unit tests.
*
* @author James Seibel
* @version 2022-11-20
*/
public class DhOneTimeTestEventHandlerAlt extends DhOneTimeTestEventHandler
{
// all other code should be a duplicate of the parent
}
@@ -8,7 +8,7 @@ import testItems.events.abstractObjects.DhApiTestEvent;
* @author James Seibel
* @version 2022-9-11
*/
public class DhTestEvent extends DhApiTestEvent
public class DhTestEventHandler extends DhApiTestEvent
{
public Boolean eventFiredValue = null;
@@ -8,7 +8,7 @@ import testItems.events.abstractObjects.DhApiTestEvent;
* @author James Seibel
* @version 2022-9-11
*/
public class DhTestEventAlt extends DhApiTestEvent
public class DhTestEventHandlerAlt extends DhApiTestEvent
{
public Boolean eventFiredValue = null;
+61 -11
View File
@@ -3,34 +3,38 @@ package tests;
import com.seibel.lod.core.DependencyInjection.DhApiEventInjector;
import org.junit.Assert;
import org.junit.Test;
import testItems.events.abstractObjects.DhApiOneTimeTestEvent;
import testItems.events.objects.DhOneTimeTestEventHandler;
import testItems.events.objects.DhOneTimeTestEventHandlerAlt;
import testItems.events.abstractObjects.DhApiTestEvent;
import testItems.events.objects.DhTestEvent;
import testItems.events.objects.DhTestEventAlt;
import testItems.events.objects.DhTestEventHandler;
import testItems.events.objects.DhTestEventHandlerAlt;
import java.util.ArrayList;
/**
* @author James Seibel
* @version 2022-9-13
* @version 2022-11-21
*/
public class EventInjectorTest
{
@Test
public void testEventDependencies() // this also tests list dependencies since there can be more than one event handler bound per event
public void testGeneralAndRecurringEvents() // this also tests list dependencies since there can be more than one event handler bound per event
{
// Injector setup
DhApiEventInjector TEST_EVENT_HANDLER = DhApiEventInjector.INSTANCE;
TEST_EVENT_HANDLER.clear();
// pre-dependency setup
Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(DhApiTestEvent.class));
// dependency setup
TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEvent());
TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventAlt());
TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandler());
TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandlerAlt());
TEST_EVENT_HANDLER.runDelayedSetup();
@@ -60,8 +64,8 @@ public class EventInjectorTest
// unbind
DhApiTestEvent unboundEvent = afterRenderEventList.get(0);
Assert.assertTrue("Unbind should've removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEvent.class));
Assert.assertFalse("Unbind should've already removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEvent.class));
Assert.assertTrue("Unbind should've removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEventHandler.class));
Assert.assertFalse("Unbind should've already removed item.", TEST_EVENT_HANDLER.unbind(DhApiTestEvent.class, DhTestEventHandler.class));
// check unbinding
afterRenderEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class);
@@ -72,10 +76,56 @@ public class EventInjectorTest
// check unbound event firing
Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", false, TEST_EVENT_HANDLER.fireAllEvents(DhApiTestEvent.class, false));
// remaining event
Assert.assertEquals("Event not fired for remaining object.", false, ((DhTestEventAlt) afterRenderEventList.get(0)).eventFiredValue);
Assert.assertEquals("Event not fired for remaining object.", false, ((DhTestEventHandlerAlt) afterRenderEventList.get(0)).eventFiredValue);
// unbound event
Assert.assertEquals("Event fired for unbound object.", true, unboundEvent.getTestValue());
// prevent event handlers from being bound to the wrong event interface
Assert.assertThrows("Event bound to a non-implementing interface.", IllegalArgumentException.class, () -> { TEST_EVENT_HANDLER.bind(DhApiOneTimeTestEvent.class, new DhTestEventHandler()); });
Assert.assertThrows("Event bound to a non-implementing interface.", IllegalArgumentException.class, () -> { TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhOneTimeTestEventHandler()); });
}
@Test
public void testOneTimeEventFiring()
{
// Injector setup
DhApiEventInjector TEST_EVENT_HANDLER = DhApiEventInjector.INSTANCE;
TEST_EVENT_HANDLER.clear();
// pre-dependency setup
Assert.assertNull("Nothing should have been bound.", TEST_EVENT_HANDLER.get(DhApiOneTimeTestEvent.class));
// pre-event fire binding
TEST_EVENT_HANDLER.bind(DhApiOneTimeTestEvent.class, new DhOneTimeTestEventHandler());
TEST_EVENT_HANDLER.runDelayedSetup();
// pre-bound event firing
Assert.assertEquals("fireAllEvents canceled returned canceled incorrectly.", true, TEST_EVENT_HANDLER.fireAllEvents(DhApiOneTimeTestEvent.class, true));
// validate event fired correctly
ArrayList<DhApiOneTimeTestEvent> oneTimeEventList = TEST_EVENT_HANDLER.getAll(DhApiOneTimeTestEvent.class);
Assert.assertEquals("Event not fired for pre-fire object.", true, oneTimeEventList.get(0).getTestValue());
// post-event fire binding
// the event should fire instantly
TEST_EVENT_HANDLER.bind(DhApiOneTimeTestEvent.class, new DhOneTimeTestEventHandlerAlt());
// validate both events have fired
oneTimeEventList = TEST_EVENT_HANDLER.getAll(DhApiOneTimeTestEvent.class);
Assert.assertEquals("Event not fired for pre-fire object.", true, oneTimeEventList.get(0).getTestValue());
Assert.assertEquals("Event not fired for post-fire object.", true, oneTimeEventList.get(1).getTestValue());
// recurring event test
TEST_EVENT_HANDLER.bind(DhApiTestEvent.class, new DhTestEventHandler());
ArrayList<DhApiTestEvent> recurringEventList = TEST_EVENT_HANDLER.getAll(DhApiTestEvent.class);
Assert.assertNull("This unrealted recurring event shouldn't have been fired.", recurringEventList.get(0).getTestValue());
}
}
@@ -22,7 +22,6 @@ package com.seibel.lod.core.api.internal;
import com.seibel.lod.api.methods.events.abstractEvents.*;
import com.seibel.lod.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.lod.core.DependencyInjection.DhApiEventInjector;
import com.seibel.lod.core.api.external.methods.data.DhApiTerrainDataRepo;
import com.seibel.lod.core.level.IDhClientLevel;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.ModInfo;