/* * 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 . */ package fr.neatmonster.nocheatplus.checks.net.protocollib; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bukkit.Location; import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.reflect.StructureModifier; import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.net.NetConfig; import fr.neatmonster.nocheatplus.compat.versions.ServerVersion; import fr.neatmonster.nocheatplus.players.DataManager; import fr.neatmonster.nocheatplus.players.IPlayerData; import fr.neatmonster.nocheatplus.utilities.location.TrigUtil; public class SoundDistance extends BaseAdapter { // TODO: Will not be effective with 512 radius, if they add the patch by @Amranth. // TODO: For lower distances more packets might need to be intercepted. /** Partly by debugging, partly from various sources, possibly including wrong spelling. */ private static final Set effectNames = new HashSet(Arrays.asList( //////////// // PRE 1.9 //////////// // Weather "ambient.weather.thunder", // Wither "wither-spawn-sound-radius", "mob.wither.spawn", "mob.wither.shoot", "mob.wither.idle", "mob.wither.hurt", "mob.wither.death", // Enderdragon "dragon-death-sound-radius", "mob.enderdragon.wings", "mob.enderdragon.grow", "mob.enderdragon.growl", "mob.enderdragon.hit", "mob.enderdragon.end", "game.neutral.die", // Enderdragon 1.8.7 (debug). ////////////////// // 1.9 AND LATER ////////////////// // Weather "ENTITY_LIGHTNING_IMPACT", "ENTITY_LIGHTNING_THUNDER", // Enderdragon "ENTITY_ENDERDRAGON_AMBIENT", "ENTITY_ENDERDRAGON_DEATH", "ENTITY_ENDERDRAGON_FIREBALL_EXPLODE", "ENTITY_ENDERDRAGON_FLAP", "ENTITY_ENDERDRAGON_GROWL", "ENTITY_ENDERDRAGON_HURT", "ENTITY_ENDERDRAGON_SHOOT", // Wither "ENTITY_WITHER_AMBIENT", "ENTITY_WITHER_BREAK_BLOCK", "ENTITY_WITHER_DEATH", "ENTITY_WITHER_HURT", "ENTITY_WITHER_SHOOT", "ENTITY_WITHER_SPAWN" )); private final Integer idSoundEffectCancel = counters.registerKey("packet.sound.cancel"); private final Location useLoc = new Location(null, 0, 0, 0); /** Legacy check behavior. */ private final boolean pre1_9; public SoundDistance(Plugin plugin) { super(plugin, ListenerPriority.LOW, PacketType.Play.Server.NAMED_SOUND_EFFECT); this.checkType = CheckType.NET_SOUNDDISTANCE; pre1_9 = ServerVersion.compareMinecraftVersion("1.9") < 0; inflateEffectNames(); } /** * Ensure both lower and upper case are contained. */ private void inflateEffectNames() { final List names = new ArrayList(effectNames); for (String name : names) { effectNames.add(name.toLowerCase()); effectNames.add(name.toUpperCase()); } } private boolean isSoundMonitoredPre1_9(final PacketContainer packetContainer) { //debug(null, packetContainer.getStrings().read(0)); return effectNames.contains(packetContainer.getStrings().read(0)); } private boolean isSoundMonitoredLatest(final PacketContainer packetContainer) { StructureModifier sounds = packetContainer.getSoundEffects(); for (final Sound sound : sounds.getValues()) { if (effectNames.contains(sound.name())) { //debug(null, "MONITOR SOUND: " + sound); return true; } } return false; } private boolean isSoundMonitored(final PacketContainer packetContainer) { if (pre1_9) { return isSoundMonitoredPre1_9(packetContainer); } else { return isSoundMonitoredLatest(packetContainer); } } @Override public void onPacketSending(final PacketEvent event) { final PacketContainer packetContainer = event.getPacket(); // Compare sound effect name. if (!isSoundMonitored(packetContainer)) { return; } final Player player = event.getPlayer(); final IPlayerData pData = DataManager.getPlayerData(player); if (!pData.isCheckActive(CheckType.NET_SOUNDDISTANCE, player)) { return; } // Compare distance of player to the weather location. final Location loc = player.getLocation(useLoc); final StructureModifier ints = packetContainer.getIntegers(); final double dSq = TrigUtil.distanceSquared(ints.read(0) / 8, ints.read(2) / 8, loc.getX(), loc.getZ()); // if (data.debug) { // debug(player, "SoundDistance(" + soundName + "): " + StringUtil.fdec1.format(Math.sqrt(dSq))); // } final NetConfig cc = pData.getGenericInstance(NetConfig.class); if (dSq > cc.soundDistanceSq) { event.setCancelled(true); counters.add(idSoundEffectCancel, 1); } useLoc.setWorld(null); } }