Create Lifetime system for managing Listeners with a defined duration.

This commit introduces a Component system focused around Games, which is completely
backwards compatible, as well as designed to be flexible enough for later improvements
such as dependency injection. Each GameState is associated with a phase of the
PhasedLifetime that each Game has. Components can be registered with a specific phased
or the entirety of the Lifetime. Refer to the javadocs for Lifetime and PhasedLifetime
for more details.  Currently the main two Component types are ICommand and
ListenerComponent.

This commit includes the first refactoring into using this system, which is replacing
the Wizards minigame's usage of Miniplugin with a Lifetimed Component, allowing for
the shop to be appropriately registered and unregistered.  This change allows for
Wizards to be run more than once on the same server instance.  Previously, attempting
to register the Miniplugin twice would result in the minigame failing to start after
the initial registration.

This commit additionally includes slight refactoring within GameCreationManager as
required for the Lifetime system to function correctly.  These changes ensure that
Games are only disabled once, whereas before they could be repeatedly disabled.
The previous implementation of disable(), along with the classes that override it,
functioned correctly despite being called several times.

Finally, this commit adds in changes to the pom to allow for unit testing.
This commit is contained in:
Nate Mortensen 2016-11-10 10:50:38 -07:00 committed by cnr
parent 062fdd31b2
commit 833c52d0f2
22 changed files with 848 additions and 71 deletions

View File

