[BLIND] Continue to implement input specific penalty support.

Have a penalty list to collect input specific penalties from
ViolationData and apply after event handling.

Pass through the list to fight.critical.

Missing:
* Penalty factories and configuration. Penalty registry. Link with
actions.
* Pass through the penalty list to all fight checks.
* (Implement default penalty types.)
* ((Implement stored penalties.))
This commit is contained in:
asofold 2018-04-05 11:07:42 +02:00
parent 96e3869ff1
commit 274c15eccf
9 changed files with 241 additions and 27 deletions

View File

@ -74,6 +74,16 @@ public abstract class AbstractGenericPenalty<RI> implements GenericPenalty<RI> {
}
}
@Override
public void applyPrecisely(final RI input) {
applyGenericEffects(input);
}
@Override
public void addToPenaltyList(final IPenaltyList penaltyList) {
penaltyList.addGenericPenalty(registeredInput, this);
}
/**
* Override for implementation of input-specific effects.
*

View File

@ -0,0 +1,107 @@
package fr.neatmonster.nocheatplus.actions.types.penalty;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class DefaultPenaltyList implements IPenaltyList {
/**
* Desperation.
*
* @author asofold
*
* @param <RI>
*/
private static class GenericNode<RI> {
private final List<GenericPenalty<RI>> penalties = new LinkedList<GenericPenalty<RI>>();
private void apply(final RI input) {
for (final GenericPenalty<RI> penalty : penalties) {
penalty.applyPrecisely(input);
}
}
}
private boolean isEmpty = true;
private boolean hasGeneric = false;
private boolean hasNonGeneric= false;
private final Map<Class<?>, GenericNode<?>> genericPenalties = new LinkedHashMap<Class<?>, GenericNode<?>>();
private final List<InputSpecificPenalty> inputSpecificPenalties = new LinkedList<InputSpecificPenalty>();
@Override
public void addInputSpecificPenalty(final InputSpecificPenalty penalty) {
if (penalty == null) {
// Until decided how parsing / optimized lists are done.
return;
}
if (penalty instanceof GenericPenalty) {
((GenericPenalty<?>) penalty).addToPenaltyList(this);
}
else {
inputSpecificPenalties.add(penalty);
isEmpty = false;
hasNonGeneric = true;
}
}
@Override
public <RI> void addGenericPenalty(final Class<RI> registeredInput,
final GenericPenalty<RI> penalty) {
@SuppressWarnings("unchecked")
GenericNode<RI> node = (GenericNode<RI>) genericPenalties.get(registeredInput);
if (node == null) {
node = new GenericNode<RI>();
genericPenalties.put(registeredInput, node);
}
node.penalties.add(penalty);
isEmpty = false;
hasGeneric = true;
}
@Override
public <RI, I extends RI> void applyGenericPenaltiesPrecisely(
final Class<RI> type, final I input) {
@SuppressWarnings("unchecked")
final GenericNode<RI> node = (GenericNode<RI>) genericPenalties.get(type);
if (node != null) {
node.apply(input);
}
}
@SuppressWarnings("unchecked")
@Override
public <I> void applyAllApplicableGenericPenalties(final I input) {
final Class<?> inputClass = input.getClass();
for (final Entry<Class<?>, GenericNode<?>> entry : genericPenalties.entrySet()) {
if (entry.getKey().isAssignableFrom(inputClass)) {
((GenericNode<? super I>) entry.getValue()).apply(input);
}
}
}
@Override
public void applyNonGenericPenalties(Object input) {
for (final InputSpecificPenalty penalty : inputSpecificPenalties) {
penalty.apply(input);
}
}
@Override
public boolean isEmpty() {
return isEmpty;
}
@Override
public boolean hasGenericPenalties() {
return hasGeneric;
}
@Override
public boolean hasNonGenericPenalties() {
return hasNonGeneric;
}
}

View File

@ -30,4 +30,21 @@ public interface GenericPenalty<RI> extends InputSpecificPenalty {
*/
public Class<RI> getRegisteredInput();
/**
* Internal convenience method to get around some of generics.
* <hr>
* <b>This method must not call
* {@link IPenaltyList#addInputSpecificPenalty(InputSpecificPenalty)}</b>
*
* @param penaltyList
*/
public void addToPenaltyList(IPenaltyList penaltyList);
/**
* Alternative to {@link #apply(Object)}, just typed.
*
* @param input
*/
public void applyPrecisely(RI input);
}

View File

