280 lines
10 KiB
Java
280 lines
10 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.utilities;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Random;
|
|
import java.util.logging.Level;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
|
import fr.neatmonster.nocheatplus.checks.access.ICheckConfig;
|
|
import fr.neatmonster.nocheatplus.checks.access.ICheckData;
|
|
import fr.neatmonster.nocheatplus.checks.blockbreak.BlockBreakData;
|
|
import fr.neatmonster.nocheatplus.checks.combined.CombinedData;
|
|
import fr.neatmonster.nocheatplus.checks.fight.FightData;
|
|
import fr.neatmonster.nocheatplus.checks.inventory.InventoryData;
|
|
import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
|
|
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
|
|
import fr.neatmonster.nocheatplus.logging.StaticLog;
|
|
import fr.neatmonster.nocheatplus.logging.Streams;
|
|
import fr.neatmonster.nocheatplus.permissions.RegisteredPermission;
|
|
import fr.neatmonster.nocheatplus.players.PlayerData;
|
|
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
|
|
|
|
|
|
/**
|
|
* Random auxiliary gear, some might have general quality. Contents are likely
|
|
* to get moved to other classes. All that is in here should be set up with
|
|
* checks and not be related to early setup stages of the plugin.
|
|
*/
|
|
public class CheckUtils {
|
|
|
|
/**
|
|
* Improper API access.
|
|
*
|
|
* @param checkType
|
|
* the check type
|
|
*/
|
|
private static void improperAPIAccess(final CheckType checkType) {
|
|
// TODO: Log once + examine stack (which plugins/things are involved).
|
|
final String trace = Arrays.toString(Thread.currentThread().getStackTrace());
|
|
StaticLog.logOnce(Streams.STATUS, Level.SEVERE, "Off primary thread call to hasByPass for " + checkType, trace);
|
|
}
|
|
|
|
/**
|
|
* Kick and log.
|
|
*
|
|
* @param player
|
|
* the player
|
|
* @param cc
|
|
* the cc
|
|
*/
|
|
public static void kickIllegalMove(final Player player, final MovingConfig cc){
|
|
player.kickPlayer(cc.msgKickIllegalMove);
|
|
StaticLog.logWarning("[NCP] Disconnect " + player.getName() + " due to illegal move!");
|
|
}
|
|
|
|
/**
|
|
* Guess some last-action time, likely to be replaced with centralized
|
|
* PlayerData use.
|
|
*
|
|
* @param player
|
|
* the player
|
|
* @param now
|
|
* the now
|
|
* @param maxAge
|
|
* Maximum age in milliseconds.
|
|
* @return Return timestamp or Long.MIN_VALUE if not possible or beyond
|
|
* maxAge.
|
|
*/
|
|
public static final long guessKeepAliveTime(final Player player, final long now, final long maxAge){
|
|
final int tick = TickTask.getTick();
|
|
long ref = Long.MIN_VALUE;
|
|
// Estimate last fight action time (important for gode modes).
|
|
final FightData fData = FightData.getData(player);
|
|
ref = Math.max(ref, fData.speedBuckets.lastUpdate());
|
|
ref = Math.max(ref, now - 50L * (tick - fData.lastAttackTick)); // Ignore lag.
|
|
// Health regain (not unimportant).
|
|
ref = Math.max(ref, fData.regainHealthTime);
|
|
// Move time.
|
|
ref = Math.max(ref, CombinedData.getData(player).lastMoveTime);
|
|
// Inventory.
|
|
final InventoryData iData = InventoryData.getData(player);
|
|
ref = Math.max(ref, iData.lastClickTime);
|
|
ref = Math.max(ref, iData.instantEatInteract);
|
|
// BlcokBreak/interact.
|
|
final BlockBreakData bbData = BlockBreakData.getData(player);
|
|
ref = Math.max(ref, bbData.frequencyBuckets.lastUpdate());
|
|
ref = Math.max(ref, bbData.fastBreakfirstDamage);
|
|
// TODO: More, less ...
|
|
if (ref > now || ref < now - maxAge){
|
|
return Long.MIN_VALUE;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
/**
|
|
* Check for config flag and exemption (hasBypass). Meant thread-safe.
|
|
*
|
|
* @param checkType
|
|
* the check type
|
|
* @param player
|
|
* the player
|
|
* @param data
|
|
* If data is null, the data factory will be used for the given
|
|
* check type.
|
|
* @param cc
|
|
* If config is null, the config factory will be used for the
|
|
* given check type.
|
|
* @return true, if is enabled
|
|
*/
|
|
public static boolean isEnabled(final CheckType checkType, final Player player,
|
|
final ICheckConfig cc, final PlayerData pData) {
|
|
if (cc == null) {
|
|
if (!checkType.isEnabled(player)) {
|
|
return false;
|
|
}
|
|
}
|
|
else if (!cc.isEnabled(checkType)) {
|
|
return false;
|
|
}
|
|
return !hasBypass(checkType, player, pData);
|
|
}
|
|
|
|
/**
|
|
* Check for exemption by permissions, API access, possibly other. Meant
|
|
* thread-safe.
|
|
*
|
|
* @see #hasBypass(CheckType, Player, ICheckData, boolean)
|
|
*
|
|
* @param checkType
|
|
* the check type
|
|
* @param player
|
|
* the player
|
|
* @param pData
|
|
* Must not be null.
|
|
* @return true, if successful
|
|
*/
|
|
public static boolean hasBypass(final CheckType checkType, final Player player, final PlayerData pData) {
|
|
// TODO: Checking for the thread might be a temporary measure.
|
|
return hasBypass(checkType, player, pData, Bukkit.isPrimaryThread());
|
|
}
|
|
|
|
/**
|
|
* Check for exemption by permissions, API access, possibly other. Meant
|
|
* thread-safe.
|
|
*
|
|
* @param checkType
|
|
* the check type
|
|
* @param player
|
|
* the player
|
|
* @param pData
|
|
* Must not be null.
|
|
* @param isPrimaryThread
|
|
* If set to true, this must be the primary server thread as
|
|
* returned by Bukkit.isPrimaryThread().
|
|
* @return true, if successful
|
|
*/
|
|
public static boolean hasBypass(final CheckType checkType, final Player player, final PlayerData pData,
|
|
final boolean isPrimaryThread) {
|
|
// TODO: Thread testing should be removed, once thread-safe read is implemented for exemption too.
|
|
/*
|
|
* TODO: Thread context information may be part of a CheckPipeline
|
|
* object later on (alongside PlayerData and other policy stuff),
|
|
* replacing some of the arguments (IsPrimaryThread must be rock solid
|
|
* information.).
|
|
*/
|
|
|
|
// TODO: Refine this error message +- put in place where it needs to be.
|
|
if (!isPrimaryThread && !CheckTypeUtil.needsSynchronization(checkType)) {
|
|
/*
|
|
* Checking for exemption can't cause harm anymore, however even
|
|
* fetching data or configuration might still lead to everything
|
|
* exploding.
|
|
*/
|
|
improperAPIAccess(checkType);
|
|
}
|
|
// Exemption check.
|
|
if (NCPExemptionManager.isExempted(player, checkType)) {
|
|
return true;
|
|
}
|
|
// Check permission policy/cache regardless of the thread context.
|
|
final RegisteredPermission permission = checkType.getPermission();
|
|
if (permission != null && pData.hasPermission(permission, player)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Static relay for the check-specific convenience methods, logging with
|
|
* standard format ([check_type] [player_name] ...).
|
|
*
|
|
* @param player
|
|
* May be null.
|
|
* @param checkType
|
|
* the check type
|
|
* @param message
|
|
* the message
|
|
*/
|
|
public static void debug(final Player player, final CheckType checkType, final String message) {
|
|
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, getLogMessagePrefix(player, checkType) + message);
|
|
}
|
|
|
|
/**
|
|
* Get the standard log message prefix with a trailing space.
|
|
*
|
|
* @param player
|
|
* May be null.
|
|
* @param checkType
|
|
* the check type
|
|
* @return the log message prefix
|
|
*/
|
|
public static String getLogMessagePrefix(final Player player, final CheckType checkType) {
|
|
String base = "[" + checkType + "] ";
|
|
if (player != null) {
|
|
base += "[" + player.getName() + "] ";
|
|
}
|
|
return base;
|
|
}
|
|
|
|
/**
|
|
* Convenience method to get a Random instance from the generic registry
|
|
* (NoCheatPlusAPI).
|
|
*
|
|
* @return the random
|
|
*/
|
|
public static Random getRandom() {
|
|
return NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Random.class);
|
|
}
|
|
|
|
/**
|
|
* Update and then reduce all given ActionFrequency instances by the given
|
|
* amount, capped at a maximum of 0 for the resulting first bucket score.
|
|
*
|
|
* @param amount
|
|
* The amount to subtract.
|
|
* @param freqs
|
|
*/
|
|
public static void reduce(final long time, final float amount, final ActionFrequency... freqs) {
|
|
for (int i = 0; i < freqs.length; i++) {
|
|
final ActionFrequency freq = freqs[i];
|
|
freq.update(time);
|
|
freq.setBucket(0, Math.max(0f, freq.bucketScore(0) - amount));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update and then reduce all given ActionFrequency instances by the given
|
|
* amount, without capping the result.
|
|
*
|
|
* @param amount
|
|
* The amount to subtract.
|
|
* @param freqs
|
|
*/
|
|
public static void subtract(final long time, final float amount, final ActionFrequency... freqs) {
|
|
for (int i = 0; i < freqs.length; i++) {
|
|
final ActionFrequency freq = freqs[i];
|
|
freq.update(time);
|
|
freq.setBucket(0, freq.bucketScore(0) - amount);
|
|
}
|
|
}
|
|
|
|
}
|