252 lines
11 KiB
Java
252 lines
11 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.net.protocollib;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
|
import org.bukkit.plugin.Plugin;
|
|
|
|
import com.comphenix.protocol.PacketType;
|
|
import com.comphenix.protocol.PacketType.Protocol;
|
|
import com.comphenix.protocol.PacketType.Sender;
|
|
import com.comphenix.protocol.ProtocolLibrary;
|
|
import com.comphenix.protocol.ProtocolManager;
|
|
import com.comphenix.protocol.events.PacketAdapter;
|
|
|
|
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
|
import fr.neatmonster.nocheatplus.checks.net.NetData;
|
|
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
|
|
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
|
|
import fr.neatmonster.nocheatplus.components.registry.feature.IDisableListener;
|
|
import fr.neatmonster.nocheatplus.components.registry.feature.INotifyReload;
|
|
import fr.neatmonster.nocheatplus.components.registry.feature.JoinLeaveListener;
|
|
import fr.neatmonster.nocheatplus.config.ConfPaths;
|
|
import fr.neatmonster.nocheatplus.config.ConfigManager;
|
|
import fr.neatmonster.nocheatplus.logging.StaticLog;
|
|
import fr.neatmonster.nocheatplus.logging.Streams;
|
|
import fr.neatmonster.nocheatplus.players.DataManager;
|
|
import fr.neatmonster.nocheatplus.players.IPlayerData;
|
|
import fr.neatmonster.nocheatplus.stats.Counters;
|
|
import fr.neatmonster.nocheatplus.utilities.StringUtil;
|
|
import fr.neatmonster.nocheatplus.worlds.IWorldDataManager;
|
|
|
|
/**
|
|
* Quick and dirty ProtocolLib setup.
|
|
*
|
|
* @author asofold
|
|
*
|
|
*/
|
|
public class ProtocolLibComponent implements IDisableListener, INotifyReload, JoinLeaveListener, Listener {
|
|
|
|
// TODO: Static reference is problematic (needs a static and accessible Counters instance?).
|
|
public static final int idNullPlayer = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class).registerKey("packet.nullplayer");
|
|
/** Likely does not happen, TODO: Code review protocol plugin. */
|
|
public static final int idInconsistentIsAsync = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class).registerKey("packet.inconsistent.isasync");
|
|
|
|
/**
|
|
* Auxiliary method for suppressing exceptions.
|
|
*
|
|
* @param protocol
|
|
* @param sender
|
|
* @param name
|
|
* PacketType if available, null otherwise.
|
|
* @return
|
|
*/
|
|
public static PacketType findPacketTypeByName(Protocol protocol, Sender sender, String name) {
|
|
try {
|
|
return PacketType.findCurrent(protocol, sender, name);
|
|
}
|
|
catch (Throwable t) {
|
|
// uh
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// INSTANCE ----
|
|
|
|
private final List<PacketAdapter> registeredPacketAdapters = new LinkedList<PacketAdapter>();
|
|
|
|
public ProtocolLibComponent(Plugin plugin) {
|
|
register(plugin);
|
|
/*
|
|
* TODO: Register listeners iff any check is enabled - unregister from
|
|
* EventRegistry with unregister.
|
|
*/
|
|
}
|
|
|
|
private void register(Plugin plugin) {
|
|
StaticLog.logInfo("Adding packet level hooks for ProtocolLib (MC " + ProtocolLibrary.getProtocolManager().getMinecraftVersion().getVersion() + ")...");
|
|
final IWorldDataManager worldMan = NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager();
|
|
//Special purpose.
|
|
if (ConfigManager.isTrueForAnyConfig(ConfPaths.NET + ConfPaths.SUB_DEBUG) || ConfigManager.isTrueForAnyConfig(ConfPaths.CHECKS_DEBUG) ) {
|
|
// (Debug logging. Only activates if debug is set for checks or checks.net, not on the fly.)
|
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.DebugAdapter", plugin);
|
|
}
|
|
// Actual checks.
|
|
if (ServerVersion.compareMinecraftVersion("1.6.4") <= 0) {
|
|
// Don't use this listener.
|
|
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().info(Streams.STATUS, "Disable EntityUseAdapter due to incompatibilities. Use fight.speed instead of net.attackfrequency.");
|
|
}
|
|
else if (worldMan.isActiveAnywhere(CheckType.NET_ATTACKFREQUENCY)) {
|
|
// (Also sets lastKeepAliveTime, if enabled.)
|
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.UseEntityAdapter", plugin);
|
|
}
|
|
if (worldMan.isActiveAnywhere(CheckType.NET_FLYINGFREQUENCY)) {
|
|
// (Also sets lastKeepAliveTime, if enabled.)
|
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.MovingFlying", plugin);
|
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.OutgoingPosition", plugin);
|
|
}
|
|
if (worldMan.isActiveAnywhere(CheckType.NET_KEEPALIVEFREQUENCY)
|
|
|| worldMan.isActiveAnywhere(CheckType.FIGHT_GODMODE)) {
|
|
// (Set lastKeepAlive if this or fight.godmode is enabled.)
|
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.KeepAliveAdapter", plugin);
|
|
}
|
|
if (worldMan.isActiveAnywhere(CheckType.NET_SOUNDDISTANCE)) {
|
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.SoundDistance", plugin);
|
|
}
|
|
if (ServerVersion.compareMinecraftVersion("1.9") < 0) {
|
|
if (worldMan.isActiveAnywhere(CheckType.NET_PACKETFREQUENCY)) {
|
|
register("fr.neatmonster.nocheatplus.checks.net.protocollib.CatchAllAdapter", plugin);
|
|
}
|
|
}
|
|
|
|
if (!registeredPacketAdapters.isEmpty()) {
|
|
List<String> names = new ArrayList<String>(registeredPacketAdapters.size());
|
|
for (PacketAdapter adapter : registeredPacketAdapters) {
|
|
names.add(adapter.getClass().getSimpleName());
|
|
}
|
|
StaticLog.logInfo("Available (and activated) packet level hooks: " + StringUtil.join(names, " | "));
|
|
NCPAPIProvider.getNoCheatPlusAPI().addFeatureTags("packet-listeners", names);
|
|
} else {
|
|
StaticLog.logInfo("No packet level hooks activated.");
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void register(String name, Plugin plugin) {
|
|
Throwable t = null;
|
|
try {
|
|
Class<?> clazz = Class.forName(name);
|
|
register((Class<? extends PacketAdapter>) clazz, plugin);
|
|
return;
|
|
} catch (ClassNotFoundException e) {
|
|
t = e;
|
|
} catch (ClassCastException e) {
|
|
t = e;
|
|
}
|
|
StaticLog.logWarning("Could not register packet level hook: " + name);
|
|
StaticLog.logWarning(t);
|
|
}
|
|
|
|
private void register(Class<? extends PacketAdapter> clazz, Plugin plugin) {
|
|
try {
|
|
// Construct a new instance using reflection.
|
|
PacketAdapter adapter = clazz.getDeclaredConstructor(Plugin.class).newInstance(plugin);
|
|
ProtocolLibrary.getProtocolManager().addPacketListener(adapter);
|
|
registeredPacketAdapters.add(adapter);
|
|
} catch (Throwable t) {
|
|
StaticLog.logWarning("Could not register packet level hook: " + clazz.getSimpleName());
|
|
StaticLog.logWarning(t);
|
|
if (t.getCause() != null) {
|
|
StaticLog.logWarning(t.getCause());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisable() {
|
|
unregister();
|
|
}
|
|
|
|
@Override
|
|
public void onReload() {
|
|
unregister();
|
|
NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager().removeGenericInstance(NetData.class); // Currently needed for FlyingFrequency.
|
|
register(Bukkit.getPluginManager().getPlugin("NoCheatPlus")); // TODO: static plugin getter?
|
|
}
|
|
|
|
private void unregister() {
|
|
final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
|
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
|
|
for (PacketAdapter adapter : registeredPacketAdapters) {
|
|
try {
|
|
protocolManager.removePacketListener(adapter);
|
|
api.removeComponent(adapter); // Bit heavy, but consistent.
|
|
} catch (Throwable t) {
|
|
StaticLog.logWarning("Failed to unregister packet level hook: " + adapter.getClass().getName());
|
|
}// TODO Auto-generated method stub
|
|
|
|
}
|
|
registeredPacketAdapters.clear();
|
|
}
|
|
|
|
@Override
|
|
public void playerJoins(final Player player) {
|
|
if (!registeredPacketAdapters.isEmpty()) {
|
|
DataManager.getGenericInstance(player, NetData.class).onJoin(player);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void playerLeaves(final Player player) {
|
|
if (!registeredPacketAdapters.isEmpty()) {
|
|
DataManager.getGenericInstance(player, NetData.class).onLeave(player);
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false)
|
|
public void onPlayerRespawn(final PlayerRespawnEvent event) {
|
|
if (!registeredPacketAdapters.isEmpty()) {
|
|
final Player player = event.getPlayer();
|
|
final NetData data = DataManager.getGenericInstance(player, NetData.class);
|
|
data.onJoin(player);
|
|
final Location loc = event.getRespawnLocation();
|
|
data.teleportQueue.onTeleportEvent(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
|
|
}
|
|
}
|
|
|
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
|
public void onPlayerTeleport(final PlayerTeleportEvent event) {
|
|
if (!registeredPacketAdapters.isEmpty()) {
|
|
// TODO: Might move to MovingListener.
|
|
// TODO: Might still add cancelled UNKNOWN events. TEST IT
|
|
final Location to = event.getTo();
|
|
if (to == null) {
|
|
return;
|
|
}
|
|
final Player player = event.getPlayer();
|
|
final IPlayerData pData = DataManager.getPlayerData(player);
|
|
final NetData data = pData.getGenericInstance(NetData.class);
|
|
if (pData.isCheckActive(CheckType.NET_FLYINGFREQUENCY, player)) {
|
|
// Register expected location for comparison with outgoing packets.
|
|
data.teleportQueue.onTeleportEvent(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch());
|
|
}
|
|
data.clearFlyingQueue();
|
|
}
|
|
}
|
|
|
|
}
|