@ -15,7 +15,19 @@
package fr.neatmonster.nocheatplus.actions.types.penalty;
/**
* Contain applicable penalty types that need to be handled outside of ViolationData.executeActions, for access by ViolationData.
* Contain applicable penalty types that need to be handled outside of
* ViolationData.executeActions, for access by ViolationData.
* <hr/>
* Excluded should be:
* <ul>
* <li>Penalties only applying to the player.</li>
* </ul>
* Specifically to be contained are:
* <ul>
* <li>InputSpecificPenalty</li>
* <li>Generic (input specific) penalties.</li>
* <ul/>
* <hr/>
*
* @author asofold
*
@ -24,25 +36,54 @@ public interface IPenaltyList {
// TODO: Typed ? + typed per input getter (mapped lists)
/**
* Add an input-specific penalty.
* Add an input-specific penalty. Generic penalties are stored extra for
* more efficient processing.
*
* @param penalty
*/
public void addInputSpecificPenalty(InputSpecificPenalty penalty);
/**
* Apply input specific penalties registered exactly for the given type,
* using the given input.
* Generic method to let the JVM deal with generics.
*
* @param input
* @param registeredInput
* @param penalty
*/
public <RI, I extends RI> void applyInputSpecificPenaltiesPrecisely(Class<RI> type, I input);
public <RI> void addGenericPenalty(Class<RI> registeredInput, GenericPenalty<RI> penalty);
/**
* Apply all penalties registered for the type and all super types of the
* Apply generic penalties registered exactly for the given type, using the
* given input.
*
* @param input
*/
public <I> void applyAllApplicableInputSpecificPenalties(I input);
public <RI, I extends RI> void applyGenericPenaltiesPrecisely(Class<RI> type, I input);
/**
* Apply all generic penalties registered for the type and all super types
* of the given input
*
* @param input
*/
public <I> void applyAllApplicableGenericPenalties(I input);
/**
* Specifically apply non-generic penalties.
*
* @param input
*/
public void applyNonGenericPenalties(Object input);
public boolean isEmpty();
public boolean hasGenericPenalties();
/**
* Test for InputSpecificPenalty instances that are not GenericPenalty
* instances.
*
* @return
*/
public boolean hasNonGenericPenalties();
}

View File

