NoCheatPlus/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingConfig.java

394 lines
21 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.moving;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.actions.ActionList;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.access.ACheckConfig;
import fr.neatmonster.nocheatplus.checks.moving.magic.Magic;
import fr.neatmonster.nocheatplus.checks.moving.model.ModelFlying;
import fr.neatmonster.nocheatplus.checks.moving.player.PlayerSetBackMethod;
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
import fr.neatmonster.nocheatplus.command.CommandUtil;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.versions.Bugs;
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.utilities.ColorUtil;
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleCharPrefixTree;
import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation;
import fr.neatmonster.nocheatplus.worlds.IWorldData;
/**
* Configurations specific for the moving checks. Every world gets one of these
* assigned to it.
*/
public class MovingConfig extends ACheckConfig {
// Model flying ids.
public static final String ID_JETPACK_ELYTRA = "jetpack.elytra";
public static final String ID_POTION_LEVITATION = "potion.levitation";
// INSTANCE
public final boolean ignoreCreative;
public final boolean ignoreAllowFlight;
private final Map<GameMode, ModelFlying> flyingModelGameMode = new HashMap<GameMode, ModelFlying>();
private final ModelFlying flyingModelElytra;
private final ModelFlying flyingModelLevitation;
public final ActionList creativeFlyActions;
/** Assumed number of packets per second under ideal conditions. */
public final float morePacketsEPSIdeal;
/** The maximum number of packets per second that we accept. */
public final float morePacketsEPSMax;
public final int morePacketsEPSBuckets;
public final float morePacketsBurstPackets;
public final double morePacketsBurstDirect;
public final double morePacketsBurstEPM;
public final int morePacketsSetBackAge;
public final ActionList morePacketsActions;
/**
* Deal damage instead of Minecraft, whenever a player is judged to be on
* ground.
*/
public final boolean noFallDealDamage;
public final boolean noFallSkipAllowFlight;
/**
* Reset data on violation, i.e. a player taking fall damage without being
* on ground.
*/
public final boolean noFallViolationReset;
/** Reset data on tp. */
public final boolean noFallTpReset;
/** Reset if in vehicle. */
public final boolean noFallVehicleReset;
/** Reset fd to 0 if on ground (dealdamage only). */
public final boolean noFallAntiCriticals;
public final ActionList noFallActions;
// TODO: passableAccuracy: also use if not using ray-tracing
public final ActionList passableActions;
public final boolean passableUntrackedTeleportCheck;
public final boolean passableUntrackedCommandCheck;
public final boolean passableUntrackedCommandTryTeleport;
public final SimpleCharPrefixTree passableUntrackedCommandPrefixes = new SimpleCharPrefixTree();
public final int survivalFlyBlockingSpeed;
public final int survivalFlySneakingSpeed;
public final int survivalFlySpeedingSpeed;
public final int survivalFlySprintingSpeed;
public final int survivalFlySwimmingSpeed;
public final int survivalFlyWalkingSpeed;
public final boolean survivalFlyCobwebHack;
public final boolean sfSlownessSprintHack;
/**
* If true, will allow moderate bunny hop without lift off. Applies for
* normal speed on 1.6.4 and probably below.
*/
public final boolean sfGroundHop;
public final boolean survivalFlyAccountingH;
public final boolean survivalFlyAccountingV;
public final boolean sfSetBackPolicyVoid;
public final boolean sfSetBackPolicyFallDamage;
public final double sfStepHeight;
public final long survivalFlyVLFreeze;
public final ActionList survivalFlyActions;
public final boolean sfHoverCheck; // TODO: Sub check ?
public final int sfHoverTicks;
public final int sfHoverLoginTicks;
public final boolean sfHoverFallDamage;
public final double sfHoverViolation;
// Special tolerance values:
/** Horizontal buffer (rather sf), after failure leniency. */
public final double hBufMax;
/**
* Number of moving packets until which a velocity entry must be activated,
* in order to not be removed.
*/
public final int velocityActivationCounter;
/** Server ticks until invalidating queues velocity. */
public final int velocityActivationTicks;
public final boolean velocityStrictInvalidation;
public final double noFallyOnGround;
public final double yOnGround;
// General things.
/**
* If to allow splitting moves, due to player.getLocation reflecting
* something else than from/to.
*/
public final boolean splitMoves;
public final boolean ignoreStance;
public final boolean tempKickIllegal;
public final boolean loadChunksOnJoin;
public final boolean loadChunksOnMove;
public final boolean loadChunksOnTeleport;
public final boolean loadChunksOnWorldChange;
public final long sprintingGrace;
public final boolean assumeSprint;
public final int speedGrace;
public final boolean enforceLocation;
public final boolean trackBlockMove;
public final PlayerSetBackMethod playerSetBackMethod;
// Vehicles
public final boolean vehicleEnforceLocation;
public final boolean vehiclePreventDestroyOwn;
public final boolean scheduleVehicleSetBacks;
public final Set<EntityType> ignoredVehicles = new HashSet<EntityType>();
public final ActionList vehicleMorePacketsActions;
public final HashMap<EntityType, Double> vehicleEnvelopeHorizontalSpeedCap = new HashMap<EntityType, Double>();
public final ActionList vehicleEnvelopeActions;
// Trace
public final int traceMaxAge;
public final int traceMaxSize;
// Messages.
public final String msgKickIllegalMove;
public final String msgKickIllegalVehicleMove;
/**
* Instantiates a new moving configuration.
*
* @param config
* the data
*/
public MovingConfig(final IWorldData worldData) {
super(worldData);
final ConfigFile config = worldData.getRawConfiguration();
ignoreCreative = config.getBoolean(ConfPaths.MOVING_CREATIVEFLY_IGNORECREATIVE);
ignoreAllowFlight = config.getBoolean(ConfPaths.MOVING_CREATIVEFLY_IGNOREALLOWFLIGHT);
final ModelFlying defaultModel = new ModelFlying("gamemode.creative", config,
ConfPaths.MOVING_CREATIVEFLY_MODEL + "creative.", new ModelFlying().lock());
for (final GameMode gameMode : GameMode.values()) {
flyingModelGameMode.put(gameMode, new ModelFlying("gamemode." + gameMode.name().toLowerCase(), config,
ConfPaths.MOVING_CREATIVEFLY_MODEL + (gameMode.name().toLowerCase()) + ".", defaultModel).lock());
}
flyingModelLevitation = new ModelFlying(ID_POTION_LEVITATION, config,
ConfPaths.MOVING_CREATIVEFLY_MODEL + "levitation.",
new ModelFlying(null, defaultModel).scaleLevitationEffect(true).lock());
flyingModelElytra = new ModelFlying(ID_JETPACK_ELYTRA, config,
ConfPaths.MOVING_CREATIVEFLY_MODEL + "elytra.",
new ModelFlying(null, defaultModel).verticalAscendGliding(true).lock());
creativeFlyActions = config.getOptimizedActionList(ConfPaths.MOVING_CREATIVEFLY_ACTIONS,
Permissions.MOVING_CREATIVEFLY);
morePacketsEPSIdeal = config.getInt(ConfPaths.MOVING_MOREPACKETS_EPSIDEAL);
morePacketsEPSMax = Math.max(morePacketsEPSIdeal, config.getInt(ConfPaths.MOVING_MOREPACKETS_EPSMAX));
morePacketsEPSBuckets = 2 * Math.max(1, Math.min(60, config.getInt(ConfPaths.MOVING_MOREPACKETS_SECONDS)));
morePacketsBurstPackets = config.getInt(ConfPaths.MOVING_MOREPACKETS_BURST_EPM);
morePacketsBurstDirect = config.getInt(ConfPaths.MOVING_MOREPACKETS_BURST_DIRECT);
morePacketsBurstEPM = config.getInt(ConfPaths.MOVING_MOREPACKETS_BURST_EPM);
morePacketsSetBackAge = config.getInt(ConfPaths.MOVING_MOREPACKETS_SETBACKAGE);
morePacketsActions = config.getOptimizedActionList(ConfPaths.MOVING_MOREPACKETS_ACTIONS, Permissions.MOVING_MOREPACKETS);
noFallDealDamage = config.getBoolean(ConfPaths.MOVING_NOFALL_DEALDAMAGE);
noFallSkipAllowFlight = config.getBoolean(ConfPaths.MOVING_NOFALL_SKIPALLOWFLIGHT);
noFallViolationReset = config.getBoolean(ConfPaths.MOVING_NOFALL_RESETONVL);
noFallTpReset = config.getBoolean(ConfPaths.MOVING_NOFALL_RESETONTP);
noFallVehicleReset = config.getBoolean(ConfPaths.MOVING_NOFALL_RESETONVEHICLE);
noFallAntiCriticals = config.getBoolean(ConfPaths.MOVING_NOFALL_ANTICRITICALS);
noFallActions = config.getOptimizedActionList(ConfPaths.MOVING_NOFALL_ACTIONS, Permissions.MOVING_NOFALL);
passableActions = config.getOptimizedActionList(ConfPaths.MOVING_PASSABLE_ACTIONS, Permissions.MOVING_PASSABLE);
passableUntrackedTeleportCheck = config.getBoolean(ConfPaths.MOVING_PASSABLE_UNTRACKED_TELEPORT_ACTIVE);
passableUntrackedCommandCheck = config.getBoolean(ConfPaths.MOVING_PASSABLE_UNTRACKED_CMD_ACTIVE);
passableUntrackedCommandTryTeleport = config.getBoolean(ConfPaths.MOVING_PASSABLE_UNTRACKED_CMD_TRYTELEPORT);
CommandUtil.feedCommands(passableUntrackedCommandPrefixes, config, ConfPaths.MOVING_PASSABLE_UNTRACKED_CMD_PREFIXES, true);
// Default values are specified here because this settings aren't showed by default into the configuration file.
survivalFlyBlockingSpeed = config.getInt(ConfPaths.MOVING_SURVIVALFLY_BLOCKINGSPEED, 100);
survivalFlySneakingSpeed = config.getInt(ConfPaths.MOVING_SURVIVALFLY_SNEAKINGSPEED, 100);
survivalFlySpeedingSpeed = config.getInt(ConfPaths.MOVING_SURVIVALFLY_SPEEDINGSPEED, 200);
survivalFlySprintingSpeed = config.getInt(ConfPaths.MOVING_SURVIVALFLY_SPRINTINGSPEED, 100);
survivalFlySwimmingSpeed = config.getInt(ConfPaths.MOVING_SURVIVALFLY_SWIMMINGSPEED, 100);
survivalFlyWalkingSpeed = config.getInt(ConfPaths.MOVING_SURVIVALFLY_WALKINGSPEED, 100);
survivalFlyCobwebHack = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_COBWEBHACK, true);
sfSlownessSprintHack = config.getAlmostBoolean(ConfPaths.MOVING_SURVIVALFLY_SLOWNESSSPRINTHACK, AlmostBoolean.MAYBE).decideOptimistically();
sfGroundHop = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_GROUNDHOP, ServerVersion.compareMinecraftVersion("1.7") == -1);
survivalFlyAccountingH = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_EXTENDED_HACC, false);
survivalFlyAccountingV = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_EXTENDED_VACC);
sfSetBackPolicyFallDamage = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_SETBACKPOLICY_FALLDAMAGE);
sfSetBackPolicyVoid = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_SETBACKPOLICY_VOIDTOVOID);
final double sfStepHeight = config.getDouble(ConfPaths.MOVING_SURVIVALFLY_STEPHEIGHT, Double.MAX_VALUE);
if (sfStepHeight == Double.MAX_VALUE) {
final String ref;
if (Bukkit.getVersion().toLowerCase().indexOf("spigot") != -1) {
// Assume 1.8 clients being supported.
ref = "1.7.10";
} else {
ref = "1.8";
}
this.sfStepHeight = ServerVersion.select(ref, 0.5, 0.6, 0.6, 0.5).doubleValue();
} else {
this.sfStepHeight = sfStepHeight;
}
survivalFlyVLFreeze = config.getLong(ConfPaths.MOVING_SURVIVALFLY_VLFREEZE, 2000L);
survivalFlyActions = config.getOptimizedActionList(ConfPaths.MOVING_SURVIVALFLY_ACTIONS, Permissions.MOVING_SURVIVALFLY);
sfHoverCheck = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_HOVER_CHECK);
sfHoverTicks = config.getInt(ConfPaths.MOVING_SURVIVALFLY_HOVER_TICKS);
sfHoverLoginTicks = Math.max(0, config.getInt(ConfPaths.MOVING_SURVIVALFLY_HOVER_LOGINTICKS));
sfHoverFallDamage = config.getBoolean(ConfPaths.MOVING_SURVIVALFLY_HOVER_FALLDAMAGE);
sfHoverViolation = config.getDouble(ConfPaths.MOVING_SURVIVALFLY_HOVER_SFVIOLATION);
hBufMax = config.getDouble(ConfPaths.MOVING_SURVIVALFLY_HBUFMAX);
velocityActivationCounter = config.getInt(ConfPaths.MOVING_VELOCITY_ACTIVATIONCOUNTER);
velocityActivationTicks = config.getInt(ConfPaths.MOVING_VELOCITY_ACTIVATIONTICKS);
velocityStrictInvalidation = config.getBoolean(ConfPaths.MOVING_VELOCITY_STRICTINVALIDATION);
yOnGround = config.getDouble(ConfPaths.MOVING_YONGROUND, Magic.Y_ON_GROUND_MIN, Magic.Y_ON_GROUND_MAX, Magic.Y_ON_GROUND_DEFAULT); // sqrt(1/256), see: NetServerHandler.
noFallyOnGround = config.getDouble(ConfPaths.MOVING_NOFALL_YONGROUND, Magic.Y_ON_GROUND_MIN, Magic.Y_ON_GROUND_MAX, yOnGround);
AlmostBoolean refSplitMoves = config.getAlmostBoolean(ConfPaths.MOVING_SPLITMOVES, AlmostBoolean.MAYBE);
//splitMoves = refSplitMoves == AlmostBoolean.MAYBE ? ServerVersion.compareMinecraftVersion("1.9") == -1 : refSplitMoves.decide();
splitMoves = refSplitMoves.decideOptimistically();
// TODO: Ignore the stance, once it is known that the server catches such.
AlmostBoolean refIgnoreStance = config.getAlmostBoolean(ConfPaths.MOVING_IGNORESTANCE, AlmostBoolean.MAYBE);
ignoreStance = refIgnoreStance == AlmostBoolean.MAYBE ? ServerVersion.compareMinecraftVersion("1.8") >= 0 : refIgnoreStance.decide();
tempKickIllegal = config.getBoolean(ConfPaths.MOVING_TEMPKICKILLEGAL);
loadChunksOnJoin = config.getBoolean(ConfPaths.MOVING_LOADCHUNKS_JOIN);
loadChunksOnMove = config.getBoolean(ConfPaths.MOVING_LOADCHUNKS_MOVE);
loadChunksOnTeleport = config.getBoolean(ConfPaths.MOVING_LOADCHUNKS_TELEPORT);
loadChunksOnWorldChange = config.getBoolean(ConfPaths.MOVING_LOADCHUNKS_WORLDCHANGE);
sprintingGrace = Math.max(0L, (long) (config.getDouble(ConfPaths.MOVING_SPRINTINGGRACE) * 1000.0)); // Config: seconds.
assumeSprint = config.getBoolean(ConfPaths.MOVING_ASSUMESPRINT);
speedGrace = Math.max(0, (int) Math.round(config.getDouble(ConfPaths.MOVING_SPEEDGRACE) * 20.0)); // Config: seconds
AlmostBoolean ref = config.getAlmostBoolean(ConfPaths.MOVING_ENFORCELOCATION, AlmostBoolean.MAYBE);
if (ref == AlmostBoolean.MAYBE) {
enforceLocation = Bugs.shouldEnforceLocation();
} else {
enforceLocation = ref.decide();
}
// TODO: Rename overall flag to trackBlockChanges. Create a sub-config rather.
trackBlockMove = config.getBoolean(ConfPaths.COMPATIBILITY_BLOCKS_CHANGETRACKER_ACTIVE)
&& (config.getBoolean(ConfPaths.COMPATIBILITY_BLOCKS_CHANGETRACKER_PISTONS
// TODO: || other activation flags.
));
final PlayerSetBackMethod playerSetBackMethod = PlayerSetBackMethod.fromString(
"extern.fromconfig", config.getString(ConfPaths.MOVING_SETBACK_METHOD));
if (playerSetBackMethod.doesThisMakeSense()) {
// (Might info/warn if legacy is used without setTo and without SCHEDULE and similar?)
this.playerSetBackMethod = playerSetBackMethod;
}
else if (ServerVersion.compareMinecraftVersion("1.9") < 0) {
this.playerSetBackMethod = PlayerSetBackMethod.LEGACY;
}
else {
// Latest.
this.playerSetBackMethod = PlayerSetBackMethod.MODERN;
}
traceMaxAge = config.getInt(ConfPaths.MOVING_TRACE_MAXAGE, 200);
traceMaxSize = config.getInt(ConfPaths.MOVING_TRACE_MAXSIZE, 200);
ref = config.getAlmostBoolean(ConfPaths.MOVING_VEHICLE_ENFORCELOCATION, AlmostBoolean.MAYBE);
vehicleEnforceLocation = ref.decideOptimistically(); // Currently rather enabled.
vehiclePreventDestroyOwn = config.getBoolean(ConfPaths.MOVING_VEHICLE_PREVENTDESTROYOWN);
scheduleVehicleSetBacks = config.getAlmostBoolean(ConfPaths.MOVING_VEHICLE_SCHEDULESETBACKS, AlmostBoolean.MAYBE).decide();
vehicleMorePacketsActions = config.getOptimizedActionList(ConfPaths.MOVING_VEHICLE_MOREPACKETS_ACTIONS, Permissions.MOVING_MOREPACKETS);
ref = config.getAlmostBoolean(ConfPaths.MOVING_VEHICLE_ENVELOPE_ACTIVE, AlmostBoolean.MAYBE);
if (ServerVersion.compareMinecraftVersion("1.9") < 0) {
worldData.overrideCheckActivation(CheckType.MOVING_VEHICLE_ENVELOPE,
AlmostBoolean.NO, OverrideType.PERMANENT, true);
}
config.readDoubleValuesForEntityTypes(ConfPaths.MOVING_VEHICLE_ENVELOPE_HSPEEDCAP, vehicleEnvelopeHorizontalSpeedCap, 4.0, true);
vehicleEnvelopeActions = config.getOptimizedActionList(ConfPaths.MOVING_VEHICLE_ENVELOPE_ACTIONS, Permissions.MOVING_VEHICLE_ENVELOPE);
// Ignored vehicle types (ignore mostly, no checks run).
List<String> types;
if (config.get(ConfPaths.MOVING_VEHICLE_IGNOREDVEHICLES) == null) { // Hidden setting for now.
// Use defaults.
types = Arrays.asList("arrow", "spectral_arrow", "tipped_arrow");
}
else {
types = config.getStringList(ConfPaths.MOVING_VEHICLE_IGNOREDVEHICLES);
}
for (String stype : types) {
try {
EntityType type = EntityType.valueOf(stype.toUpperCase());
if (type != null) {
ignoredVehicles.add(type);
}
}
catch (IllegalArgumentException e) {}
}
// Messages.
msgKickIllegalMove = ColorUtil.replaceColors(config.getString(ConfPaths.MOVING_MESSAGE_ILLEGALPLAYERMOVE));
msgKickIllegalVehicleMove = ColorUtil.replaceColors(config.getString(ConfPaths.MOVING_MESSAGE_ILLEGALVEHICLEMOVE));
}
public ModelFlying getModelFlying(final Player player, final PlayerLocation fromLocation,
final MovingData data, final MovingConfig cc) {
final GameMode gameMode = player.getGameMode();
final ModelFlying modelGameMode = flyingModelGameMode.get(gameMode);
switch(gameMode) {
case SURVIVAL:
case ADVENTURE:
case CREATIVE:
// Specific checks.
break;
default:
// Default by game mode (spectator, yet unknown).
return modelGameMode;
}
final boolean isGlidingWithElytra = Bridge1_9.isGlidingWithElytra(player)
&& MovingUtil.isGlidingWithElytraValid(player, fromLocation, data, cc);
// Actual flying (ignoreAllowFlight is a legacy option for rocket boots like flying).
if (player.isFlying() || !isGlidingWithElytra && !ignoreAllowFlight && player.getAllowFlight()) {
return modelGameMode;
}
// Elytra.
if (isGlidingWithElytra) { // Defensive: don't demand isGliding.
return flyingModelElytra;
}
// Levitation.
if (gameMode != GameMode.CREATIVE && !Double.isInfinite(Bridge1_9.getLevitationAmplifier(player))
&& !fromLocation.isInLiquid()) {
return flyingModelLevitation;
}
// Default by game mode.
return modelGameMode;
}
}