NoCheatPlus/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/blockbreak/FastBreak.java

162 lines
7.6 KiB
Java

/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.checks.blockbreak;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.utilities.PotionUtil;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
/**
* A check used to verify if the player isn't breaking blocks faster than possible.
*/
public class FastBreak extends Check {
/**
* Instantiates a new fast break check.
*/
public FastBreak() {
super(CheckType.BLOCKBREAK_FASTBREAK);
}
/**
* Checks a player for fastbreak. This is NOT for creative mode.
*
* @param player
* the player
* @param block
* the block
* @param isInstaBreak
* @param data
* @param cc
* @param elaspedTime
* @return true, if successful
*/
public boolean check(final Player player, final Block block, final AlmostBoolean isInstaBreak,
final BlockBreakConfig cc, final BlockBreakData data, final IPlayerData pData) {
final long now = System.currentTimeMillis();
boolean cancel = false;
// Determine expected breaking time by block type.
final Material blockType = block.getType();
final long expectedBreakingTime = Math.max(0, Math.round((double) BlockProperties.getBreakingDuration(blockType, player) * (double) cc.fastBreakModSurvival / 100D));
final long elapsedTime;
// TODO: Concept for unbreakable blocks? Context: extreme VL.
// TODO: Should it be breakingTime instead of 0 for inconsistencies?
if (cc.fastBreakStrict) {
// Counting interact...break.
elapsedTime = (data.fastBreakBreakTime > data.fastBreakfirstDamage) ? 0 : now - data.fastBreakfirstDamage;
}
else {
// Counting break...break.
elapsedTime = (data.fastBreakBreakTime > now) ? 0 : now - data.fastBreakBreakTime;
}
// Check if the time used time is lower than expected.
if (isInstaBreak.decideOptimistically()) {
// Ignore those for now.
// TODO: Find out why this was commented out long ago a) did not fix mcMMO b) exploits.
// TODO: Maybe adjust time to min(time, SOMETHING) for MAYBE/YES.
}
else if (elapsedTime < 0) {
// Ignore it. TODO: ?
}
else if (elapsedTime + cc.fastBreakDelay < expectedBreakingTime) {
// lag or cheat or Minecraft.
// Count in server side lag, if desired.
final float lag = pData.getCurrentWorldDataSafe().shouldAdjustToLag(type)
? TickTask.getLag(expectedBreakingTime, true) : 1f;
final long missingTime = expectedBreakingTime - (long) (lag * elapsedTime);
if (missingTime > 0) {
// Add as penalty
data.fastBreakPenalties.add(now, (float) missingTime);
// Only raise a violation, if the total penalty score exceeds the contention duration (for lag, delay).
if (data.fastBreakPenalties.score(cc.fastBreakBucketFactor) > cc.fastBreakGrace) {
// TODO: maybe add one absolute penalty time for big amounts to stop breaking until then
final double vlAdded = (double) missingTime / 1000.0;
data.fastBreakVL += vlAdded;
final ViolationData vd = new ViolationData(this, player, data.fastBreakVL, vlAdded, cc.fastBreakActions);
if (vd.needsParameters()) {
vd.setParameter(ParameterName.BLOCK_TYPE, blockType.toString());
}
cancel = executeActions(vd).willCancel();
}
// else: still within contention limits.
}
}
else if (expectedBreakingTime > cc.fastBreakDelay) {
// Fast breaking does not decrease violation level.
data.fastBreakVL *= 0.9D;
}
// TODO: Rework to use (then hopefully completed) BlockBreakKey.
if (pData.isDebugActive(type)) {
tailDebugStats(player, isInstaBreak, blockType,
elapsedTime, expectedBreakingTime, data, pData);
}
else {
data.stats = null;
}
// (The break time is set in the listener).
return cancel;
}
private void tailDebugStats(final Player player, final AlmostBoolean isInstaBreak,
final Material blockType, final long elapsedTime, final long expectedBreakingTime,
final BlockBreakData data, final IPlayerData pData) {
if (pData.hasPermission(Permissions.ADMINISTRATION_DEBUG, player)) {
// General stats:
// TODO: Replace stats by new system (BlockBreakKey once complete), commands to inspect / auto-config.
data.setStats();
data.stats.addStats(data.stats.getId(blockType+ "/u", true), elapsedTime);
data.stats.addStats(data.stats.getId(blockType + "/r", true), expectedBreakingTime);
player.sendMessage(data.stats.getStatsStr(true));
// Send info about current break:
final ItemStack stack = Bridge1_9.getItemInMainHand(player);
final boolean isValidTool = BlockProperties.isValidTool(blockType, stack);
final double haste = PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.FAST_DIGGING);
String msg = (isInstaBreak.decideOptimistically() ? ("[Insta=" + isInstaBreak + "]") : "[Normal]") + "[" + blockType + "] "+ elapsedTime + "u / " + expectedBreakingTime +"r (" + (isValidTool?"tool":"no-tool") + ")" + (Double.isInfinite(haste) ? "" : " haste=" + ((int) haste + 1));
player.sendMessage(msg);
// net.minecraft.server.Item mcItem = net.minecraft.server.Item.byId[stack.getTypeId()];
// if (mcItem != null) {
// double x = mcItem.getDestroySpeed(((CraftItemStack) stack).getHandle(), net.minecraft.server.Block.byId[blockId]);
// player.sendMessage("mc speed: " + x);
// }
}
}
}