@ -19,6 +19,15 @@ public interface InputSpecificPenalty extends Penalty {
/**
* Apply the input-specific effects of a penalty, for other input than
* Player.
* <hr/>
* Applying input specific penalties might only be possible within the
* surrounding context of creation of ViolationData, i.e. during the event
* handling. Input-specific effects will not apply within
* ViolationData.executeActions, be it within the TickTask
* (requestActionsExecution) or during handling a primary-thread check
* failure. Instead input specific penalties are executed within the context
* that provides the input, e.g. after handling a damage event.
* <hr/>
*
* @param input
* May be of unexpected type.

View File

@ -35,14 +35,18 @@ public interface Penalty {
/**
* Test if there are input-specific effects, other than with Player instance
* input.
* <hr/>
* Applying input specific penalties might only be possible within the
* surrounding context of creation of ViolationData, i.e. during the event
* handling. Input-specific effects will not apply within
* ViolationData.executeActions, be it within the TickTask
* (requestActionsExecution) or during handling a primary-thread check
* failure. Instead input specific penalties are executed within the context
* that provides the input, e.g. after handling a damage event.
* <hr/>
*
* @return If true, this instance must implement InputSpecificPenalty as
* well. Applying input specific penalties might only be possible
* within the surrounding context of creation of ViolationData, i.e.
* during the event handling. Input-specific effects will not apply
* within ViolationData.executeActions, be it within the TickTask
* (requestActionsExecution) or during handling a primary-thread
* check failure.
* well.
*/
public boolean hasInputSpecificEffects();

View File

@ -79,7 +79,7 @@ public class ViolationData implements IViolationInfo, ActionData {
private final IPenaltyList penaltyList;
/**
* Instantiates a new violation data without syupport for input-specific penalties..
* Instantiates a new violation data without support for input-specific penalties..
* <hr>
* This constructor must be thread-safe for checks that might be executed
* outside of the primary thread.

View File

@ -23,6 +23,7 @@ import org.bukkit.potion.PotionEffectType;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.actions.types.penalty.IPenaltyList;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
@ -59,7 +60,7 @@ public class Critical extends Check {
*/
public boolean check(final Player player, final Location loc,
final FightData data, final FightConfig cc,
final IPlayerData pData) {
final IPlayerData pData, final IPenaltyList penaltyList) {
boolean cancel = false;
final double mcFallDistance = (double) player.getFallDistance();

View File

@ -34,6 +34,8 @@ import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.actions.types.penalty.DefaultPenaltyList;
import fr.neatmonster.nocheatplus.actions.types.penalty.IPenaltyList;
import fr.neatmonster.nocheatplus.checks.CheckListener;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.combined.Combined;
@ -170,7 +172,8 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
private boolean handleNormalDamage(final Player player, final boolean attackerIsFake,
final Entity damaged, final boolean damagedIsFake,
final double originalDamage, final double finalDamage,
final int tick, final FightData data, final IPlayerData pData) {
final int tick, final FightData data, final IPlayerData pData,
final IPenaltyList penaltyList) {
final FightConfig cc = pData.getGenericInstance(FightConfig.class);
@ -337,7 +340,8 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
}
// TODO: Consider to always check improbable (first?). At least if config.always or speed or net.attackfrequency are enabled.
if (!cancelled && critical.isEnabled(player, pData) && critical.check(player, loc, data, cc, pData)) {
if (!cancelled && critical.isEnabled(player, pData)
&& critical.check(player, loc, data, cc, pData, penaltyList)) {
// TODO: Check config for settings.
cancelled = true;
}
@ -565,17 +569,22 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
final FightData damagedData;
final boolean damagedIsDead = damaged.isDead();
final boolean damagedIsFake = !crossPlugin.getHandle().isNativeEntity(damaged);
IPenaltyList penaltyList = null;
if (damagedPlayer != null) {
final IPlayerData damagedPData = DataManager.getPlayerData(damagedPlayer);
damagedData = damagedPData.getGenericInstance(FightData.class);
if (!damagedIsDead) {
// God mode check.
// (Do not test the savage.)
if (damagedPData.isCheckActive(CheckType.FIGHT_GODMODE, damagedPlayer)
&& godMode.check(damagedPlayer, damagedIsFake,
BridgeHealth.getDamage(event), damagedData, damagedPData)) {
// It requested to "cancel" the players invulnerability, so set their noDamageTicks to 0.
damagedPlayer.setNoDamageTicks(0);
if (damagedPData.isCheckActive(CheckType.FIGHT_GODMODE, damagedPlayer)) {
if (penaltyList == null) {
penaltyList = new DefaultPenaltyList();
}
if (godMode.check(damagedPlayer, damagedIsFake,
BridgeHealth.getDamage(event), damagedData, damagedPData)) {
// It requested to "cancel" the players invulnerability, so set their noDamageTicks to 0.
damagedPlayer.setNoDamageTicks(0);
}
}
// Adjust buffer for fast heal checks.
if (BridgeHealth.getHealth(damagedPlayer) >= BridgeHealth.getMaxHealth(damagedPlayer)) {
@ -602,9 +611,23 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// Attacking entities.
if (event instanceof EntityDamageByEntityEvent) {
if (penaltyList == null) {
penaltyList = new DefaultPenaltyList();
}
onEntityDamageByEntity(damaged, damagedPlayer, damagedIsDead, damagedIsFake,
damagedData, (EntityDamageByEntityEvent) event);
damagedData, (EntityDamageByEntityEvent) event,
penaltyList);
}
if (penaltyList != null && !penaltyList.isEmpty()) {
if (penaltyList.hasGenericPenalties()) {
penaltyList.applyAllApplicableGenericPenalties(event);
}
if (penaltyList.hasNonGenericPenalties()) {
penaltyList.applyNonGenericPenalties(event);
}
}
}
/**
@ -618,7 +641,8 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
*/
private void onEntityDamageByEntity(final Entity damaged, final Player damagedPlayer,
final boolean damagedIsDead, final boolean damagedIsFake,
final FightData damagedData, final EntityDamageByEntityEvent event) {
final FightData damagedData, final EntityDamageByEntityEvent event,
final IPenaltyList penaltyList) {
final Entity damager = event.getDamager();
final int tick = TickTask.getTick();
if (damagedPlayer != null && !damagedIsDead) {
@ -658,7 +682,8 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
UnusedVelocity.checkUnusedVelocity(attacker, CheckType.FIGHT, attackerPData);
}
// Workaround for subsequent melee damage eventsfor explosions. TODO: Legacy or not, need a KB.
if (damageCause == DamageCause.BLOCK_EXPLOSION || damageCause == DamageCause.ENTITY_EXPLOSION) {
if (damageCause == DamageCause.BLOCK_EXPLOSION
|| damageCause == DamageCause.ENTITY_EXPLOSION) {
// NOTE: Pigs don't have data.
attackerData.lastExplosionEntityId = damaged.getEntityId();
attackerData.lastExplosionDamageTick = tick;
@ -688,7 +713,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
else if (handleNormalDamage(player, !crossPlugin.getHandle().isNativePlayer(player),
damaged, damagedIsFake,
BridgeHealth.getOriginalDamage(event), BridgeHealth.getFinalDamage(event),
tick, attackerData, attackerPData)) {
tick, attackerData, attackerPData, penaltyList)) {
event.setCancelled(true);
}
}