224 lines
8.5 KiB
Java
224 lines
8.5 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.fight;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
|
import fr.neatmonster.nocheatplus.checks.Check;
|
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
|
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
|
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
|
|
import fr.neatmonster.nocheatplus.compat.IBridgeCrossPlugin;
|
|
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
|
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
|
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
|
|
import fr.neatmonster.nocheatplus.utilities.TickTask;
|
|
|
|
/**
|
|
* The GodMode check will find out if a player tried to stay invulnerable after being hit or after dying.
|
|
*/
|
|
public class GodMode extends Check {
|
|
|
|
private final IGenericInstanceHandle<IBridgeCrossPlugin> crossPlugin = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstanceHandle(IBridgeCrossPlugin.class);
|
|
|
|
/**
|
|
* Instantiates a new god mode check.
|
|
*/
|
|
public GodMode() {
|
|
super(CheckType.FIGHT_GODMODE);
|
|
}
|
|
|
|
/**
|
|
* New style god mode check. Much more sensitive.
|
|
* @param player
|
|
* @param damage
|
|
* @return
|
|
*/
|
|
public boolean check(final Player player, final boolean playerIsFake,
|
|
final double damage, final FightData data, final IPlayerData pData){
|
|
final int tick = TickTask.getTick();
|
|
|
|
final int noDamageTicks = Math.max(0, player.getNoDamageTicks());
|
|
final int invulnerabilityTicks = playerIsFake ? 0 : mcAccess.getHandle().getInvulnerableTicks(player);
|
|
|
|
// TODO: cleanup this leugique beume...
|
|
|
|
boolean legit = false; // Return, reduce vl.
|
|
boolean set = false; // Set tick/ndt and return
|
|
boolean resetAcc = false; // Reset acc counter.
|
|
boolean resetAll = false; // Reset all and return
|
|
|
|
// Check difference to expectation:
|
|
final int dTick = tick - data.lastDamageTick;
|
|
final int dNDT = data.lastNoDamageTicks - noDamageTicks;
|
|
final int delta = dTick - dNDT;
|
|
|
|
final double health = BridgeHealth.getHealth(player);
|
|
|
|
// TODO: Adjust to double values.
|
|
|
|
if (data.godModeHealth > health ){
|
|
data.godModeHealthDecreaseTick = tick;
|
|
legit = set = resetAcc = true;
|
|
}
|
|
|
|
// TODO: Might account for ndt/2 on regain health (!).
|
|
|
|
// Invulnerable or inconsistent.
|
|
// TODO: might check as well if NCP has taken over invulnerable ticks of this player.
|
|
if (invulnerabilityTicks != Integer.MAX_VALUE && invulnerabilityTicks > 0 || tick < data.lastDamageTick){
|
|
// (Second criteria is for MCAccessBukkit.)
|
|
legit = set = resetAcc = true;
|
|
}
|
|
|
|
// Reset accumulator.
|
|
if (20 + data.godModeAcc < dTick || dTick > 40){
|
|
legit = resetAcc = true;
|
|
set = true; // TODO
|
|
}
|
|
|
|
// Check if reduced more than expected or new/count down fully.
|
|
// TODO: Mostly workarounds.
|
|
if (delta <= 0 || data.lastNoDamageTicks <= player.getMaximumNoDamageTicks() / 2 || dTick > data.lastNoDamageTicks || damage > BridgeHealth.getLastDamage(player)|| damage == 0.0){
|
|
// Not resetting acc.
|
|
legit = set = true;
|
|
}
|
|
|
|
if (dTick == 1 && noDamageTicks < 19){
|
|
set = true;
|
|
}
|
|
|
|
if (delta == 1){
|
|
// Ignore these, but keep reference value from before.
|
|
legit = true;
|
|
}
|
|
|
|
// Bukkit.getServer().broadcastMessage("God " + player.getName() + " delta=" + delta + " dt=" + dTick + " dndt=" + dNDT + " acc=" + data.godModeAcc + " d=" + damage + " ndt=" + noDamageTicks + " h=" + health + " slag=" + TickTask.getLag(dTick, true));
|
|
|
|
// TODO: might check last damage taken as well (really taken with health change)
|
|
|
|
// Resetting
|
|
data.godModeHealth = health;
|
|
|
|
if (resetAcc || resetAll){
|
|
data.godModeAcc = 0;
|
|
}
|
|
if (legit){
|
|
data.godModeVL *= 0.97;
|
|
}
|
|
if (resetAll){
|
|
// Reset all.
|
|
data.lastNoDamageTicks = 0;
|
|
data.lastDamageTick = 0;
|
|
return false;
|
|
}
|
|
else if (set){
|
|
// Only set the tick values.
|
|
data.lastNoDamageTicks = noDamageTicks;
|
|
data.lastDamageTick = tick;
|
|
return false;
|
|
}
|
|
else if (legit){
|
|
// Just return;
|
|
return false;
|
|
}
|
|
|
|
if (tick < data.godModeHealthDecreaseTick){
|
|
data.godModeHealthDecreaseTick = 0;
|
|
}
|
|
else{
|
|
final int dht = tick - data.godModeHealthDecreaseTick;
|
|
if (dht <= 20) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
final FightConfig cc = pData.getGenericInstance(FightConfig.class);
|
|
|
|
// Check for client side lag.
|
|
final long now = System.currentTimeMillis();
|
|
final long maxAge = cc.godModeLagMaxAge;
|
|
long keepAlive = Long.MIN_VALUE;
|
|
if (NCPAPIProvider.getNoCheatPlusAPI().hasFeatureTag("checks", "KeepAliveFrequency")) {
|
|
keepAlive = pData.getGenericInstance(NetData.class).lastKeepAliveTime;
|
|
}
|
|
keepAlive = Math.max(keepAlive, CheckUtils.guessKeepAliveTime(player, now, maxAge, pData));
|
|
|
|
if (keepAlive != Double.MIN_VALUE && now - keepAlive > cc.godModeLagMinAge && now - keepAlive < maxAge){
|
|
// Assume lag.
|
|
return false;
|
|
}
|
|
|
|
// Violation probably.
|
|
data.godModeAcc += delta;
|
|
|
|
boolean cancel = false;
|
|
// TODO: bounds
|
|
if (data.godModeAcc > 2){
|
|
// TODO: To match with old checks vls / actions, either change actions or apply a factor.
|
|
data.godModeVL += delta;
|
|
if (executeActions(player, data.godModeVL, delta,
|
|
pData.getGenericInstance(FightConfig.class).godModeActions).willCancel()){
|
|
cancel = true;
|
|
}
|
|
else {
|
|
cancel = false;
|
|
}
|
|
}
|
|
else{
|
|
cancel = false;
|
|
}
|
|
|
|
// Set tick values.
|
|
data.lastNoDamageTicks = noDamageTicks;
|
|
data.lastDamageTick = tick;
|
|
|
|
return cancel;
|
|
}
|
|
|
|
/**
|
|
* If a player apparently died, make sure they really die after some time if they didn't already, by setting up a
|
|
* Bukkit task.
|
|
*
|
|
* @param player
|
|
* the player
|
|
*/
|
|
public void death(final Player player) {
|
|
// TODO: Is this still relevant ?
|
|
// First check if the player is really dead (e.g. another plugin could have just fired an artificial event).
|
|
if (BridgeHealth.getHealth(player) <= 0.0 && player.isDead()
|
|
&& crossPlugin.getHandle().isNativeEntity(player)) {
|
|
try {
|
|
// Schedule a task to be executed in roughly 1.5 seconds.
|
|
// TODO: Get plugin otherwise !?
|
|
Bukkit.getScheduler().scheduleSyncDelayedTask(Bukkit.getPluginManager().getPlugin("NoCheatPlus"), new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
// Check again if the player should be dead, and if the game didn't mark them as dead.
|
|
if (mcAccess.getHandle().shouldBeZombie(player)){
|
|
// Artificially "kill" them.
|
|
mcAccess.getHandle().setDead(player, 19);
|
|
}
|
|
} catch (final Exception e) {}
|
|
}
|
|
}, 30);
|
|
} catch (final Exception e) {}
|
|
}
|
|
}
|
|
}
|