@ -45,6 +45,11 @@
<artifactId>anticheat</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -7,6 +7,9 @@ import mineplex.core.common.util.NautHashMap;
import mineplex.core.common.util.UtilServer;
import mineplex.core.common.util.UtilTime;
import mineplex.core.common.util.UtilTime.TimeUnit;
import mineplex.core.lifetimes.Lifetime;
import mineplex.core.lifetimes.Lifetimed;
import mineplex.core.lifetimes.SimpleLifetime;
import mineplex.core.thread.ThreadPool;
import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
@ -26,12 +29,16 @@ import org.bukkit.scheduler.BukkitTask;
*
* This way, we can reflectively create them during {@link #require} when they do not exist, leading to much cleaner code
*/
public abstract class MiniPlugin implements Listener
public abstract class MiniPlugin implements Listener, Lifetimed
{
// As MiniPlugins can technically be disabled at any time, each
// has its own unique Lifetime. If MiniPlugins are declared
// to never be able to be disabled, then a "Singleton" Lifetime
// could be shared between all of them.
private final SimpleLifetime _lifetime = new SimpleLifetime();
protected String _moduleName = "Default";
protected JavaPlugin _plugin;
protected NautHashMap<String, ICommand> _commands;
protected long _initializedTime;
public MiniPlugin(String moduleName)
@ -46,8 +53,7 @@ public abstract class MiniPlugin implements Listener
_initializedTime = System.currentTimeMillis();
_commands = new NautHashMap<>();
_lifetime.start();
onEnable();
registerEvents(this);
@ -171,4 +177,10 @@ public abstract class MiniPlugin implements Listener
{
return Managers.require(clazz);
}
@Override
public Lifetime getLifetime()
{
return _lifetime;
}
}

View File

@ -5,10 +5,11 @@ import java.util.List;
import mineplex.core.common.Rank;
import mineplex.core.lifetimes.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public interface ICommand
public interface ICommand extends Component
{
void SetCommandCenter(CommandCenter commandCenter);
void Execute(Player caller, String[] args);
@ -21,4 +22,16 @@ public interface ICommand
Rank[] GetSpecificRanks();
List<String> onTabComplete(CommandSender sender, String commandLabel, String[] args);
@Override
default void activate()
{
CommandCenter.Instance.addCommand(this);
}
@Override
default void deactivate()
{
CommandCenter.Instance.removeCommand(this);
}
}

View File

@ -0,0 +1,21 @@
package mineplex.core.lifetimes;
/**
* A Component defines behavior that can exist within a Lifetime. Components
* should have no impact upon the game while not active.
*/
public interface Component
{
/**
* Activates the Component, performing any sort of required initialization.
* Components may be activated and deactivated multiple times, however a component
* will not be activated more than once without subsequent calls to deactivate.
*/
void activate();
/**
* Deactivates the Component, disabling any sort of functionality it provides
* and performing clean up. A Component may be subsequently reactivated.
*/
void deactivate();
}

View File

@ -0,0 +1,29 @@
package mineplex.core.lifetimes;
/**
* A Lifetime represents a duration for which a collection of Components
* will be active. While Lifetime does contain a method for registering
* instantiated Components, individual Lifetimes may have unique
* strategies for creating Components and activating them. Lifetime
* doesn't provide any guarantee of Component activation or deactivation
* order. Implementations of Lifetime, however, may.
* <p />
* Lifetime doesn't provide mechanisms for beginning or ending a Lifetime.
* This is provided by the various implementations of Lifetime, as it varies
* between the implementations and is functionality that most consumers of
* Lifetimes will not need.
*/
public interface Lifetime
{
/**
* Registers the provided component with this Lifetime. If the Lifetime
* is currently active, then the Component will be immediately activated.
* @param component the component to register
*/
void register(Component component);
/**
* Gets whether the Lifetime is currently active.
* @return true if the Lifetime is active
*/
boolean isActive();
}

View File

@ -0,0 +1,19 @@
package mineplex.core.lifetimes;
/**
* Represents an object that is associated with a specific lifetime.
* Multiple Lifetimed objects may be associated with the same Lifetime.
* As a roughly generalized explanation, any time functionality should
* be enabled(whether its a command, listener, etc.) it should be registered
* as a Component of a Lifetime. Any object wishing to enable functionality
* that is associated with this Lifetimed object should do so with the Lifetime
* returned by {@link #getLifetime()}.
*/
public interface Lifetimed
{
/**
* Gets the Lifetime associated with this Lifetimed object.
* @return non-null Lifetime
*/
Lifetime getLifetime();
}

View File

@ -0,0 +1,54 @@
package mineplex.core.lifetimes;
import mineplex.core.common.util.UtilServer;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
/**
* A convenience class for Components that are a Listener. ListenerComponent
* can either be used as a wrapper class for a specific Listener, or can be
* extended by another Component to provide event registration.
*/
public class ListenerComponent implements Component, Listener
{
private final Listener _listener;
/**
* Creates a ListenerComponent that registers the provided Listener when
* activated and unregisters it when deactivated. The newly created
* ListenerComponent will not be registered as a Listener. When a
* ListenerComponent is created with this constructor, it is effectively
* a wrapper to bind a Listener to a specific lifetime.
*
* @param listener non-null listener to wrap
* @throws IllegalArgumentException if listener is null
*/
public ListenerComponent(Listener listener) throws IllegalArgumentException
{
Validate.notNull(listener);
_listener = listener;
}
/**
* Creates a ListenerComponent that registers itself when activated and
* unregisters itself when deactivated.
*/
public ListenerComponent()
{
_listener = this;
}
@Override
public void activate()
{
Bukkit.getPluginManager().registerEvents(_listener, UtilServer.getPlugin());
}
@Override
public void deactivate()
{
HandlerList.unregisterAll(_listener);
}
}

View File

@ -0,0 +1,7 @@
package mineplex.core.lifetimes;
public interface PhasedComponent<T> extends Component
{
void setPhase(T phase);
}

View File

@ -0,0 +1,289 @@
package mineplex.core.lifetimes;
import com.google.common.base.Preconditions;
import mineplex.core.common.util.UtilServer;
import org.apache.commons.lang.Validate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* A PhasedLifetime is a lifetime that is composed of several
* smaller lifetimes, referred to as phases. This class is provided
* in order to support a system in which Components may exist within multiple
* Lifetimes. PhasedLifetime is not thread-safe.
* <p />
* Registering a Component will register it for the entirety of this Lifetime,
* unless registered with {@link #register(Component, Iterable)}. Special behavior
* is provided for instances of {@link PhasedComponent}. See {@link #register(Component,
* Iterable)} for more information. Components registered using {@link
* #register(Component)} are registered for the entire duration of the Lifetime.
*/
public class PhasedLifetime<T> implements Lifetime
{
private final Map<T, List<RegisteredComponent>> _phases = new HashMap<>();
private final List<Component> _global = new ArrayList<>();
private boolean _active = false;
private T _current;
/**
* Registers the Component for all phases within the provided Iterable,
* and creates a Lifetime that is active during all those phases, and
* therefore identical to when the provided Component is active. If a change
* occurs from a Phase that the Component is active in to another phase that
* the component is active in, it will not be disabled. When this
* Lifetime ends, all Lifetimes created by this Lifetime also end, as all
* phases are considered over.
* <p />
* If the Component is an instance of PhasedComponent, then any phase change
* in which one of the phases is within the provided Iterable of phases will
* result in an invocation of {@link PhasedComponent#setPhase(Object)}. This
* should not be used as a mechanism to detect when the component is being
* disabled, but rather as a means to provide specific behavior when that
* phase change occurs. If a phase change drastically changes the behavior
* of a Component such that not all functionality is active for some phases
* that a Component is registered for, you should consider refactoring that
* Component into two separate Components.
* <p />
* As an example, assume that we have a PhasedLifetime with phases A-F,
* and a PhasedComponent is registered for phases A, B, and E. The chain
* of events would be as followed(italic indicates an event call to the
* PhasedComponent, an activation, or deactivation).
* <ul>
* <li>Lifetime started with a value of A</li>
* <li><i>Component is activated</i></li>
* <li><i>setPhase is called with a value of A</i></li>
* <li>Phase is set to B</li>
* <li><i>setPhase is called with a value of B</i></li>
* <li>Phase is set to C</li>
* <li><i>setPhase is called with a value of C</i></li>
* <li><i>Component is deactivated</i></li>
* <li>Phase is set to D</li>
* <li>Phase is set to E</li>
* <li><i>Component is activated</i></li>
* <li><i>setPhase is called with a value of E</i></li>
* <li>Phase is set to F</li>
* <li><i>setPhase is called with a value of F</i></li>
* <li><i>Component is deactivated</i></li>
* <li>Lifetime ends</li>
* </ul>
* <p />
* If phases contains no elements, then the Component will not be
* registered.
* @param component non-null Component being registered
* @param phases non-null Iterable of phases to register the Component for
* @return a Lifetime corresponding to when the Component is active
* @throws IllegalArgumentException if component or phases is null
*/
public Lifetime register(Component component, Iterable<T> phases) throws IllegalArgumentException {
Validate.notNull(component, "Component cannot be null");
Validate.notNull(phases, "Phases cannot be null");
RegisteredComponent rComponent = new RegisteredComponent(component);
for (T phase : phases)
{
_phases.computeIfAbsent(phase, (p) -> new ArrayList<>()).add(rComponent);
if (Objects.equals(phase, _current))
{
rComponent.start();
if (component instanceof PhasedComponent)
{
((PhasedComponent<T>) component).setPhase(phase);
}
}
}
return rComponent;
}
/**
* Starts the Lifetime, activating all components that are active for
* the entire lifetime, and then activating all components that are part
* of the provided phase.
* @param phase non-null phase to start
* @throws IllegalArgumentException if phase is null
* @throws IllegalStateException if the Lifetime is currently active
*/
public void start(T phase) throws IllegalArgumentException, IllegalStateException
{
Validate.notNull(phase, "phase cannot be null");
Preconditions.checkState(!_active, "Lifetime already started");
_active = true;
_global.forEach(PhasedLifetime::active);
setPhase(phase);
}
/**
* Ends the Lifetime, deactivating all components in the current phase
* and then deactivating all components that are active for the entire Lifetime.
* A Lifetime may be subsequently reactivated after it has ended.
* @throws IllegalStateException if the lifetime isn't active
*/
public void end() throws IllegalStateException
{
Preconditions.checkState(_active, "Lifetime not active");
List<RegisteredComponent> toDisable = _phases.get(getPhase());
if (toDisable != null)
{
toDisable.forEach(RegisteredComponent::end);
}
_global.forEach(PhasedLifetime::deactive);
_active = false;
_current = null;
}
/**
* Sets the current phase to the provided value, activating components
* that are active in the phase and not currently active, and deactiving
* components that were previously active and are not registered for the
* provided phase.
* @param phase non-null
* @throws IllegalStateException if the Lifetime isn't active
* @throws IllegalArgumentException if the phase equals the current phase
* or is null
*/
public void setPhase(T phase) throws IllegalStateException, IllegalArgumentException
{
Preconditions.checkState(_active, "Lifetime not active");
Validate.isTrue(!Objects.equals(phase, _current), "Can't set the phase to the current phase");
Validate.notNull(phase, "the phase cannot be null");
T oldPhase = getPhase();
_current = phase;
List<RegisteredComponent> old = _phases.get(oldPhase);
List<RegisteredComponent> nextPhase = _phases.get(phase);
for (Component c : _global)
{
if (c instanceof PhasedComponent)
{
((PhasedComponent<T>) c).setPhase(phase);
}
}
// Disable components that were active in the last phase but not in the next phase.
if (old != null)
{
List<RegisteredComponent> toDisable = new ArrayList<>();
toDisable.addAll(old);
// Components that are in the next phase shouldn't be disabled.
if (nextPhase != null)
{
toDisable.removeAll(nextPhase);
}
// Ensure that all old ones get a setPhase call before disabling them.
for (RegisteredComponent r : toDisable)
{
if (r.getComponent() instanceof PhasedComponent)
{
((PhasedComponent<T>) r.getComponent()).setPhase(phase);
}
}
toDisable.forEach(RegisteredComponent::end);
}
if (nextPhase != null)
{
// New but not old
List<RegisteredComponent> toActivate = new ArrayList<>();
toActivate.addAll(nextPhase);
// Ensure that all components from last phase don't end up getting activated again.
if (old != null)
{
toActivate.removeAll(old);
}
// Start all the new ones
toActivate.forEach(RegisteredComponent::start);
// Give every remaining component a call to setPhase
for (RegisteredComponent r : nextPhase)
{
if (r.getComponent() instanceof PhasedComponent)
{
((PhasedComponent<T>) r.getComponent()).setPhase(phase);
}
}
}
}
private static void active(Component component)
{
try
{
component.activate();
} catch (Exception e)
{
e.printStackTrace();
UtilServer.getPlugin().getLogger().severe("Failed to activate component: " + component);
}
}
private static void deactive(Component component)
{
try
{
component.deactivate();
} catch (Exception e)
{
e.printStackTrace();
UtilServer.getPlugin().getLogger().severe("Failed to deactivate component: " + component);
}
}
/**
* Gets the current Phase. If this PhasedLifetime isn't active then
* the current phase is null.
* @return the current phase, null if not active
*/
public T getPhase()
{
return _current;
}
/**
* {@inheritDoc}
* If the Component is an instance of PhasedComponent, it will receive
* notifications of phase changes prior to any components registered
* with specific phases. Global components will also be deactivated last.
* @param component the component to register
*/
@Override
public void register(Component component)
{
_global.add(component);
if (this.isActive())
{
component.activate();
}
}
@Override
public boolean isActive()
{
return _active;
}
private static class RegisteredComponent extends SimpleLifetime implements Lifetime {
private Component _component;
public RegisteredComponent(Component component)
{
this._component = component;
}
public Component getComponent()
{
return _component;
}
@Override
public void start() throws IllegalStateException
{
PhasedLifetime.active(_component);
super.start();
}
@Override
public void end() throws IllegalStateException
{
super.end();
PhasedLifetime.deactive(_component);
}
}
}

View File

@ -0,0 +1,96 @@
package mineplex.core.lifetimes;
import mineplex.core.common.util.UtilServer;
import org.apache.commons.lang3.Validate;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
/**
* A Lifetime that is not thread-safe and is essentially a list of Components.
* All components are activated in the order that they are registered. All
* components are deactivated in the reverse order that they are registered.
* Components may not be registered during invocations of start or end. If a
* Component throws an exception during activation it will be logged, however the
* Component will still only be disabled once all other Components are disabled.
*/
public class SimpleLifetime implements Lifetime
{
protected final List<Component> _components = new ArrayList<>();
protected boolean _active = false;
@Override
public void register(Component component)
{
this._components.add(component);
if (this.isActive())
{
try
{
component.activate();
} catch (Exception e)
{
e.printStackTrace();
UtilServer.getPlugin().getLogger().severe("Failed to active component: " + component);
}
}
}
@Override
public boolean isActive()
{
return _active;
}
/**
* Starts the SimpleLifetime, activating all components in the order that
* they were registered. A SimpleLifetime may be started multiple times,
* so long that every invocation of start after the first is preceded by
* an invocation of {@link #end()}. If a Component throws an exception
* during activation the exception will be logged, however no specific
* error-handling mechanism is provided. The Component will still be
* considered active and will be deactivated with all other Components.
* @throws IllegalStateException if currently active
*/
public void start() throws IllegalStateException
{
Validate.isTrue(!_active);
_active = true;
for (Component component : _components)
{
try
{
component.activate();
} catch (Exception e)
{
e.printStackTrace();
UtilServer.getPlugin().getLogger().severe("Failed to active component: " + component);
}
}
}
/**
* Deactivates all components in the reverse order that they were registered.
* Any exception thrown by a Component while being deactivated will be logged,
* however no exception handling mechanism is provided.
* @throws IllegalStateException if not currently active
*/
public void end() throws IllegalStateException
{
Validate.isTrue(_active);
_active = false;
ListIterator<Component> reverseIterator = _components.listIterator(_components.size());
while (reverseIterator.hasPrevious())
{
Component component = reverseIterator.previous();
try
{
component.deactivate();
} catch (Exception e)
{
e.printStackTrace();
UtilServer.getPlugin().getLogger().severe("Failed to deactivate component: " + component);
}
}
}
}

View File

@ -1,13 +1,15 @@
package mineplex.core.shop;
import mineplex.core.MiniPlugin;
import mineplex.core.account.CoreClientManager;
import mineplex.core.common.util.NautHashMap;
import mineplex.core.donation.DonationManager;
import mineplex.core.lifetimes.Lifetimed;
import mineplex.core.lifetimes.ListenerComponent;
import mineplex.core.npc.event.NpcDamageByEntityEvent;
import mineplex.core.npc.event.NpcInteractEntityEvent;
import mineplex.core.shop.page.ShopPageBase;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_8_R3.event.CraftEventFactory;
@ -16,7 +18,6 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
@ -24,8 +25,9 @@ import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashSet;
import java.util.Map;
public abstract class ShopBase<PluginType extends MiniPlugin> implements Listener
public abstract class ShopBase<PluginType extends Lifetimed> extends ListenerComponent
{
private NautHashMap<String, Long> _errorThrottling;
private NautHashMap<String, Long> _purchaseBlock;
@ -49,7 +51,7 @@ public abstract class ShopBase<PluginType extends MiniPlugin> implements Listene
_errorThrottling = new NautHashMap<String, Long>();
_purchaseBlock = new NautHashMap<String, Long>();
_plugin.registerEvents(this);
_plugin.getLifetime().register(this);
}
@EventHandler(priority = EventPriority.LOWEST)
@ -147,14 +149,7 @@ public abstract class ShopBase<PluginType extends MiniPlugin> implements Listene
{
if (_playerPageMap.containsKey(event.getPlayer().getName()) && _playerPageMap.get(event.getPlayer().getName()).getTitle() != null && _playerPageMap.get(event.getPlayer().getName()).getTitle().equalsIgnoreCase(event.getInventory().getTitle()))
{
_playerPageMap.get(event.getPlayer().getName()).playerClosed();
_playerPageMap.get(event.getPlayer().getName()).dispose();
_playerPageMap.remove(event.getPlayer().getName());
closeShopForPlayer((Player) event.getPlayer());
_openedShop.remove(event.getPlayer().getName());
removePlayer((Player) event.getPlayer());
}
}
@ -166,17 +161,39 @@ public abstract class ShopBase<PluginType extends MiniPlugin> implements Listene
if (_playerPageMap.containsKey(event.getPlayer().getName()) && _playerPageMap.get(event.getPlayer().getName()).getTitle() != null && _playerPageMap.get(event.getPlayer().getName()).getTitle().equalsIgnoreCase(event.getInventory().getTitle()))
{
_playerPageMap.get(event.getPlayer().getName()).playerClosed();
_playerPageMap.get(event.getPlayer().getName()).dispose();
_playerPageMap.remove(event.getPlayer().getName());
closeShopForPlayer((Player) event.getPlayer());
_openedShop.remove(event.getPlayer().getName());
removePlayer((Player) event.getPlayer());
}
}
protected void removePlayer(Player player)
{
_playerPageMap.get(player.getName()).playerClosed();
_playerPageMap.get(player.getName()).dispose();
_playerPageMap.remove(player.getName());
closeShopForPlayer(player);
_openedShop.remove(player.getName());
}
@Override
public void deactivate()
{
super.deactivate();
_playerPageMap.entrySet().stream().map(Map.Entry::getKey).map(Bukkit::getPlayer).forEach(p -> {
if (_playerPageMap.get(p.getName()).getTitle().equals(p.getOpenInventory().getTitle()))
{
p.closeInventory();
}
removePlayer(p);
});
_playerPageMap.clear();
_openedShop.clear();
_purchaseBlock.clear();
_errorThrottling.clear();
}
protected boolean canOpenShop(Player player)
{
return true;
@ -191,15 +208,8 @@ public abstract class ShopBase<PluginType extends MiniPlugin> implements Listene
{
if (_playerPageMap.containsKey(event.getPlayer().getName()))
{
_playerPageMap.get(event.getPlayer().getName()).playerClosed();
_playerPageMap.get(event.getPlayer().getName()).dispose();
removePlayer(event.getPlayer());
event.getPlayer().closeInventory();
closeShopForPlayer(event.getPlayer());
_playerPageMap.remove(event.getPlayer().getName());
_openedShop.remove(event.getPlayer().getName());
}
}

View File

@ -1,5 +1,14 @@
package mineplex.core.shop.page;
import mineplex.core.account.CoreClient;
import mineplex.core.account.CoreClientManager;
import mineplex.core.common.util.NautHashMap;
import mineplex.core.common.util.UtilInv;
import mineplex.core.donation.DonationManager;
import mineplex.core.lifetimes.Lifetimed;
import mineplex.core.shop.ShopBase;
import mineplex.core.shop.item.IButton;
import org.bukkit.Sound;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftInventoryCustom;
@ -11,16 +20,7 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import mineplex.core.MiniPlugin;
import mineplex.core.account.CoreClient;
import mineplex.core.account.CoreClientManager;
import mineplex.core.common.util.NautHashMap;
import mineplex.core.common.util.UtilInv;
import mineplex.core.donation.DonationManager;
import mineplex.core.shop.ShopBase;
import mineplex.core.shop.item.IButton;
public abstract class ShopPageBase<PluginType extends MiniPlugin, ShopType extends ShopBase<PluginType>> extends CraftInventoryCustom implements Listener
public abstract class ShopPageBase<PluginType extends Lifetimed, ShopType extends ShopBase<PluginType>> extends CraftInventoryCustom implements Listener
{
protected PluginType _plugin;
protected CoreClientManager _clientManager;

View File

@ -0,0 +1,32 @@
package mineplex.core.lifetimes;
import java.util.List;
public class LoggingComponent implements PhasedComponent {
private final List<String> _events;
private final String _name;
public LoggingComponent(List<String> events, String name)
{
_events = events;
this._name = name;
}
@Override
public void activate()
{
_events.add(this._name + " activated");
}
@Override
public void deactivate()
{
_events.add(this._name + " deactivated");
}
@Override
public void setPhase(Object phase)
{
_events.add(this._name + " setPhase " + phase);
}
}

View File

@ -0,0 +1,99 @@
package mineplex.core.lifetimes;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class PhasedLifetimeTest
{
PhasedLifetime<Phase> _lifetime = new PhasedLifetime<>();
List<String> _events = new ArrayList<>();
@Test
public void testTwoPhaseComponent()
{
Assert.assertFalse(_lifetime.isActive());
_lifetime.register(new LoggingComponent(_events, "component"), Arrays.asList(Phase.A, Phase.B));
_lifetime.start(Phase.A);
Assert.assertTrue(_lifetime.isActive());
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A"), _events);
_lifetime.setPhase(Phase.B);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A", "component setPhase B"), _events);
_lifetime.setPhase(Phase.C);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A", "component setPhase B", "component setPhase C", "component deactivated"), _events);
_lifetime.end();
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A", "component setPhase B", "component setPhase C", "component deactivated"), _events);
Assert.assertFalse(_lifetime.isActive());
}
@Test
public void testGlobalComponent()
{
_lifetime.register(new LoggingComponent(_events, "component"));
_lifetime.start(Phase.A);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A"), _events);
_lifetime.setPhase(Phase.B);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A", "component setPhase B"), _events);
_lifetime.end();
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A", "component setPhase B", "component deactivated"), _events);
}
@Test
public void testLateRegistration()
{
_lifetime.start(Phase.A);
_lifetime.register(new LoggingComponent(_events, "component"), Arrays.asList(Phase.A, Phase.B));
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A"), _events);
_lifetime.setPhase(Phase.B);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A", "component setPhase B"), _events);
_lifetime.end();
Assert.assertEquals(Arrays.asList("component activated", "component setPhase A", "component setPhase B", "component deactivated"), _events);
}
@Test
public void testSinglePhase()
{
_lifetime.register(new LoggingComponent(_events, "component"), Collections.singletonList(Phase.B));
_lifetime.start(Phase.A);
Assert.assertEquals(Collections.emptyList(), _events);
_lifetime.setPhase(Phase.B);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase B"), _events);
_lifetime.setPhase(Phase.C);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase B", "component setPhase C", "component deactivated"), _events);
_lifetime.end();
Assert.assertEquals(Arrays.asList("component activated", "component setPhase B", "component setPhase C", "component deactivated"), _events);
}
@Test
public void testComponentLifetimes()
{
_lifetime.register(new LoggingComponent(_events, "component"), Collections.singletonList(Phase.B)).register(new LoggingComponent(_events, "child"));
_lifetime.start(Phase.A);
Assert.assertEquals(Collections.emptyList(), _events);
_lifetime.setPhase(Phase.B);
Assert.assertEquals(Arrays.asList("component activated", "child activated", "component setPhase B"), _events);
_lifetime.setPhase(Phase.C);
Assert.assertEquals(Arrays.asList("component activated", "child activated","component setPhase B", "component setPhase C", "child deactivated", "component deactivated"), _events);
_lifetime.end();
Assert.assertEquals(Arrays.asList("component activated", "child activated", "component setPhase B", "component setPhase C", "child deactivated", "component deactivated"), _events);
}
@Test
public void testEarlyShutdown()
{
_lifetime.register(new LoggingComponent(_events, "component"), Arrays.asList(Phase.B, Phase.C));
_lifetime.start(Phase.A);
Assert.assertEquals(Collections.emptyList(), _events);
_lifetime.setPhase(Phase.B);
Assert.assertEquals(Arrays.asList("component activated", "component setPhase B"), _events);
_lifetime.end();
Assert.assertEquals(Arrays.asList("component activated", "component setPhase B", "component deactivated"), _events);
}
enum Phase
{
A,
B,
C,
;
}
}

View File

@ -0,0 +1,41 @@
package mineplex.core.lifetimes;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SimpleLifetimeTest
{
private final SimpleLifetime _lifetime = new SimpleLifetime();
private final List<String> _events = new ArrayList<>();
@Test
public void testAddition()
{
_lifetime.register(new LoggingComponent(_events,"a"));
_lifetime.start();
_lifetime.end();
Assert.assertEquals(_events, Arrays.asList("a activated", "a deactivated"));
}
@Test
public void testLateAddition()
{
_lifetime.start();
_lifetime.register(new LoggingComponent(_events,"a"));
_lifetime.end();
Assert.assertEquals(_events, Arrays.asList("a activated", "a deactivated"));
}
@Test
public void testActivationOrder()
{
_lifetime.register(new LoggingComponent(_events,"a"));
_lifetime.register(new LoggingComponent(_events,"b"));
_lifetime.start();
_lifetime.end();
Assert.assertEquals(_events, Arrays.asList("a activated", "b activated", "b deactivated", "a deactivated"));
}
}

View File

@ -13,6 +13,9 @@ import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import mineplex.core.lifetimes.Lifetimed;
import mineplex.core.lifetimes.ListenerComponent;
import mineplex.core.lifetimes.PhasedLifetime;
import org.apache.commons.lang3.tuple.Triple;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@ -106,9 +109,8 @@ import nautilus.game.arcade.world.WorldData;
import net.minecraft.server.v1_8_R3.EntityItem;
import net.minecraft.server.v1_8_R3.PacketPlayInUseEntity;
public abstract class Game implements Listener
public abstract class Game extends ListenerComponent implements Lifetimed
{
private final static int MAX_TICK_SPEED_MEASUREMENT = 40;
public long getGameLiveTime()
@ -132,6 +134,7 @@ public abstract class Game implements Listener
private GameType _gameType;
protected String[] _gameDesc;
private PhasedLifetime<GameState> _lifetime = new PhasedLifetime<>();
// Map
private HashMap<GameType, ArrayList<String>> _files;
@ -384,6 +387,7 @@ public abstract class Game implements Listener
{
Manager = manager;
_lifetime.register(this);
// Player List
UtilTabTitle.broadcastHeaderAndFooter(C.cGold + C.Bold + gameType.GetName(), "Visit " + C.cGreen + "www.mineplex.com"
+ ChatColor.RESET + " for News, Forums and Shop");
@ -754,6 +758,9 @@ public abstract class Game implements Listener
for (Player player : UtilServer.getPlayers())
player.leaveVehicle();
_lifetime.setPhase(state);
// Event
GameStateChangeEvent stateEvent = new GameStateChangeEvent(this, state);
UtilServer.getServer().getPluginManager().callEvent(stateEvent);
@ -2336,7 +2343,11 @@ public abstract class Game implements Listener
public void disable()
{
cleanupModules();
cleanupCommands();
Managers.get(AntiHack.class).resetIgnoredChecks();
getLifetime().end();
getStatTrackers().forEach(HandlerList::unregisterAll);
}
@EventHandler
@ -2444,5 +2455,10 @@ public abstract class Game implements Listener
{
return clazz.cast(_modules.get(clazz));
}
@Override
public PhasedLifetime<GameState> getLifetime()
{
return _lifetime;
}
}

View File

@ -0,0 +1,31 @@
package nautilus.game.arcade.game;
import mineplex.core.lifetimes.Lifetime;
import mineplex.core.lifetimes.Lifetimed;
import mineplex.core.lifetimes.ListenerComponent;
import java.util.Arrays;
public class GameComponent extends ListenerComponent implements Lifetimed
{
private final Lifetime _lifetime;
public GameComponent(Game game, Game.GameState...active)
{
if (active == null || active.length == 0)
{
// Active for the entire duration of the game.
_lifetime = game.getLifetime();
_lifetime.register(this);
} else
{
_lifetime = game.getLifetime().register(this, Arrays.asList(active));
}
}
@Override
public Lifetime getLifetime()
{
return _lifetime;
}
}

View File

@ -1115,6 +1115,7 @@ public class Gladiators extends SoloGame
@Override
public void disable()
{
super.disable();
_hotbarEditor.deregisterSelf(); // De-register as listener
_hotbarEditor.onDisable(); // Fully disable
}

View File

@ -1,13 +1,14 @@
package nautilus.game.arcade.game.games.wizards;
import mineplex.core.MiniPlugin;
import mineplex.core.common.util.C;
import mineplex.core.common.util.UtilInv;
import mineplex.core.common.util.UtilServer;
import mineplex.core.itemstack.ItemBuilder;
import mineplex.core.lifetimes.Lifetime;
import nautilus.game.arcade.events.GameStateChangeEvent;
import nautilus.game.arcade.game.Game.GameState;
import nautilus.game.arcade.game.GameComponent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@ -20,18 +21,17 @@ import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
public class WizardSpellMenu extends MiniPlugin
public class WizardSpellMenu extends GameComponent
{
private Wizards _wizards;
private WizardSpellMenuShop _wizardShop;
private ItemStack _wizardSpells = new ItemBuilder(Material.ENCHANTED_BOOK).setTitle(C.cGold + "Wizard Spells")
.addLore(C.cGray + "Right click with this to view the spells").build();
public WizardSpellMenu(String moduleName, JavaPlugin plugin, Wizards wizards)
public WizardSpellMenu(Wizards wizards)
{
super("Wizard Spell Menu", plugin);
super(wizards);
_wizardShop = new WizardSpellMenuShop(this, wizards.getArcadeManager().GetClients(), wizards.getArcadeManager()
.GetDonation(), wizards);
_wizards = wizards;
@ -49,7 +49,7 @@ public class WizardSpellMenu extends MiniPlugin
@EventHandler
public void onDeath(final PlayerDeathEvent event)
{
Bukkit.getScheduler().scheduleSyncDelayedTask(_plugin, new Runnable()
Bukkit.getScheduler().scheduleSyncDelayedTask(UtilServer.getPlugin(), new Runnable()
{
public void run()
{
@ -131,5 +131,4 @@ public class WizardSpellMenu extends MiniPlugin
}
}
}
}

View File

@ -153,7 +153,7 @@ public class Wizards extends SoloGame
new KitWitchDoctor(manager)
});
_wizard = new WizardSpellMenu("Wizard Spell Menu", getArcadeManager().getPlugin(), this);
_wizard = new WizardSpellMenu(this);
AnnounceStay = false;
BlockBreak = true;

View File

@ -87,8 +87,7 @@ public class GameCreationManager implements Listener
{
if (Manager.GetGame().GetState() == GameState.Dead)
{
HandlerList.unregisterAll(Manager.GetGame());
Manager.GetGame().disable();
//Schedule Cleanup
_ended.add(Manager.GetGame());
@ -105,15 +104,6 @@ public class GameCreationManager implements Listener
while (gameIterator.hasNext())
{
Game game = gameIterator.next();
game.cleanupModules();
game.cleanupCommands();
game.disable();
HandlerList.unregisterAll(game);
for (StatTracker tracker : game.getStatTrackers())
HandlerList.unregisterAll(tracker);
TimingManager.start("GameCreationManager - Attempting Removal - " + game.GetName());
@ -123,8 +113,7 @@ public class GameCreationManager implements Listener
gameIterator.remove();
}
else
{
{
boolean removedPlayers = false;
if (UtilTime.elapsed(game.GetStateTime(), 20000))
{
@ -154,7 +143,7 @@ public class GameCreationManager implements Listener
game.WorldData.Uninitialize();
game.WorldData = null;
gameIterator.remove();
TimingManager.stop("GameCreationManager - Uninit World - " + game.GetName());
};
}
@ -275,7 +264,7 @@ public class GameCreationManager implements Listener
TimingManager.stop("DisplayNext");
TimingManager.start("registerEvents");
UtilServer.getServer().getPluginManager().registerEvents(Manager.GetGame(), Manager.getPlugin());
Manager.GetGame().getLifetime().start(GameState.Loading);
TimingManager.stop("registerEvents");
}

View File

@ -134,11 +134,25 @@
<version>2.8.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.name}</finalName>
<testSourceDirectory>${project.basedir}/test</testSourceDirectory>
<sourceDirectory>${project.basedir}/src</sourceDirectory>
<plugins>
<plugin>