Second iteration on the workaround infrastructure (not in use).
* Rename interfaces to I... * Split off the statistics counting to accept/deny counters. * Remove support for parent workarounds. * Do use a testUse method for overriding in AbstractWorkaround. * Add a stage counter to WorkaroundCountDown, for meta checks per stage. * Extend/alter/implement default method signatures and interfaces. Missing: * Tests for the workaround package. * Add a primary thread / moving checks registry instance. * Add default sets and use in moving checks (primary thread only).
This commit is contained in:
parent
f337538ce3
commit
d2b151af99
|
@ -0,0 +1,58 @@
|
|||
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
|
||||
|
||||
/**
|
||||
* Default implementation for accept-deny counters,
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public class AcceptDenyCounter implements IResettableAcceptDenyCounter, ICounterWithParent {
|
||||
|
||||
private int acceptCount = 0;
|
||||
private int denyCount = 0;
|
||||
|
||||
private IAcceptDenyCounter parent = null;
|
||||
|
||||
@Override
|
||||
public AcceptDenyCounter setParentCounter(IAcceptDenyCounter parent) {
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAcceptDenyCounter getParentCounter() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() {
|
||||
acceptCount ++;
|
||||
if (parent != null) {
|
||||
parent.accept();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAcceptCount() {
|
||||
return acceptCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deny() {
|
||||
denyCount ++;
|
||||
if (parent != null) {
|
||||
parent.deny();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDenyCount() {
|
||||
return denyCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCounter() {
|
||||
acceptCount = denyCount = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
|
||||
|
||||
/**
|
||||
* Count the number of times of accepting something.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface IAcceptCounter {
|
||||
|
||||
/**
|
||||
* Increase the accept count. Propagate to parent (if any).
|
||||
*/
|
||||
public void accept();
|
||||
|
||||
/**
|
||||
* Get the number of times, that accept() has been called (since last reset,
|
||||
* if resettable).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getAcceptCount();
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
|
||||
|
||||
/**
|
||||
* Just combining accept and deny counts.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface IAcceptDenyCounter extends IAcceptCounter, IDenyCounter {
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
|
||||
|
||||
public interface ICounterWithParent {
|
||||
|
||||
/**
|
||||
* Set a parent counter.
|
||||
*
|
||||
* @param parent
|
||||
* @return This (counter) instance for chaining (not the previous parent).
|
||||
*/
|
||||
public IAcceptDenyCounter setParentCounter(IAcceptDenyCounter parent);
|
||||
|
||||
/**
|
||||
* Retrieve the parent counter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IAcceptDenyCounter getParentCounter();
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
|
||||
|
||||
/**
|
||||
* Count the number of times of denying something.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface IDenyCounter {
|
||||
|
||||
/**
|
||||
* Increase the deny count. Propagate to parent, if any.
|
||||
*/
|
||||
public void deny();
|
||||
|
||||
/**
|
||||
* Get the number of times, that deny() has been called (since last reset,
|
||||
* if resettable).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getDenyCount();
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
|
||||
|
||||
/**
|
||||
* Juts combine all three.
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface IResettableAcceptDenyCounter extends IAcceptDenyCounter, IResettableCounter {
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
|
||||
|
||||
/**
|
||||
* Allow resetting counters.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface IResettableCounter {
|
||||
|
||||
/**
|
||||
* Reset all contained counters. Not propagated to parent.
|
||||
*
|
||||
*/
|
||||
public void resetCounter();
|
||||
|
||||
}
|
|
@ -1,36 +1,27 @@
|
|||
package fr.neatmonster.nocheatplus.workaround;
|
||||
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.ICounterWithParent;
|
||||
|
||||
/**
|
||||
* Implementing the minimum features for counting use, plus the ability to trigger a parent
|
||||
* count.
|
||||
* Implementing the minimum features for counting use.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractWorkaround implements Workaround {
|
||||
public abstract class AbstractWorkaround implements IWorkaround {
|
||||
|
||||
private final String id;
|
||||
private final Workaround parent;
|
||||
private int useCount = 0;
|
||||
|
||||
public AbstractWorkaround(String id) {
|
||||
this(id, null); // No parent.
|
||||
}
|
||||
private final AcceptDenyCounter allTimeCounter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
* @param parent
|
||||
* For (global) count: parent.use() is called from within
|
||||
* this.use(), but the result is not evaluate.
|
||||
*/
|
||||
public AbstractWorkaround(String id, Workaround parent) {
|
||||
public AbstractWorkaround(String id) {
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Workaround getParent() {
|
||||
return parent;
|
||||
this.allTimeCounter = new AcceptDenyCounter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,25 +29,65 @@ public abstract class AbstractWorkaround implements Workaround {
|
|||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUseCount() {
|
||||
return useCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not meant to be overridden (alters counter, depending on result).
|
||||
* Override testUse(false) instead.
|
||||
*/
|
||||
@Override
|
||||
public boolean use() {
|
||||
if (canUse()) {
|
||||
useCount ++;
|
||||
if (parent != null) {
|
||||
// TODO: Might consider a hierarchy (parent could overrule the result to false).
|
||||
parent.use();
|
||||
}
|
||||
if (testUse(true)) {
|
||||
allTimeCounter.accept();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// DenyUseCound is handled in sub-classes, if needed.
|
||||
allTimeCounter.deny();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not meant to be overridden. Override testUse(true) instead.
|
||||
*/
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return testUse(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IAcceptDenyCounter getAllTimeCounter() {
|
||||
return allTimeCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to check for pre/side-conditions.
|
||||
*
|
||||
* @param isUse
|
||||
* If set to false, this must not alter any data, nor have any
|
||||
* other side conditions. If set to true, this is called from
|
||||
* within use(), meant to update stage counters and similar. Note
|
||||
* that the allTimeCounter is updated from within use, depending
|
||||
* on the result of calling testUse(true).
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean testUse(boolean isUse);
|
||||
|
||||
/**
|
||||
* Override the parent counters in instance with the ones set in this
|
||||
* instance, if possible.
|
||||
*
|
||||
* @param instance
|
||||
* @return The given instance.
|
||||
*/
|
||||
<W extends IWorkaround> W setParentCounters(final W instance) {
|
||||
final IAcceptDenyCounter allTimeParent = this.allTimeCounter.getParentCounter();
|
||||
if (allTimeParent != null) {
|
||||
final IAcceptDenyCounter instanceAllTime = instance.getAllTimeCounter();
|
||||
if ((instance instanceof ICounterWithParent)) {
|
||||
((ICounterWithParent) instanceAllTime).setParentCounter(allTimeParent);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package fr.neatmonster.nocheatplus.workaround;
|
||||
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
|
||||
|
||||
/**
|
||||
* Workaround allowing for resetting pre/side-conditions, with an extra counter
|
||||
* lasting until reset. Parent counters may not be supported.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface IStagedWorkaround {
|
||||
|
||||
// TODO: resetConditions: Consider to have it return false by default (x.use() || x.resetConditions() with pre-conditions).
|
||||
/**
|
||||
* Generic reset to the initial conditions. This does not reset the all time
|
||||
* counters, other effects depend on the implementation.
|
||||
*/
|
||||
public void resetConditions();
|
||||
|
||||
/**
|
||||
* Get the counter that will reset with the next call to resetConditions.
|
||||
* Parent counters may not be supported.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IAcceptDenyCounter getStageCounter();
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package fr.neatmonster.nocheatplus.workaround;
|
||||
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
|
||||
|
||||
/**
|
||||
* Provide a means of controlling when workarounds should be able to apply,
|
||||
* enabling preconditions, mid-to-long-term side-conditions, as well as
|
||||
* statistics for how often a workaround has been used.
|
||||
* <hr>
|
||||
* The method use() must be called after all other preconditions have been met
|
||||
* for that (stage of) workaround, so that success means that the (stage of)
|
||||
* workaround does apply. The method canUse can be used to test if the
|
||||
* workaround would apply, aimed at cases where that is better performance-wise.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface IWorkaround {
|
||||
|
||||
// TODO: Add setEnabled() ? Allow to configure workarounds (-> IConfigurableThing).
|
||||
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* Attempt to use the workaround, considering all preconditions and
|
||||
* side-conditions set. This will increase the accept counter in case of
|
||||
* returning true, or increase the deny counter in case of returning false.
|
||||
* It might also alter/use other counters based on the value to be returned
|
||||
* (stage counter, parent counter, other side conditions).
|
||||
*
|
||||
* @return If actually can be used.
|
||||
*/
|
||||
public boolean use();
|
||||
|
||||
/**
|
||||
* Test if this (stage of) workaround would apply. This must not have any
|
||||
* side effect nor change any data. Intended use is a fast-denial check, to
|
||||
* be performed before other heavier to check pre-conditions are tested, as
|
||||
* well as being called from within use().
|
||||
*
|
||||
* @return If a call returns false, use() must also return false (until
|
||||
* conditions are changed). If a call returns true, future
|
||||
* implementations might still return false on use().
|
||||
*/
|
||||
public boolean canUse();
|
||||
|
||||
/**
|
||||
* Retrieve the counter used for acceptance and denial with calling use().
|
||||
* This counter may or may not have a parent counter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IAcceptDenyCounter getAllTimeCounter();
|
||||
|
||||
/**
|
||||
* Serving as factory, retrieve a new instance of the same kind, in the
|
||||
* default state (not clone). Counters should be new counter instances,
|
||||
* except for parents.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IWorkaround getNewInstance();
|
||||
|
||||
}
|
|
@ -2,11 +2,15 @@ package fr.neatmonster.nocheatplus.workaround;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.ICounterWithParent;
|
||||
|
||||
/**
|
||||
* Simple registry for workarounds. No thread-safety built in.
|
||||
*
|
||||
|
@ -16,10 +20,10 @@ import java.util.Map;
|
|||
public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
||||
|
||||
/** Global counter by id. */
|
||||
private final Map<String, WorkaroundCounter> counters = new HashMap<String, WorkaroundCounter>();
|
||||
private final Map<String, IAcceptDenyCounter> counters = new HashMap<String, IAcceptDenyCounter>();
|
||||
|
||||
/** Workaround blue print by id. */
|
||||
private final Map<String, Workaround> bluePrints = new HashMap<String, Workaround>();
|
||||
private final Map<String, IWorkaround> bluePrints = new HashMap<String, IWorkaround>();
|
||||
|
||||
/** Map group id to array of workaround ids. */
|
||||
private final Map<String, String[]> groups = new HashMap<String, String[]>();
|
||||
|
@ -31,10 +35,19 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
private final Map<String, String[]> workaroundSetGroups = new HashMap<String, String[]>();
|
||||
|
||||
@Override
|
||||
public void setWorkaroundBluePrint(final Workaround... bluePrints) {
|
||||
public void setWorkaroundBluePrint(final IWorkaround... bluePrints) {
|
||||
// TODO: Might consistency check, plus policy for overriding (ignore all if present).
|
||||
for (int i = 0; i < bluePrints.length; i++) {
|
||||
final Workaround workaround = bluePrints[i];
|
||||
this.bluePrints.put(workaround.getId(), workaround.getNewInstance());
|
||||
final IWorkaround bluePrintCopy = bluePrints[i].getNewInstance();
|
||||
this.bluePrints.put(bluePrintCopy.getId(), bluePrintCopy);
|
||||
// Set a parent counter, if not already set.
|
||||
final IAcceptDenyCounter allTimeCounter = bluePrintCopy.getAllTimeCounter();
|
||||
if (allTimeCounter instanceof ICounterWithParent) {
|
||||
final ICounterWithParent bluePrintCopyWithParent = (ICounterWithParent) bluePrintCopy;
|
||||
if (bluePrintCopyWithParent.getParentCounter() == null) {
|
||||
bluePrintCopyWithParent.setParentCounter(createGlobalCounter(bluePrintCopy.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,10 +57,10 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setWorkaroundSet(final String workaroundSetId, final Collection<Workaround> bluePrints, final String... groupIds) {
|
||||
public void setWorkaroundSet(final String workaroundSetId, final Collection<IWorkaround> bluePrints, final String... groupIds) {
|
||||
final String[] ids = new String[bluePrints.size()];
|
||||
int i = 0;
|
||||
for (final Workaround bluePrint : bluePrints) {
|
||||
for (final IWorkaround bluePrint : bluePrints) {
|
||||
final String id = bluePrint.getId();
|
||||
if (!this.bluePrints.containsKey(id)) {
|
||||
// Lazily register.
|
||||
|
@ -69,9 +82,9 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
|
||||
@Override
|
||||
public void setWorkaroundSetByIds(final String workaroundSetId, final Collection<String> bluePrintIds, final String... groupIds) {
|
||||
final List<Workaround> bluePrints = new ArrayList<Workaround>(bluePrintIds.size());
|
||||
final List<IWorkaround> bluePrints = new ArrayList<IWorkaround>(bluePrintIds.size());
|
||||
for (final String id : bluePrintIds) {
|
||||
final Workaround bluePrint = this.bluePrints.get(id);
|
||||
final IWorkaround bluePrint = this.bluePrints.get(id);
|
||||
if (bluePrint == null) {
|
||||
throw new IllegalArgumentException("the blueprint is not registered: " + id);
|
||||
}
|
||||
|
@ -86,7 +99,7 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
if (workaroundIds == null) {
|
||||
throw new IllegalArgumentException("WorkaroundSet not registered: " + workaroundSetId);
|
||||
}
|
||||
final Workaround[] bluePrints = new Workaround[workaroundIds.length];
|
||||
final IWorkaround[] bluePrints = new IWorkaround[workaroundIds.length];
|
||||
for (int i = 0; i < workaroundIds.length; i++) {
|
||||
bluePrints[i] = this.bluePrints.get(workaroundIds[i]);
|
||||
}
|
||||
|
@ -106,15 +119,15 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
}
|
||||
|
||||
@Override
|
||||
public WorkaroundCounter getGlobalCounter(final String id) {
|
||||
public IAcceptDenyCounter getGlobalCounter(final String id) {
|
||||
return counters.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkaroundCounter createGlobalCounter(final String id) {
|
||||
WorkaroundCounter counter = counters.get(id);
|
||||
public IAcceptDenyCounter createGlobalCounter(final String id) {
|
||||
IAcceptDenyCounter counter = counters.get(id);
|
||||
if (counter == null) {
|
||||
counter = new WorkaroundCounter(id);
|
||||
counter = new AcceptDenyCounter();
|
||||
counters.put(id, counter);
|
||||
}
|
||||
return counter;
|
||||
|
@ -122,8 +135,8 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <C extends Workaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
|
||||
final Workaround workaround = getWorkaround(id);
|
||||
public <C extends IWorkaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
|
||||
final IWorkaround workaround = getWorkaround(id);
|
||||
if (workaroundClass.isAssignableFrom(workaround.getClass())) {
|
||||
return (C) workaround;
|
||||
}
|
||||
|
@ -133,8 +146,8 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Workaround getWorkaround(final String id) {
|
||||
final Workaround bluePrint = bluePrints.get(id);
|
||||
public IWorkaround getWorkaround(final String id) {
|
||||
final IWorkaround bluePrint = bluePrints.get(id);
|
||||
if (bluePrint == null) {
|
||||
throw new IllegalArgumentException("Id not registered as blueprint: " + id);
|
||||
}
|
||||
|
@ -142,12 +155,8 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getGlobalUseCount() {
|
||||
final Map<String, Integer> currentCounts = new LinkedHashMap<String, Integer>(counters.size());
|
||||
for (final WorkaroundCounter counter : counters.values()) {
|
||||
currentCounts.put(counter.getId(), counter.getUseCount());
|
||||
}
|
||||
return currentCounts;
|
||||
public Map<String, IAcceptDenyCounter> getGlobalCounters() {
|
||||
return Collections.unmodifiableMap(counters);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package fr.neatmonster.nocheatplus.workaround;
|
||||
|
||||
/**
|
||||
* Provide a means of controlling when workarounds should be able to apply,
|
||||
* enabling preconditions, mid-to-long-term side-conditions, as well as
|
||||
* statistics for how often a workaround has been used.
|
||||
* <hr>
|
||||
* The method use() must be called after all other preconditions have been met
|
||||
* for that (stage of) workaround, so that success means that the (stage of)
|
||||
* workaround does apply. The method canUse can be used to test if the
|
||||
* workaround would apply, aimed at cases where that is better performance-wise.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public interface Workaround {
|
||||
|
||||
// TODO: getDiscardCount() ?
|
||||
// TODO: Add setEnabled() ? Allow to configure workarounds.
|
||||
|
||||
public String getId();
|
||||
|
||||
/** The all-time use count. */
|
||||
public int getUseCount();
|
||||
|
||||
/**
|
||||
* Attempt to use the workaround, considering all preconditions and
|
||||
* side-conditions set. This will increase the use count in case of
|
||||
* returning true, it might also alter/use other counters based on the value
|
||||
* to be returned.
|
||||
*
|
||||
* @return If actually can be used.
|
||||
*/
|
||||
public boolean use();
|
||||
|
||||
/**
|
||||
* Test if this (stage of) workaround would apply, excluding checking for
|
||||
* parent state. This must not have any side effect nor change any data.
|
||||
* This should also not call parent.canUse, as that would lead to quadratic
|
||||
* time checking with a deeper hierarchy (likely it's just 1 deep).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean canUse();
|
||||
|
||||
/**
|
||||
* Generic reset to the initial conditions. This does not reset the use
|
||||
* count, other effects depend on the implementation.
|
||||
*/
|
||||
public void resetConditions();
|
||||
|
||||
/**
|
||||
* Serving as factory, retrieve a new instance of the same kind, in the
|
||||
* default state (not clone).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Workaround getNewInstance();
|
||||
|
||||
}
|
|
@ -1,52 +1,86 @@
|
|||
package fr.neatmonster.nocheatplus.workaround;
|
||||
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IResettableAcceptDenyCounter;
|
||||
|
||||
/**
|
||||
* Count down to 0, use is only possible if the currentCounter is greater than
|
||||
* 0. An initialCounter of 0 together with setting the currentCount manually,
|
||||
* allows to activate a workaround on base of a precondition.
|
||||
* allows to activate a workaround on base of a precondition. Also keep track of
|
||||
* denyUseCount (all time + stage). Parent counters are not supported for the
|
||||
* stage counter.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public class WorkaroundCountDown extends AbstractWorkaround {
|
||||
public class WorkaroundCountDown extends AbstractWorkaround implements IStagedWorkaround {
|
||||
|
||||
/** The start value for the count down. */
|
||||
private final int initialCount;
|
||||
/** The current state */
|
||||
private int currentCount;
|
||||
private final IResettableAcceptDenyCounter stageCounter;
|
||||
|
||||
public WorkaroundCountDown(String id, int initialCount, Workaround parent) {
|
||||
super(id, parent);
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
* @param initialCount
|
||||
*/
|
||||
public WorkaroundCountDown(String id, int initialCount) {
|
||||
super(id);
|
||||
this.initialCount = initialCount;
|
||||
this.currentCount = initialCount;
|
||||
this.stageCounter = new AcceptDenyCounter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current count down value.
|
||||
* @param currentCount
|
||||
*/
|
||||
public void setCurrentCount(int currentCount) {
|
||||
this.currentCount = currentCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current count down value.
|
||||
*/
|
||||
public int getCurrentCount() {
|
||||
return currentCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean use() {
|
||||
// Adjust currentCount based on super.use().
|
||||
if (super.use()) {
|
||||
currentCount --;
|
||||
stageCounter.accept();
|
||||
return true;
|
||||
} else {
|
||||
stageCounter.deny();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return currentCount > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConditions() {
|
||||
currentCount = initialCount;
|
||||
stageCounter.resetCounter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkaroundCountDown getNewInstance() {
|
||||
return new WorkaroundCountDown(getId(), initialCount, getParent());
|
||||
return setParentCounters(new WorkaroundCountDown(getId(), initialCount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAcceptDenyCounter getStageCounter() {
|
||||
return stageCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testUse(boolean isUse) {
|
||||
return currentCount > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,28 +7,19 @@ package fr.neatmonster.nocheatplus.workaround;
|
|||
*/
|
||||
public class WorkaroundCounter extends AbstractWorkaround {
|
||||
|
||||
public WorkaroundCounter(String id, Workaround parent) {
|
||||
super(id, parent);
|
||||
}
|
||||
|
||||
public WorkaroundCounter(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
public boolean testUse(final boolean isUse) {
|
||||
// Just counting.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConditions() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkaroundCounter getNewInstance() {
|
||||
return new WorkaroundCounter(getId());
|
||||
return setParentCounters(new WorkaroundCounter(getId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
|
||||
|
||||
/**
|
||||
* An access point for fetching global WorkaroundCounter instances and a factory
|
||||
* for fetching new sets of per-player workarounds.
|
||||
|
@ -21,7 +22,8 @@ public interface WorkaroundRegistry {
|
|||
* Convenience to retrieve any type of per-player Workaround by id, for the
|
||||
* case one doesn't want to store the registry and/or individual Workaround
|
||||
* implementations as members. Groups allow resetting certain types of
|
||||
* workarounds in bunches.
|
||||
* workarounds in bunches. The resetConditions methods only call
|
||||
* resetConditions for instances of IStagedWorkaround.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
|
@ -30,16 +32,20 @@ public interface WorkaroundRegistry {
|
|||
|
||||
// TODO: getUseCount()
|
||||
// TODO: A list for ids of just used workarounds (reset externally. Add use(id) vs alter Workaround)?
|
||||
// TODO: Better optimized constructor.
|
||||
// TODO: Better optimized constructor (instanceof-decisions can be pre-cached).
|
||||
|
||||
/** Map workaround id to workaround. */
|
||||
private final Map<String, Workaround> workaroundsById = new LinkedHashMap<String, Workaround>();
|
||||
private final Map<String, IWorkaround> workaroundsById = new LinkedHashMap<String, IWorkaround>();
|
||||
|
||||
/** Only the workarounds that might need resetting. */
|
||||
private final Workaround[] mightNeedReset;
|
||||
private final IStagedWorkaround[] stagedWorkarounds;
|
||||
|
||||
/** Map groupId to workarounds. */
|
||||
private final Map<String, Workaround[]> groups;
|
||||
// TODO: Consider to make accessible (flexible log/stats command) or remove keeping entire groups.
|
||||
/** Map groupId to workarounds. Set to null, if no groups are present. */
|
||||
private final Map<String, IWorkaround[]> groups;
|
||||
|
||||
/** Only the staged workarounds within a group by group id. Set to null, if no groups are present. */
|
||||
private final Map<String, IStagedWorkaround[]> stagedGroups;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -49,36 +55,48 @@ public interface WorkaroundRegistry {
|
|||
* are set. All referenced workaround ids must be registered,
|
||||
* workarounds can be in multiple groups.
|
||||
*/
|
||||
public WorkaroundSet(final Workaround[] bluePrints, final Map<String, String[]> groups) {
|
||||
final Class<?> excludeFromReset = WorkaroundCounter.class;
|
||||
final List<Workaround> mightNeedReset = new ArrayList<Workaround>(bluePrints.length);
|
||||
public WorkaroundSet(final IWorkaround[] bluePrints, final Map<String, String[]> groups) {
|
||||
// Add new instances to workaroundsById and stagedWorkarounds.
|
||||
final ArrayList<IWorkaround> stagedWorkarounds = new ArrayList<IWorkaround>(bluePrints.length);
|
||||
for (int i = 0; i < bluePrints.length; i++) {
|
||||
final Workaround workaround = bluePrints[i].getNewInstance();
|
||||
final IWorkaround workaround = bluePrints[i].getNewInstance();
|
||||
workaroundsById.put(workaround.getId(), workaround);
|
||||
if (workaround.getClass() != excludeFromReset) {
|
||||
mightNeedReset.add(workaround);
|
||||
if (workaround instanceof IStagedWorkaround) {
|
||||
stagedWorkarounds.add(workaround);
|
||||
}
|
||||
}
|
||||
this.mightNeedReset = mightNeedReset.toArray(new Workaround[mightNeedReset.size()]);
|
||||
this.stagedWorkarounds = stagedWorkarounds.toArray(new IStagedWorkaround[stagedWorkarounds.size()]);
|
||||
|
||||
// Prepare fast to reset lists, if groups are given.
|
||||
if (groups != null) {
|
||||
this.groups = new HashMap<String, Workaround[]>();
|
||||
this.groups = new HashMap<String, IWorkaround[]>();
|
||||
this.stagedGroups = new HashMap<String, IStagedWorkaround[]>();
|
||||
for (final Entry<String, String[]> entry : groups.entrySet()) {
|
||||
final String groupId = entry.getKey();
|
||||
final String[] workaroundIds = entry.getValue();
|
||||
final Workaround[] group = new Workaround[workaroundIds.length];
|
||||
final IWorkaround[] group = new IWorkaround[workaroundIds.length];
|
||||
final ArrayList<IStagedWorkaround> stagedGroup = new ArrayList<IStagedWorkaround>(workaroundIds.length);
|
||||
for (int i = 0; i < workaroundIds.length; i++) {
|
||||
group[i] = getWorkaround(workaroundIds[i]);
|
||||
final IWorkaround workaround = getWorkaround(workaroundIds[i]);
|
||||
group[i] = workaround;
|
||||
if (workaround instanceof IStagedWorkaround) {
|
||||
stagedGroup.add((IStagedWorkaround) workaround);
|
||||
}
|
||||
}
|
||||
this.groups.put(groupId, group);
|
||||
if (!stagedGroup.isEmpty()) {
|
||||
this.stagedGroups.put(groupId, stagedGroup.toArray(new IStagedWorkaround[stagedGroup.size()]));
|
||||
}
|
||||
this.groups.put(entry.getKey(), group);
|
||||
}
|
||||
} else {
|
||||
this.groups = null;
|
||||
this.stagedGroups = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends Workaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
|
||||
final Workaround present = getWorkaround(id);
|
||||
public <C extends IWorkaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
|
||||
final IWorkaround present = getWorkaround(id);
|
||||
if (!workaroundClass.isAssignableFrom(present.getClass())) {
|
||||
throw new IllegalArgumentException("Wrong type of registered workaround requested: " + workaroundClass.getName() + " instead of " + present.getClass().getName());
|
||||
} else {
|
||||
|
@ -86,8 +104,8 @@ public interface WorkaroundRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
public Workaround getWorkaround(final String id) {
|
||||
final Workaround present = workaroundsById.get(id);
|
||||
public IWorkaround getWorkaround(final String id) {
|
||||
final IWorkaround present = workaroundsById.get(id);
|
||||
if (present == null) {
|
||||
throw new IllegalArgumentException("Workaround id not registered: " + id);
|
||||
}
|
||||
|
@ -99,8 +117,8 @@ public interface WorkaroundRegistry {
|
|||
* WorkaroundCounter instances (sub classes get reset too).
|
||||
*/
|
||||
public void resetConditions() {
|
||||
for (int i = 0; i < mightNeedReset.length; i++) {
|
||||
mightNeedReset[i].resetConditions();
|
||||
for (int i = 0; i < stagedWorkarounds.length; i++) {
|
||||
stagedWorkarounds[i].resetConditions();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,26 +129,54 @@ public interface WorkaroundRegistry {
|
|||
* @param groupId
|
||||
*/
|
||||
public void resetConditions(final String groupId) {
|
||||
final Workaround[] workarounds = groups.get(groupId);
|
||||
if (workarounds == null) {
|
||||
throw new IllegalArgumentException("Group not registered: " + groupId);
|
||||
}
|
||||
for (int i = 0; i < workarounds.length; i++) {
|
||||
workarounds[i].resetConditions();
|
||||
if (this.stagedGroups != null) {
|
||||
final IStagedWorkaround[] workarounds = stagedGroups.get(groupId);
|
||||
if (workarounds != null) {
|
||||
for (int i = 0; i < workarounds.length; i++) {
|
||||
workarounds[i].resetConditions();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unchecked use.
|
||||
*
|
||||
* @param workaroundId
|
||||
* @return The result of IWorkaround.use() for the registered instance.
|
||||
* @throws NullPointerException
|
||||
* if no workaround is registered for this id.
|
||||
*
|
||||
*/
|
||||
public boolean use(String workaroundId) {
|
||||
// TODO: For consistency might throw the same exception everywhere (IllegalArgument?).
|
||||
return workaroundsById.get(workaroundId).use();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unchecked canUse.
|
||||
*
|
||||
* @param workaroundId
|
||||
* @return The result of IWorkaround.canUse() for the registered
|
||||
* instance.
|
||||
* @throws NullPointerException
|
||||
* if no workaround is registered for this id.
|
||||
*
|
||||
*/
|
||||
public boolean canUse(String workaroundId) {
|
||||
// TODO: For consistency might throw the same exception everywhere (IllegalArgument?).
|
||||
return workaroundsById.get(workaroundId).canUse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Might make getWorkaround non public, to favor use of WorkaroundSet.
|
||||
|
||||
/**
|
||||
* Registers workaround.getNewInstance() for the set id. Set parent to
|
||||
* createGlobalCounter(id), if a global counter is desired.
|
||||
* Registers workaround.getNewInstance() for the set id as a blueprint. A
|
||||
* parent counter will be created, if it doesn't exist yet.
|
||||
*
|
||||
* @param bluePrints
|
||||
*/
|
||||
public void setWorkaroundBluePrint(Workaround...bluePrints);
|
||||
public void setWorkaroundBluePrint(IWorkaround...bluePrints);
|
||||
|
||||
/**
|
||||
* Specify what workaround ids belong to a certain group. Workarounds can be
|
||||
|
@ -147,12 +193,12 @@ public interface WorkaroundRegistry {
|
|||
*
|
||||
* @param workaroundSetId
|
||||
* @param bluePrints
|
||||
* Lazily registers, if no blueprint is present. Already
|
||||
* Lazily registers, if no blueprint is present (calling setWorkaroundBluePrint, note parent counters). Already
|
||||
* registered blueprints are kept.
|
||||
* @param groupIds
|
||||
* Must already be registered.
|
||||
*/
|
||||
public void setWorkaroundSet(String workaroundSetId, Collection<Workaround> bluePrints, String... groupIds);
|
||||
public void setWorkaroundSet(String workaroundSetId, Collection<IWorkaround> bluePrints, String... groupIds);
|
||||
|
||||
/**
|
||||
* Define which workarounds and which groups belong to the WorkaroundSet of
|
||||
|
@ -174,21 +220,22 @@ public interface WorkaroundRegistry {
|
|||
public WorkaroundSet getWorkaroundSet(String workaroundSetId);
|
||||
|
||||
/**
|
||||
* Get a registered global WorkaroundCounter, if registered.
|
||||
* Get a registered global IAcceptDenyCounter instance, if registered.
|
||||
*
|
||||
* @param id
|
||||
* @return The registered WorkaroundCounter instance, or null if none is
|
||||
* @return The registered IAcceptDenyCounter instance, or null if none is
|
||||
* registered for the given id.
|
||||
*/
|
||||
public WorkaroundCounter getGlobalCounter(String id);
|
||||
public IAcceptDenyCounter getGlobalCounter(String id);
|
||||
|
||||
/**
|
||||
* Get a registered global WorkaroundCounter, create if not present.
|
||||
* Get a registered global IAcceptDenyCounter instance, create if not
|
||||
* present.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public WorkaroundCounter createGlobalCounter(String id);
|
||||
public IAcceptDenyCounter createGlobalCounter(String id);
|
||||
|
||||
/**
|
||||
* Retrieve a new instance, ready for use, attached to a global counter of
|
||||
|
@ -202,7 +249,7 @@ public interface WorkaroundRegistry {
|
|||
* @throws IllegalArgumentException
|
||||
* If either of id or workaroundClass is not possible to use.
|
||||
*/
|
||||
public <C extends Workaround> C getWorkaround(String id, Class<C> workaroundClass);
|
||||
public <C extends IWorkaround> C getWorkaround(String id, Class<C> workaroundClass);
|
||||
|
||||
/**
|
||||
* Retrieve a new instance, ready for use, attached to a global counter of
|
||||
|
@ -213,13 +260,14 @@ public interface WorkaroundRegistry {
|
|||
* @throws IllegalArgumentException
|
||||
* If either of id or workaroundClass is not possible to use.
|
||||
*/
|
||||
public Workaround getWorkaround(String id);
|
||||
public IWorkaround getWorkaround(String id);
|
||||
|
||||
/**
|
||||
* Get all global count values by id.
|
||||
* Retrieve an unmodifiable map for all registered global counters. The counters
|
||||
* are not copies, so they could be altered, discouraged though.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Integer> getGlobalUseCount();
|
||||
public Map<String, IAcceptDenyCounter> getGlobalCounters();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package fr.neatmonster.nocheatplus.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
|
||||
|
||||
public class TestAcceptDenyCounters {
|
||||
|
||||
/**
|
||||
* Test the default implementation.
|
||||
*/
|
||||
@Test
|
||||
public void testAcceptDenyCounter() {
|
||||
AcceptDenyCounter leaf = new AcceptDenyCounter();
|
||||
AcceptDenyCounter parent = new AcceptDenyCounter();
|
||||
leaf.setParentCounter(parent);
|
||||
|
||||
// Add to leaf.
|
||||
for (int i = 0; i < 73; i++) {
|
||||
leaf.accept();
|
||||
}
|
||||
checkCounts(leaf, 73, 0, "leaf");
|
||||
|
||||
for (int i = 0; i < 65; i++) {
|
||||
leaf.deny();
|
||||
}
|
||||
checkCounts(leaf, 73, 65, "leaf");
|
||||
|
||||
// Add only to parent.
|
||||
for (int i = 0; i < 52; i++) {
|
||||
parent.accept();
|
||||
}
|
||||
checkCounts(parent, 73 + 52, 65, "parent");
|
||||
checkCounts(leaf, 73, 65, "leaf");
|
||||
|
||||
for (int i = 0; i < 97; i++) {
|
||||
parent.deny();
|
||||
}
|
||||
checkCounts(parent, 73 + 52, 65 + 97, "parent");
|
||||
checkCounts(leaf, 73, 65, "leaf");
|
||||
|
||||
// Reset parent.
|
||||
parent.resetCounter();
|
||||
checkCounts(parent, 0, 0, "parent");
|
||||
checkCounts(leaf, 73, 65, "leaf");
|
||||
}
|
||||
|
||||
public void checkCounts(IAcceptDenyCounter counter, int acceptCount, int denyCount, String counterName) {
|
||||
if (counter.getAcceptCount() != acceptCount) {
|
||||
fail("Wrong accept count for counter '" + counterName + "': " + counter.getAcceptCount() + " instead of " + acceptCount + ".");
|
||||
}
|
||||
if (counter.getDenyCount() != denyCount) {
|
||||
fail("Wrong deny count for counter '" + counterName + "': " + counter.getDenyCount() + " instead of " + denyCount + ".");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue