added files.
This commit is contained in:
commit
277c85eacd
150 files changed
+16138
No files matched your search
@@ -0,0 +1,263 @@
|
||||
package me.konsolas.aac;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.konsolas.aac.analysis.AnalysisManager;
|
||||
import me.konsolas.aac.api.AACAPI;
|
||||
import me.konsolas.aac.api.internal.AACApiImplementation;
|
||||
import me.konsolas.aac.command.AACCommandExecutor;
|
||||
import me.konsolas.aac.database.PlayerDatabase;
|
||||
import me.konsolas.aac.debug.DebugManager;
|
||||
import me.konsolas.aac.metrics.MetricsLite;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
import me.konsolas.aac.protocol.PacketDispatcher;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.timings.CheckTask;
|
||||
import me.konsolas.aac.timings.EntityTracker;
|
||||
import me.konsolas.aac.timings.TPSCounter;
|
||||
import org.bukkit.Bukkit;
|
||||
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.AsyncPlayerPreLoginEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class AAC extends JavaPlugin implements Listener {
|
||||
|
||||
private TPSCounter tpsCounter;
|
||||
|
||||
private final Map<UUID, PlayerLoginInfo> loginInfo = new ConcurrentHashMap();
|
||||
|
||||
private boolean databaseEnabled;
|
||||
|
||||
private PacketDispatcher packetDispatcher;
|
||||
|
||||
private BiFunction<Player, String, String> placeholderResolver;
|
||||
|
||||
private AnalysisManager analysisManager;
|
||||
|
||||
private AACAPI api;
|
||||
|
||||
private EntityTracker entityTracker;
|
||||
|
||||
private CheckTask checkTask;
|
||||
|
||||
private DebugManager debugManager;
|
||||
|
||||
private final Map<UUID, ProtocolVersion> protocolVersions = new ConcurrentHashMap();
|
||||
|
||||
private AACCommandExecutor commandExecutor;
|
||||
|
||||
private PlayerDatabase database;
|
||||
|
||||
public void loadLoginInfo(UUID uUID, InetAddress inetAddress) {
|
||||
try {
|
||||
this.loginInfo.put(uUID, this.database.findPlayerLoginInfo(uUID, inetAddress));
|
||||
} catch (SQLException sQLException) {
|
||||
this.getLogger().log(Level.SEVERE, "failed to retrieve login info for " + uUID, (Throwable) sQLException);
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerDatabase getDatabase() {
|
||||
return this.database;
|
||||
}
|
||||
|
||||
public String resolvePlaceholders(Player player, String text) {
|
||||
return (String) this.placeholderResolver.apply(player, text);
|
||||
}
|
||||
|
||||
public PacketDispatcher getPacketDispatcher() {
|
||||
return this.packetDispatcher;
|
||||
}
|
||||
|
||||
public DebugManager getDebugManager() {
|
||||
return this.debugManager;
|
||||
}
|
||||
|
||||
private void updatePlayer(Player player, boolean matches) {
|
||||
if (matches) {
|
||||
this.packetDispatcher.addExemption(player, PacketDispatcher.COMMAND_EXEMPTION);
|
||||
} else {
|
||||
this.packetDispatcher.removeExemption(player, PacketDispatcher.COMMAND_EXEMPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerLoginInfo findPlayerLoginInfo(UUID uUID) {
|
||||
return (PlayerLoginInfo) this.loginInfo.get(uUID);
|
||||
}
|
||||
|
||||
public TPSCounter getTpsCounter() {
|
||||
return this.tpsCounter;
|
||||
}
|
||||
|
||||
public EntityTracker getEntityTracker() {
|
||||
return this.entityTracker;
|
||||
}
|
||||
|
||||
public AACCommandExecutor getCommandExecutor() {
|
||||
return this.commandExecutor;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuitEvent(PlayerQuitEvent playerQuitEvent) {
|
||||
this.packetDispatcher.removePlayer(playerQuitEvent.getPlayer());
|
||||
AnalysisManager analysisManager2 = this.analysisManager;
|
||||
Player player = playerQuitEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
analysisManager2.updatePlayer(player2);
|
||||
this.loginInfo.remove(playerQuitEvent.getPlayer().getUniqueId());
|
||||
this.protocolVersions.remove(playerQuitEvent.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
public boolean isDatabaseEnabled() {
|
||||
return this.databaseEnabled;
|
||||
}
|
||||
|
||||
public AnalysisManager getAnalysisManager() {
|
||||
return this.analysisManager;
|
||||
}
|
||||
|
||||
public boolean toggleExemption(Player player) {
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
this.loginInfo.put(uniqueId, ((PlayerLoginInfo) this.loginInfo.get(uniqueId)).copy());
|
||||
boolean exempt = ((PlayerLoginInfo) this.loginInfo.get(uniqueId)).isExempt();
|
||||
try {
|
||||
this.database.setExempt(uniqueId, exempt);
|
||||
} catch (SQLException sQLException) {
|
||||
this.getLogger().log(Level.WARNING, "could not save exemption", (Throwable) sQLException);
|
||||
}
|
||||
this.updatePlayer(player, exempt);
|
||||
return exempt;
|
||||
}
|
||||
|
||||
public ProtocolVersion getProtocolVersion(Player player) {
|
||||
return this.protocolVersions.computeIfAbsent(player.getUniqueId(), result -> {
|
||||
ProtocolVersion version = ProtocolVersion.detectPlayerVersion(player);
|
||||
if (version != null) {
|
||||
this.getLogger().info("Detected " + version + " for " + player.getName());
|
||||
} else {
|
||||
this.getLogger().warning("Unresolved version for " + player.getName() + " check your ViaVersion/ProtocolSupport build");
|
||||
}
|
||||
return version;
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPlayerJoinEvent(PlayerJoinEvent playerJoinEvent) {
|
||||
PacketDispatcher packetDispatcher2 = this.packetDispatcher;
|
||||
Player player = playerJoinEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher2.initializePlayer(player2);
|
||||
this.analysisManager.evaluateUpdatePlayer(playerJoinEvent.getPlayer());
|
||||
UUID uniqueId = playerJoinEvent.getPlayer().getUniqueId();
|
||||
if (this.findPlayerLoginInfo(uniqueId) == null) {
|
||||
this.getServer().getScheduler().runTaskAsynchronously(this, () -> {
|
||||
InetAddress address = Objects.requireNonNull(playerJoinEvent.getPlayer().getAddress()).getAddress();
|
||||
UUID uniqueId2 = playerJoinEvent.getPlayer().getUniqueId();
|
||||
this.loadLoginInfo(uniqueId2, address);
|
||||
Player player3 = playerJoinEvent.getPlayer();
|
||||
UUID uniqueId3 = playerJoinEvent.getPlayer().getUniqueId();
|
||||
boolean exempt = this.findPlayerLoginInfo(uniqueId3).isExempt();
|
||||
Player player4 = player3;
|
||||
this.updatePlayer(player4, exempt);
|
||||
});
|
||||
} else {
|
||||
Player player5 = playerJoinEvent.getPlayer();
|
||||
UUID uniqueId4 = playerJoinEvent.getPlayer().getUniqueId();
|
||||
boolean exempt2 = this.findPlayerLoginInfo(uniqueId4).isExempt();
|
||||
Player player6 = player5;
|
||||
this.updatePlayer(player6, exempt2);
|
||||
}
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
this.saveDefaultConfig();
|
||||
this.reloadConfig();
|
||||
try {
|
||||
if (this.getConfig().getBoolean("mysql.enabled")) {
|
||||
String string = this.getConfig().getString("mysql.host");
|
||||
int intValue = this.getConfig().getInt("mysql.port");
|
||||
String string2 = this.getConfig().getString("mysql.database");
|
||||
String string3 = this.getConfig().getString("mysql.username");
|
||||
String string4 = this.getConfig().getString("mysql.password");
|
||||
this.database = PlayerDatabase.create(this, string, intValue, string2, string3, string4);
|
||||
} else {
|
||||
String string5 = this.getConfig().getString("sqlite_target");
|
||||
this.database = PlayerDatabase.create(this, string5);
|
||||
}
|
||||
} catch (SQLException sQLException) {
|
||||
throw new RuntimeException(sQLException);
|
||||
}
|
||||
this.tpsCounter = new TPSCounter(this);
|
||||
this.entityTracker = new EntityTracker(this);
|
||||
this.checkTask = new CheckTask(this);
|
||||
this.packetDispatcher = new PacketDispatcher(this);
|
||||
this.analysisManager = new AnalysisManager(this);
|
||||
this.commandExecutor = new AACCommandExecutor(this);
|
||||
Objects.requireNonNull(this.getCommand("aac")).setExecutor(this.commandExecutor);
|
||||
Objects.requireNonNull(this.getCommand("aac")).setTabCompleter(this.commandExecutor);
|
||||
this.getServer().getPluginManager().registerEvents(this, this);
|
||||
if (!this.getServer().getOnlinePlayers().isEmpty()) {
|
||||
Logger logger = this.getLogger();
|
||||
Logger logger2 = logger;
|
||||
logger2.warning((String) "Attempting to handle server reload");
|
||||
for (Player player : this.getServer().getOnlinePlayers()) {
|
||||
this.getServer().getScheduler().runTaskAsynchronously(this, () -> {
|
||||
InetAddress address = Objects.requireNonNull(player.getAddress()).getAddress();
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
this.loadLoginInfo(uniqueId, address);
|
||||
});
|
||||
this.packetDispatcher.initializePlayer(player);
|
||||
this.analysisManager.evaluateUpdatePlayer(player);
|
||||
}
|
||||
logger = this.getLogger();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
StringBuilder stringBuilder2 = stringBuilder;
|
||||
stringBuilder = stringBuilder2.append((String) "Initialised ").append(this.getServer().getOnlinePlayers().size());
|
||||
StringBuilder stringBuilder3 = stringBuilder;
|
||||
StringBuilder stringBuilder4 = stringBuilder3.append((String) " players");
|
||||
String text = stringBuilder4.toString();
|
||||
Logger logger3 = logger;
|
||||
logger3.warning(text);
|
||||
}
|
||||
this.databaseEnabled = this.getConfig().getBoolean("bungeecord");
|
||||
if (this.databaseEnabled) {
|
||||
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
|
||||
this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", this.analysisManager);
|
||||
Logger logger4 = this.getLogger();
|
||||
Logger logger5 = logger4;
|
||||
logger5.info((String) "Registered BungeeCord plugin channels");
|
||||
}
|
||||
this.api = new AACApiImplementation(this);
|
||||
this.getServer().getServicesManager().register(AACAPI.class, this.api, this, ServicePriority.Normal);
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
Logger logger6 = this.getLogger();
|
||||
Logger logger7 = logger6;
|
||||
logger7.info((String) "Hooking into PlaceholderAPI");
|
||||
this.placeholderResolver = (result, result2) -> PlaceholderAPI.setPlaceholders(result, result2.replace("%player_name%", result.getName()));
|
||||
} else {
|
||||
this.placeholderResolver = (result, result2) -> result2.replace("%player_name%", result.getName());
|
||||
}
|
||||
this.debugManager = new DebugManager(this);
|
||||
new MetricsLite(this, 2267);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onAsyncPlayerPreLoginEvent(AsyncPlayerPreLoginEvent asyncPlayerPreLoginEvent) {
|
||||
InetAddress address = asyncPlayerPreLoginEvent.getAddress();
|
||||
UUID uniqueId = asyncPlayerPreLoginEvent.getUniqueId();
|
||||
this.loadLoginInfo(uniqueId, address);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package me.konsolas.aac.analysis;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
public class AnalysisActionRule {
|
||||
|
||||
private final long delayTicks;
|
||||
|
||||
private final List<Pattern> includePatterns = new ArrayList();
|
||||
|
||||
private final List<Pattern> excludePatterns = new ArrayList();
|
||||
|
||||
private final Int2ObjectMap thresholdCommands = new Int2ObjectArrayMap();
|
||||
|
||||
public AnalysisActionRule(ConfigurationSection section) {
|
||||
this.delayTicks = (long) (20.0 * section.getDouble("delay", 0.0));
|
||||
for (String text : section.getStringList("features")) {
|
||||
if (text.startsWith("-")) {
|
||||
this.excludePatterns.add(Pattern.compile(("\\Q" + text.substring(1) + "\\E").replace("*", "\\E.*\\Q")));
|
||||
} else {
|
||||
this.includePatterns.add(Pattern.compile(("\\Q" + text + "\\E").replace("*", "\\E.*\\Q")));
|
||||
}
|
||||
}
|
||||
ConfigurationSection configurationSection = section.getConfigurationSection("thresholds");
|
||||
if (configurationSection != null) {
|
||||
for (String text2 : configurationSection.getKeys(false)) {
|
||||
if (configurationSection.get(text2) instanceof String) {
|
||||
this.thresholdCommands.put(Integer.parseInt(text2), Collections.singletonList(configurationSection.getString(text2)));
|
||||
} else {
|
||||
this.thresholdCommands.put(Integer.parseInt(text2), configurationSection.getStringList(text2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getDelayTicks() {
|
||||
return this.delayTicks;
|
||||
}
|
||||
|
||||
private boolean matchesFeature(String text) {
|
||||
for (Pattern pattern : this.excludePatterns) {
|
||||
if (pattern.matcher(text).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (Pattern pattern2 : this.includePatterns) {
|
||||
if (pattern2.matcher(text).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List matchingCommands(PlayerAnalysisReport playerAnalysisReport) {
|
||||
double value = 0.0;
|
||||
for (FeatureResult result : playerAnalysisReport.getFeatures()) {
|
||||
Object result2 = result.getDetails().get("id");
|
||||
String text = (String) result2;
|
||||
if (this.matchesFeature(text)) {
|
||||
value += result.getScore();
|
||||
}
|
||||
}
|
||||
Entry entry = null;
|
||||
ObjectIterator objectIterator = this.thresholdCommands.int2ObjectEntrySet().iterator();
|
||||
while (objectIterator.hasNext()) {
|
||||
Entry entry2 = (Entry) objectIterator.next();
|
||||
if (entry2.getIntKey() < value && (entry == null || entry2.getIntKey() > entry.getIntKey())) {
|
||||
entry = entry2;
|
||||
}
|
||||
}
|
||||
return entry == null ? null : (List) entry.getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package me.konsolas.aac.analysis;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.api.AACCustomFeature;
|
||||
import me.konsolas.aac.api.AACCustomFeatureProvider;
|
||||
import me.konsolas.aac.database.PlayerDatabase;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
|
||||
public class AnalysisManager implements PluginMessageListener {
|
||||
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor(result -> new Thread(result, "AAC Report Generator Thread"));
|
||||
|
||||
private CompletableFuture<PlayerAnalysisReport> pendingAnalysis;
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
private final long historyMillis;
|
||||
|
||||
private final int yellowScore;
|
||||
|
||||
private final int redScore;
|
||||
|
||||
private final Map<String, AnalysisActionRule> actionRules = new HashMap();
|
||||
|
||||
private final FeatureRegistry featureRegistry;
|
||||
|
||||
private final List<AACCustomFeatureProvider> customFeatureProviders = new ArrayList();
|
||||
|
||||
private final Set<Player> alertRecipients = Collections.synchronizedSet(new LinkedHashSet());
|
||||
|
||||
private final Map<UUID, PlayerAnalysisReport> onlineReports = new ConcurrentHashMap();
|
||||
|
||||
private final Cache reportCache;
|
||||
|
||||
public AnalysisManager(AAC plugin) {
|
||||
this.plugin = plugin;
|
||||
this.featureRegistry = new FeatureRegistry(plugin);
|
||||
double doubleValue = plugin.getConfig().getDouble("analysis.period");
|
||||
long longValue = plugin.getConfig().getLong("analysis.history");
|
||||
String string = plugin.getConfig().getString("analysis.history_time_unit");
|
||||
long value;
|
||||
try {
|
||||
value = TimeUnit.MILLISECONDS.convert(longValue, TimeUnit.valueOf(string));
|
||||
} catch (IllegalArgumentException illegalArgumentException) {
|
||||
plugin.getLogger().severe("Failed to parse history_time_unit: " + illegalArgumentException.getMessage());
|
||||
plugin.getLogger().severe("Using 1 DAY instead");
|
||||
value = TimeUnit.MILLISECONDS.convert(1L, TimeUnit.DAYS);
|
||||
}
|
||||
this.historyMillis = value;
|
||||
this.yellowScore = plugin.getConfig().getInt("analysis.yellow_score");
|
||||
this.redScore = plugin.getConfig().getInt("analysis.red_score");
|
||||
if (plugin.getConfig().getBoolean("analysis.clear_stale_entries")) {
|
||||
try {
|
||||
PlayerDatabase database = plugin.getDatabase();
|
||||
long value2 = System.currentTimeMillis() - this.historyMillis;
|
||||
int value3 = database.deleteEventsBefore(value2);
|
||||
if (value3 == 0) {
|
||||
plugin.getLogger().info("No stale entries to remove");
|
||||
} else if (value3 == 1) {
|
||||
plugin.getLogger().info("Removed 1 stale entry from the database");
|
||||
} else {
|
||||
plugin.getLogger().info("Removed " + value3 + " stale entries from the database");
|
||||
}
|
||||
} catch (SQLException sQLException) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to clear stale entries from the database", (Throwable) sQLException);
|
||||
}
|
||||
}
|
||||
ConfigurationSection configurationSection = plugin.getConfig().getConfigurationSection("actions");
|
||||
if (configurationSection != null) {
|
||||
for (String text : configurationSection.getKeys(false)) {
|
||||
ConfigurationSection configurationSection2 = configurationSection.getConfigurationSection(text);
|
||||
if (configurationSection2 != null) {
|
||||
this.actionRules.put(text, new AnalysisActionRule(configurationSection2));
|
||||
} else {
|
||||
plugin.getLogger().warning("Unexpected key " + text + " in config.yml:actions");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
plugin.getLogger().warning("config.yml missing actions section. Consider regenerating your config.");
|
||||
}
|
||||
this.reportCache = CacheBuilder.newBuilder().maximumSize(100L).expireAfterAccess(10L, TimeUnit.MINUTES).build();
|
||||
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this::tick, 100L, (long) (doubleValue * 20.0));
|
||||
}
|
||||
|
||||
public void updatePlayerWithAlertRecipients(Player player) {
|
||||
this.alertRecipients.add(player);
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
this.alertRecipients.removeIf(result -> !result.isOnline());
|
||||
if (!this.alertRecipients.isEmpty() && (this.pendingAnalysis == null || this.pendingAnalysis.isDone())) {
|
||||
Player player;
|
||||
synchronized (this.alertRecipients) {
|
||||
player = (Player) this.alertRecipients.iterator().next();
|
||||
this.alertRecipients.remove(player);
|
||||
}
|
||||
PlayerAnalysisReport report = this.getReport(player);
|
||||
RiskLevel riskLevel = report == null ? RiskLevel.NEUTRAL : report.getRiskLevel();
|
||||
this.pendingAnalysis = this.analyzeAsync(player).whenComplete((result2, result3) -> {
|
||||
if (result2 != null && result2.getRiskLevel() != riskLevel && player.isOnline()) {
|
||||
if (this.plugin.isDatabaseEnabled()) {
|
||||
this.updatePlayerWithPlugin(player);
|
||||
}
|
||||
this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, () -> this.plugin.getCommandExecutor().updatePlayer(player));
|
||||
}
|
||||
}).whenComplete((result4, result2) -> {
|
||||
if (result4 != null) {
|
||||
for (Entry entry : this.actionRules.entrySet()) {
|
||||
List<String> values = ((AnalysisActionRule) entry.getValue()).matchingCommands(result4);
|
||||
if (values != null) {
|
||||
this.plugin.getServer().getScheduler().runTaskLater(this.plugin, () -> {
|
||||
if (player.isOnline()) {
|
||||
for (String text : values) {
|
||||
text = this.plugin.resolvePlaceholders(player, text);
|
||||
this.plugin.getLogger().info("actions." + (String) entry.getKey() + ": " + text);
|
||||
this.plugin.getServer().dispatchCommand(Bukkit.getConsoleSender(), text);
|
||||
}
|
||||
}
|
||||
}, ((AnalysisActionRule) entry.getValue()).getDelayTicks());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerAnalysisReport getReport(OfflinePlayer offlinePlayer) {
|
||||
return offlinePlayer.isOnline() ? (PlayerAnalysisReport) this.onlineReports.get(offlinePlayer.getUniqueId()) : (PlayerAnalysisReport) this.reportCache.getIfPresent(offlinePlayer.getUniqueId());
|
||||
}
|
||||
|
||||
public void registerFeatureProvider(AACCustomFeatureProvider aACCustomFeatureProvider) {
|
||||
this.customFeatureProviders.add(aACCustomFeatureProvider);
|
||||
}
|
||||
|
||||
public CompletableFuture<PlayerAnalysisReport> analyzeAsync(OfflinePlayer offlinePlayer) {
|
||||
CompletableFuture<PlayerAnalysisReport> completableFuture = new CompletableFuture();
|
||||
this.executor.execute(() -> {
|
||||
try {
|
||||
AAC aacPlugin = this.plugin;
|
||||
UUID uniqueId = offlinePlayer.getUniqueId();
|
||||
PlayerLoginInfo playerLoginInfo = aacPlugin.findPlayerLoginInfo(uniqueId);
|
||||
if (playerLoginInfo == null) {
|
||||
PlayerDatabase database = this.plugin.getDatabase();
|
||||
UUID uniqueId2 = offlinePlayer.getUniqueId();
|
||||
UUID uUID = uniqueId2;
|
||||
playerLoginInfo = database.findPlayerLoginInfo(uUID);
|
||||
}
|
||||
completableFuture.complete(this.analyze(offlinePlayer, playerLoginInfo));
|
||||
} catch (SQLException sQLException) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Failed to analyse player " + offlinePlayer.getName(), (Throwable) sQLException);
|
||||
completableFuture.completeExceptionally(sQLException);
|
||||
}
|
||||
});
|
||||
return completableFuture.whenComplete((result, result2) -> {
|
||||
if (result != null) {
|
||||
if (offlinePlayer.isOnline()) {
|
||||
this.onlineReports.put(offlinePlayer.getUniqueId(), result);
|
||||
} else {
|
||||
this.reportCache.put(offlinePlayer.getUniqueId(), result);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PlayerAnalysisReport analyze(OfflinePlayer offlinePlayer, PlayerLoginInfo playerLoginInfo) throws SQLException {
|
||||
Instant instant = Instant.now();
|
||||
PlayerDatabase database = this.plugin.getDatabase();
|
||||
long value = System.currentTimeMillis() - this.historyMillis;
|
||||
UUID uniqueId = offlinePlayer.getUniqueId();
|
||||
UUID uUID = uniqueId;
|
||||
long value2 = value;
|
||||
List values = database.getEventsSince(value2, uUID);
|
||||
List<FeatureResult> values2;
|
||||
if (values.isEmpty()) {
|
||||
values2 = new ArrayList();
|
||||
} else {
|
||||
values2 = this.featureRegistry.analyze(values, playerLoginInfo);
|
||||
}
|
||||
for (AACCustomFeatureProvider aACCustomFeatureProvider : this.customFeatureProviders) {
|
||||
Collection featuresAsync = aACCustomFeatureProvider.getFeaturesAsync(offlinePlayer);
|
||||
if (featuresAsync != null) {
|
||||
((Collection<me.konsolas.aac.api.AACCustomFeature>) aACCustomFeatureProvider.getFeaturesAsync(offlinePlayer)).stream().map(result -> {
|
||||
String text = "(API) " + result.getInfo();
|
||||
double score = result.getScore();
|
||||
Map tooltip = result.getTooltip();
|
||||
String text2 = text;
|
||||
return FeatureResult.create(text2, tooltip, score);
|
||||
}).filter(Objects::nonNull).forEach(values2::add);
|
||||
}
|
||||
}
|
||||
double value3 = values2.stream().mapToDouble(FeatureResult::getScore).sum();
|
||||
RiskLevel riskLevel = value3 > this.redScore ? RiskLevel.HIGH : (value3 > this.yellowScore ? RiskLevel.SUSPICIOUS : RiskLevel.NEUTRAL);
|
||||
return new PlayerAnalysisReport(offlinePlayer, riskLevel, instant, value3, (List) values2);
|
||||
}
|
||||
|
||||
public void evaluateUpdatePlayer(Player player) {
|
||||
this.analyzeAsync(player);
|
||||
}
|
||||
|
||||
public void updatePlayer(Player player) {
|
||||
if (this.onlineReports.containsKey(player.getUniqueId())) {
|
||||
this.reportCache.put(player.getUniqueId(), (PlayerAnalysisReport) this.onlineReports.remove(player.getUniqueId()));
|
||||
}
|
||||
this.alertRecipients.remove(player);
|
||||
}
|
||||
|
||||
private void updatePlayerWithPlugin(Player player) {
|
||||
ByteArrayDataOutput byteArrayDataOutput = ByteStreams.newDataOutput();
|
||||
byteArrayDataOutput.writeUTF("Forward");
|
||||
byteArrayDataOutput.writeUTF("ONLINE");
|
||||
byteArrayDataOutput.writeUTF("AACAlerts");
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
this.plugin.getLogger().info("Sending UUID " + uniqueId);
|
||||
byteArrayDataOutput.writeShort(16);
|
||||
byteArrayDataOutput.writeLong(uniqueId.getMostSignificantBits());
|
||||
byteArrayDataOutput.writeLong(uniqueId.getLeastSignificantBits());
|
||||
player.sendPluginMessage(this.plugin, "BungeeCord", byteArrayDataOutput.toByteArray());
|
||||
}
|
||||
|
||||
public void onPluginMessageReceived(String text, Player player, byte[] values) {
|
||||
if (text.equals("BungeeCord")) {
|
||||
ByteArrayDataInput byteArrayDataInput = ByteStreams.newDataInput(values);
|
||||
String text2 = byteArrayDataInput.readUTF();
|
||||
if (text2.equals("AACAlerts")) {
|
||||
short value = byteArrayDataInput.readShort();
|
||||
if (value != 16) {
|
||||
this.plugin.getLogger().warning("Invalid bungee message: len = " + value);
|
||||
return;
|
||||
}
|
||||
long value2 = byteArrayDataInput.readLong();
|
||||
long value3 = byteArrayDataInput.readLong();
|
||||
UUID uUID = new UUID(value2, value3);
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uUID);
|
||||
if (offlinePlayer.getName() == null || offlinePlayer.getName().equals("null")) {
|
||||
this.plugin.getLogger().warning("Invalid bungee message: uuid = " + uUID);
|
||||
return;
|
||||
}
|
||||
CompletableFuture<PlayerAnalysisReport> completableFuture;
|
||||
if (this.plugin.getAnalysisManager().getReport(offlinePlayer) == null) {
|
||||
completableFuture = this.plugin.getAnalysisManager().analyzeAsync(offlinePlayer);
|
||||
} else {
|
||||
completableFuture = CompletableFuture.completedFuture(this.plugin.getAnalysisManager().getReport(offlinePlayer));
|
||||
}
|
||||
completableFuture.whenComplete((result, result2) -> this.plugin.getCommandExecutor().updatePlayer(offlinePlayer));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package me.konsolas.aac.analysis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.analyzer.AccountFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.AverageFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.CountFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.FeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.FrequencyFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.PercentileFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.RateFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.TTestFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.analyzer.ThresholdFeatureAnalyzer;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
public class FeatureRegistry {
|
||||
|
||||
private static final Collection<FeatureAnalyzer> analyzers;
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
private final Map<FeatureAnalyzer, FeatureWeight> featureWeights = new HashMap();
|
||||
|
||||
public FeatureRegistry(AAC plugin) {
|
||||
this.plugin = plugin;
|
||||
ConfigurationSection section = Objects.requireNonNull(plugin.getConfig().getConfigurationSection("features"));
|
||||
for (FeatureAnalyzer featureAnalyzer : analyzers) {
|
||||
double value;
|
||||
if ((value = section.getDouble(featureAnalyzer.getConfigurationPath() + ".weight")) > 0.0) {
|
||||
this.featureWeights.put(featureAnalyzer, new FeatureWeight(section.getString(featureAnalyzer.getConfigurationPath() + ".format"), value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<FeatureResult> analyze(List<FeatureEvent> values, PlayerLoginInfo playerLoginInfo) {
|
||||
FeatureAccumulator featureAccumulator = new FeatureAccumulator();
|
||||
for (FeatureEvent event : values) {
|
||||
FeatureType type = event.getType();
|
||||
float value = event.getValue();
|
||||
FeatureType feature = type;
|
||||
featureAccumulator.record(feature, value);
|
||||
}
|
||||
ArrayList<FeatureResult> values2 = new ArrayList();
|
||||
for (Entry<FeatureAnalyzer, FeatureWeight> entry : this.featureWeights.entrySet()) {
|
||||
FeatureAnalyzer featureAnalyzer = (FeatureAnalyzer) entry.getKey();
|
||||
String text = FeatureWeight.findString((FeatureWeight) entry.getValue());
|
||||
featureAnalyzer.analyze(values, featureAccumulator, playerLoginInfo, text).map(result -> {
|
||||
double value2 = FeatureWeight.calculateValue((FeatureWeight) entry.getValue());
|
||||
double value3 = value2;
|
||||
return result.scaledBy(value3);
|
||||
}).ifPresent(values2::add);
|
||||
}
|
||||
values2.sort(Comparator.comparingDouble(FeatureResult::getScore).reversed());
|
||||
return values2;
|
||||
}
|
||||
|
||||
static {
|
||||
ArrayList values = new ArrayList();
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_break", FeatureType.AAC_DELAYS_BREAK, 0.1, true));
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_place", FeatureType.AAC_DELAYS_PLACE, 0.1, true));
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_consume", FeatureType.AAC_DELAYS_CONSUME, 0.1, true));
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_bow", FeatureType.AAC_DELAYS_BOW, 0.1, true));
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_regen", FeatureType.AAC_DELAYS_REGEN, 0.1, true));
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_sneak", FeatureType.AAC_DELAYS_UNSNEAK, 0.1, true));
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_release", FeatureType.AAC_DELAYS_RELEASE, 0.1, true));
|
||||
values.add(new TTestFeatureAnalyzer("aac_delays_breakdelay", FeatureType.AAC_DELAYS_BREAKDELAY, 0.1, true));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_move_nofall", FeatureType.AAC_MOVE_NOFALL, FeatureType.AAC_NORM_MOVE, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_move_vel", FeatureType.AAC_MOVE_VEL, FeatureType.AAC_NORM_MOVE, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_move_vehicle", FeatureType.AAC_MOVE_VEHICLE, FeatureType.AAC_NORM_MOVE, result -> result));
|
||||
values.add(new RateFeatureAnalyzer("aac_move_elytra", FeatureType.AAC_MOVE_ELYTRA, FeatureType.AAC_NORM_MOVE, result -> result));
|
||||
values.add(new RateFeatureAnalyzer("aac_move_water", FeatureType.AAC_MOVE_WATER, FeatureType.AAC_NORM_MOVE, result -> result));
|
||||
values.add(new RateFeatureAnalyzer("aac_move_lava", FeatureType.AAC_MOVE_LAVA, FeatureType.AAC_NORM_MOVE, result -> result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_move_noslow", FeatureType.AAC_MOVE_NOSLOW, FeatureType.AAC_NORM_MOVE, result -> result));
|
||||
values.add(new RateFeatureAnalyzer("aac_move_generic", FeatureType.AAC_MOVE_GENERIC, FeatureType.AAC_NORM_MOVE, result -> result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_move_timer", FeatureType.AAC_MOVE_TIMER, FeatureType.AAC_NORM_MOVE, result -> 5.0 * result));
|
||||
values.add(new CountFeatureAnalyzer("aac_move_inf", FeatureType.AAC_MOVE_INF, result -> 100.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimbot_spread", FeatureType.AAC_AIMBOT_PITCH_SPREAD, FeatureType.AAC_NORM_ATTACK, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimbot_sync", FeatureType.AAC_AIMBOT_SENS_MISMATCH, FeatureType.AAC_NORM_ATTACK, result -> 20.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimbot_horz", FeatureType.AAC_AIMBOT_SENS_X_OOR, FeatureType.AAC_NORM_ATTACK, result -> 20.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimbot_vert", FeatureType.AAC_AIMBOT_SENS_Y_OOR, FeatureType.AAC_NORM_ATTACK, result -> 20.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimbot_snap", FeatureType.AAC_AIMBOT_HEAD_SNAP, FeatureType.AAC_NORM_ATTACK, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimbot_zero", FeatureType.AAC_AIMBOT_ZERO_POINT, FeatureType.AAC_NORM_ATTACK, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimassist_horz", FeatureType.AAC_AIMBOT_BAD_DELTA_X, FeatureType.AAC_NORM_ATTACK, result -> 10.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_aimassist_vert", FeatureType.AAC_AIMBOT_BAD_DELTA_Y, FeatureType.AAC_NORM_ATTACK, result -> 10.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_clk_noswing", FeatureType.AAC_CLICK_NOSWING, FeatureType.AAC_NORM_ATTACK, result -> 20.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_clk_tick_align", FeatureType.AAC_CLICK_TICK_DELAY, FeatureType.AAC_NORM_ATTACK, result -> result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_clk_timing", FeatureType.AAC_CLICK_TIMING, FeatureType.AAC_NORM_ATTACK, result -> result));
|
||||
values.add(new AverageFeatureAnalyzer("aac_clk_cps", FeatureType.AAC_CLICK_CPS, result -> result * result / 5.0));
|
||||
values.add(new AverageFeatureAnalyzer("aac_clk_var", FeatureType.AAC_CLICK_VAR, result -> 0.01 / result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_hitbox_multi", FeatureType.AAC_HITBOX_COUNT, FeatureType.AAC_NORM_ATTACK, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_hitbox_miss", FeatureType.AAC_HITBOX_MISS, FeatureType.AAC_NORM_ATTACK, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_hitbox_walls", FeatureType.AAC_HITBOX_WALLS, FeatureType.AAC_NORM_ATTACK, result -> 5.0 * result));
|
||||
values.add(new PercentileFeatureAnalyzer("aac_hitbox_reach", FeatureType.AAC_HITBOX_REACH, FeatureType.AAC_NORM_ATTACK, 90.0, result -> 200.0 * Math.cbrt(result)));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_interact_generic", FeatureType.AAC_INTERACT_GEN, FeatureType.AAC_NORM_INTERACT, result -> 1.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_interact_break", FeatureType.AAC_INTERACT_BREAK, FeatureType.AAC_NORM_INTERACT, result -> 5.0 * result));
|
||||
values.add(new FrequencyFeatureAnalyzer("aac_interact_place", FeatureType.AAC_INTERACT_PLACE, FeatureType.AAC_NORM_INTERACT, result -> 2.0 * result));
|
||||
values.add(new CountFeatureAnalyzer("aac_misc_abilities", FeatureType.AAC_MISC_ABILITIES, result -> 100.0 * result));
|
||||
values.add(new CountFeatureAnalyzer("aac_misc_pitch", FeatureType.AAC_MISC_PITCH, result -> 100.0 * result));
|
||||
values.add(new CountFeatureAnalyzer("aac_misc_rotation", FeatureType.AAC_MISC_ROTATION, result -> 100.0 * result));
|
||||
values.add(new AccountFeatureAnalyzer("aac_new_user", 20.0, PlayerLoginInfo::getFirstSeen));
|
||||
values.add(new AccountFeatureAnalyzer("aac_new_addr", 20.0, PlayerLoginInfo::getLastSeen));
|
||||
values.add(new ThresholdFeatureAnalyzer("aac_info_move", 10.0, FeatureType.AAC_NORM_MOVE));
|
||||
values.add(new ThresholdFeatureAnalyzer("aac_info_combat", 10.0, FeatureType.AAC_NORM_ATTACK));
|
||||
values.add(new ThresholdFeatureAnalyzer("aac_info_interact", 10.0, FeatureType.AAC_NORM_INTERACT));
|
||||
analyzers = Collections.unmodifiableCollection(values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package me.konsolas.aac.analysis;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class FeatureResult {
|
||||
|
||||
public static final double IGNORED_SCORE = 1000.0;
|
||||
|
||||
private final RiskLevel riskLevel;
|
||||
|
||||
private final String description;
|
||||
|
||||
private final Map details;
|
||||
|
||||
private final double score;
|
||||
|
||||
public FeatureResult(RiskLevel riskLevel, String description, Map details, double score) {
|
||||
for (Entry entry : (Iterable<Entry>) (Iterable<?>) (details.entrySet())) {
|
||||
description = description.replace("{" + (String) entry.getKey() + "}", (CharSequence) entry.getValue());
|
||||
}
|
||||
this.riskLevel = riskLevel;
|
||||
this.description = description;
|
||||
this.details = details;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public static FeatureResult create(String text, Map entries, double value) {
|
||||
if (value >= 10.0) {
|
||||
RiskLevel riskLevel = value > 25.0 ? (value > 100.0 ? RiskLevel.HIGH : RiskLevel.SUSPICIOUS) : RiskLevel.NEUTRAL;
|
||||
double value2 = Math.min(value, 250.0);
|
||||
RiskLevel riskLevel2 = riskLevel;
|
||||
return new FeatureResult(riskLevel2, text, entries, value2);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public FeatureResult scaledBy(double value) {
|
||||
double value2 = this.score * value;
|
||||
Map entries = this.details;
|
||||
String text = this.description;
|
||||
RiskLevel riskLevel2 = this.riskLevel;
|
||||
return new FeatureResult(riskLevel2, text, entries, value2);
|
||||
}
|
||||
|
||||
public RiskLevel getRiskLevel() {
|
||||
return this.riskLevel;
|
||||
}
|
||||
|
||||
public Map getDetails() {
|
||||
return this.details;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public double getScore() {
|
||||
return this.score;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.konsolas.aac.analysis;
|
||||
|
||||
class FeatureWeight {
|
||||
|
||||
private final String featureName;
|
||||
|
||||
private final double weight;
|
||||
|
||||
public FeatureWeight(String featureName, double weight) {
|
||||
this.featureName = featureName;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
static String findString(FeatureWeight featureWeight) {
|
||||
return featureWeight.featureName;
|
||||
}
|
||||
|
||||
static double calculateValue(FeatureWeight featureWeight) {
|
||||
return featureWeight.weight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package me.konsolas.aac.analysis;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
import me.konsolas.aac.AAC;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent.Action;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public class PlayerAnalysisReport {
|
||||
|
||||
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).withZone(ZoneId.systemDefault()).withLocale(Locale.UK);
|
||||
|
||||
private static final String PLUGIN_VERSION = "AAC " + AAC.class.getPackage().getImplementationVersion();
|
||||
|
||||
private final String playerName;
|
||||
|
||||
private final UUID playerId;
|
||||
|
||||
private final RiskLevel riskLevel;
|
||||
|
||||
private final Instant generatedAt;
|
||||
|
||||
private final double score;
|
||||
|
||||
private final List<FeatureResult> features;
|
||||
|
||||
public PlayerAnalysisReport(OfflinePlayer offlinePlayer, RiskLevel riskLevel, Instant generatedAt, double score, List features) {
|
||||
this.playerName = offlinePlayer.getName();
|
||||
this.playerId = offlinePlayer.getUniqueId();
|
||||
this.riskLevel = riskLevel;
|
||||
this.generatedAt = generatedAt;
|
||||
this.score = score;
|
||||
this.features = features;
|
||||
}
|
||||
|
||||
public RiskLevel getRiskLevel() {
|
||||
return this.riskLevel;
|
||||
}
|
||||
|
||||
public Instant getGeneratedAt() {
|
||||
return this.generatedAt;
|
||||
}
|
||||
|
||||
public double getScore() {
|
||||
return this.score;
|
||||
}
|
||||
|
||||
public List<FeatureResult> getFeatures() {
|
||||
return this.features;
|
||||
}
|
||||
|
||||
public List<BaseComponent> toChatComponents() {
|
||||
ArrayList values = new ArrayList();
|
||||
TextComponent textComponent = new TextComponent(" AAC Player Analysis of " + this.playerName + " at ");
|
||||
TextComponent textComponent2 = new TextComponent(DATE_FORMAT.format(this.generatedAt));
|
||||
textComponent2.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, new TextComponent[] { new TextComponent(DATE_FORMAT.getZone().getId()) }));
|
||||
textComponent.addExtra(textComponent2);
|
||||
TextComponent textComponent3 = new TextComponent(" Scoring (" + Math.round(this.score) + ") [" + this.features.size() + " features]");
|
||||
textComponent3.setColor(ChatColor.DARK_AQUA);
|
||||
values.add(textComponent);
|
||||
values.add(textComponent3);
|
||||
for (FeatureResult result : this.features) {
|
||||
TextComponent textComponent4 = new TextComponent(" - ");
|
||||
TextComponent textComponent5 = new TextComponent(result.getDescription());
|
||||
result.getRiskLevel().updateBaseComponent(textComponent5);
|
||||
TextComponent[] textComponents = new TextComponent[result.getDetails().size()];
|
||||
int value = 0;
|
||||
for (Entry entry : (Iterable<Entry>) (Iterable<?>) (result.getDetails().entrySet())) {
|
||||
TextComponent textComponent6 = new TextComponent((String) entry.getKey() + ": ");
|
||||
TextComponent textComponent7 = new TextComponent((String) entry.getValue() + "\n");
|
||||
textComponent7.setColor(ChatColor.GRAY);
|
||||
textComponent6.addExtra(textComponent7);
|
||||
textComponents[value++] = textComponent6;
|
||||
}
|
||||
textComponent5.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, textComponents));
|
||||
textComponent4.addExtra(textComponent5);
|
||||
values.add(textComponent4);
|
||||
}
|
||||
TextComponent textComponent8 = new TextComponent("Generated by " + PLUGIN_VERSION);
|
||||
textComponent8.setColor(ChatColor.DARK_GRAY);
|
||||
values.add(textComponent8);
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package me.konsolas.aac.analysis;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
public enum RiskLevel {
|
||||
|
||||
IGNORED(ChatColor.STRIKETHROUGH), NEUTRAL(ChatColor.GRAY), SUSPICIOUS(ChatColor.GOLD), HIGH(ChatColor.RED);
|
||||
|
||||
private final ChatColor color;
|
||||
|
||||
private RiskLevel(ChatColor color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public ChatColor getColor() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
public void updateBaseComponent(BaseComponent baseComponent) {
|
||||
switch(this) {
|
||||
case IGNORED:
|
||||
baseComponent.setStrikethrough(true);
|
||||
break;
|
||||
case NEUTRAL:
|
||||
baseComponent.setColor(net.md_5.bungee.api.ChatColor.GRAY);
|
||||
break;
|
||||
case SUSPICIOUS:
|
||||
baseComponent.setColor(net.md_5.bungee.api.ChatColor.GOLD);
|
||||
break;
|
||||
case HIGH:
|
||||
baseComponent.setColor(net.md_5.bungee.api.ChatColor.RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.ToLongFunction;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.PlayerAnalysisReport;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public class AccountFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private final double threshold;
|
||||
|
||||
private final ToLongFunction<PlayerLoginInfo> timestampExtractor;
|
||||
|
||||
public AccountFeatureAnalyzer(String text, double threshold, ToLongFunction<PlayerLoginInfo> timestampExtractor) {
|
||||
super(text);
|
||||
this.threshold = threshold;
|
||||
this.timestampExtractor = timestampExtractor;
|
||||
}
|
||||
|
||||
private static double calculateValue(double value) {
|
||||
return 2.0 / (Math.exp(value) + Math.exp(-value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
long value = this.timestampExtractor.applyAsLong(playerLoginInfo);
|
||||
long value2 = System.currentTimeMillis() - value;
|
||||
double value3 = value2 / 8.64E7;
|
||||
double value4 = value3;
|
||||
double value5 = Math.min(this.threshold * calculateValue(value4), 1000.0);
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", value5));
|
||||
entries.put("timestamp", PlayerAnalysisReport.DATE_FORMAT.format(Instant.ofEpochMilli(value)));
|
||||
entries.put("timezone", PlayerAnalysisReport.DATE_FORMAT.getZone().getId());
|
||||
entries.put("address", playerLoginInfo.getAddress());
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value5));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public class AverageFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private final FeatureType feature;
|
||||
|
||||
private final DoubleUnaryOperator scoringFunction;
|
||||
|
||||
public AverageFeatureAnalyzer(String text, FeatureType feature, DoubleUnaryOperator scoringFunction) {
|
||||
super(text);
|
||||
this.feature = feature;
|
||||
this.scoringFunction = scoringFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
double value = 0.0;
|
||||
int value2 = 0;
|
||||
for (FeatureEvent event : values) {
|
||||
if (event.getType() == this.feature) {
|
||||
double value3 = event.getValue() - value;
|
||||
value2++;
|
||||
value += value3 / value2;
|
||||
}
|
||||
}
|
||||
if (value2 < 2) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
double value4 = Math.min(this.scoringFunction.applyAsDouble(value), 1000.0);
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", value4));
|
||||
entries.put("mean", String.format(Locale.UK, "%.4f", value));
|
||||
entries.put("count", Integer.toString(value2));
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value4));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public class CountFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private final FeatureType feature;
|
||||
|
||||
private final DoubleUnaryOperator scoringFunction;
|
||||
|
||||
public CountFeatureAnalyzer(String text, FeatureType feature, DoubleUnaryOperator scoringFunction) {
|
||||
super(text);
|
||||
this.feature = feature;
|
||||
this.scoringFunction = scoringFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
FeatureType feature2 = this.feature;
|
||||
int count = featureAccumulator.getCount(feature2);
|
||||
double value = Math.min(this.scoringFunction.applyAsDouble(count), 1000.0);
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", value));
|
||||
entries.put("count", Integer.toString(count));
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public abstract class FeatureAnalyzer {
|
||||
|
||||
public static final double weight = 0.5;
|
||||
|
||||
private final String configurationPath;
|
||||
|
||||
public FeatureAnalyzer(String configurationPath) {
|
||||
this.configurationPath = configurationPath;
|
||||
}
|
||||
|
||||
public abstract Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text);
|
||||
|
||||
public String getConfigurationPath() {
|
||||
return this.configurationPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public class FrequencyFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private final FeatureType countFeature;
|
||||
|
||||
private final FeatureType totalFeature;
|
||||
|
||||
private final DoubleUnaryOperator scoringFunction;
|
||||
|
||||
public FrequencyFeatureAnalyzer(String text, FeatureType countFeature, FeatureType totalFeature, DoubleUnaryOperator scoringFunction) {
|
||||
super(text);
|
||||
this.countFeature = countFeature;
|
||||
this.totalFeature = totalFeature;
|
||||
this.scoringFunction = scoringFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
FeatureType feature = this.totalFeature;
|
||||
double total = featureAccumulator.getTotal(feature);
|
||||
FeatureType feature2 = this.countFeature;
|
||||
int count = featureAccumulator.getCount(feature2);
|
||||
double value = count / Math.max(total, 0.5);
|
||||
double value2 = Math.min(this.scoringFunction.applyAsDouble(value), 1000.0);
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", value2));
|
||||
entries.put("count", Integer.toString(count));
|
||||
entries.put("time", String.format(Locale.UK, "%.1f", total));
|
||||
entries.put("rate", String.format(Locale.UK, "%.1f", value));
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
import org.hipparchus.stat.descriptive.rank.Percentile;
|
||||
|
||||
public class PercentileFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private static final Percentile PERCENTILE = new Percentile();
|
||||
|
||||
private final FeatureType valueFeature;
|
||||
|
||||
private final FeatureType totalFeature;
|
||||
|
||||
private final double percentile;
|
||||
|
||||
private final DoubleUnaryOperator scoringFunction;
|
||||
|
||||
public PercentileFeatureAnalyzer(String text, FeatureType valueFeature, FeatureType totalFeature, double percentile, DoubleUnaryOperator scoringFunction) {
|
||||
super(text);
|
||||
this.valueFeature = valueFeature;
|
||||
this.totalFeature = totalFeature;
|
||||
this.percentile = percentile;
|
||||
this.scoringFunction = scoringFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
FeatureType feature = this.totalFeature;
|
||||
double total = featureAccumulator.getTotal(feature);
|
||||
FeatureType feature2 = this.valueFeature;
|
||||
double[] values2 = new double[featureAccumulator.getCount(feature2)];
|
||||
int value = 0;
|
||||
for (FeatureEvent event : values) {
|
||||
if (event.getType() == this.valueFeature) {
|
||||
values2[value++] = event.getValue();
|
||||
}
|
||||
}
|
||||
if (values2.length > 10) {
|
||||
double value2 = this.percentile;
|
||||
double value3 = PERCENTILE.evaluate(values2, value2);
|
||||
double value4 = Math.min(this.scoringFunction.applyAsDouble(value3) * Math.min(Math.cbrt(total / 0.5), 1.0), 1000.0);
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", value4));
|
||||
entries.put("percentile", String.format(Locale.UK, "%.1f", this.percentile));
|
||||
entries.put("value", String.format(Locale.UK, "%.4f", value3));
|
||||
entries.put("count", Integer.toString(values2.length));
|
||||
entries.put("time", String.format(Locale.UK, "%.1f", total));
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value4));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public class RateFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private final FeatureType countFeature;
|
||||
|
||||
private final FeatureType totalFeature;
|
||||
|
||||
private final DoubleUnaryOperator scoringFunction;
|
||||
|
||||
public RateFeatureAnalyzer(String text, FeatureType countFeature, FeatureType totalFeature, DoubleUnaryOperator scoringFunction) {
|
||||
super(text);
|
||||
this.countFeature = countFeature;
|
||||
this.totalFeature = totalFeature;
|
||||
this.scoringFunction = scoringFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
FeatureType feature = this.totalFeature;
|
||||
double total = featureAccumulator.getTotal(feature);
|
||||
FeatureType feature2 = this.countFeature;
|
||||
double total2 = featureAccumulator.getTotal(feature2);
|
||||
double value2 = total2 / Math.max(total, 0.5);
|
||||
double value3 = Math.min(this.scoringFunction.applyAsDouble(value2), 1000.0);
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", value3));
|
||||
entries.put("total", String.format(Locale.UK, "%.1f", total2));
|
||||
entries.put("time", String.format(Locale.UK, "%.1f", total));
|
||||
entries.put("rate", String.format(Locale.UK, "%.1f", value2));
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value3));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.analysis.statistics.StatisticalAnalysis;
|
||||
import me.konsolas.aac.analysis.statistics.StatisticalTestResult;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public class TTestFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private final FeatureType feature;
|
||||
|
||||
private final double expectedMean;
|
||||
|
||||
private final boolean upperTail;
|
||||
|
||||
public TTestFeatureAnalyzer(String text, FeatureType feature, double expectedMean, boolean upperTail) {
|
||||
super(text);
|
||||
this.feature = feature;
|
||||
this.expectedMean = expectedMean;
|
||||
this.upperTail = upperTail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
boolean matches = this.upperTail;
|
||||
double value = this.expectedMean;
|
||||
FeatureType feature2 = this.feature;
|
||||
Optional optional = StatisticalAnalysis.testMean(feature2, values, value, matches);
|
||||
if (optional.isPresent()) {
|
||||
double value2 = Math.min(1.0 / ((StatisticalTestResult) optional.get()).getPValue(), 1000.0);
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", value2));
|
||||
entries.put("count", Integer.toString(((StatisticalTestResult) optional.get()).getSampleCount()));
|
||||
entries.put("outliers", Integer.toString(((StatisticalTestResult) optional.get()).getOutlierCount()));
|
||||
entries.put("mean", String.format(Locale.UK, "%.5f", ((StatisticalTestResult) optional.get()).getMean()));
|
||||
entries.put("p", String.format(Locale.UK, "%.5f", ((StatisticalTestResult) optional.get()).getPValue()));
|
||||
entries.put("test", "Student's t-test");
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value2));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.konsolas.aac.analysis.analyzer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import me.konsolas.aac.analysis.FeatureResult;
|
||||
import me.konsolas.aac.analysis.feature.FeatureAccumulator;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
|
||||
public class ThresholdFeatureAnalyzer extends FeatureAnalyzer {
|
||||
|
||||
private final double threshold;
|
||||
|
||||
private final FeatureType feature;
|
||||
|
||||
public ThresholdFeatureAnalyzer(String text, double threshold, FeatureType feature) {
|
||||
super(text);
|
||||
this.threshold = threshold;
|
||||
this.feature = feature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FeatureResult> analyze(List<FeatureEvent> values, FeatureAccumulator featureAccumulator, PlayerLoginInfo playerLoginInfo, String text) {
|
||||
FeatureType feature2 = this.feature;
|
||||
double total = featureAccumulator.getTotal(feature2);
|
||||
if (total <= 0.5) {
|
||||
LinkedHashMap entries = new LinkedHashMap();
|
||||
entries.put("id", this.getConfigurationPath());
|
||||
entries.put("score", String.format(Locale.UK, "%.1f", this.threshold));
|
||||
entries.put("time", String.format(Locale.UK, "%.1f", total));
|
||||
double value = this.threshold;
|
||||
return Optional.ofNullable(FeatureResult.create(text, entries, value));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package me.konsolas.aac.analysis.feature;
|
||||
|
||||
public class FeatureAccumulator {
|
||||
|
||||
private static final int FEATURE_COUNT = FeatureType.values().length;
|
||||
|
||||
private final int[] counts = new int[FEATURE_COUNT];
|
||||
|
||||
private final double[] totals = new double[FEATURE_COUNT];
|
||||
|
||||
public void record(FeatureType feature, float value) {
|
||||
this.counts[feature.ordinal()]++;
|
||||
this.totals[feature.ordinal()] += value;
|
||||
}
|
||||
|
||||
public int getCount(FeatureType feature) {
|
||||
return this.counts[feature.ordinal()];
|
||||
}
|
||||
|
||||
public double getTotal(FeatureType feature) {
|
||||
return this.totals[feature.ordinal()];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.konsolas.aac.analysis.feature;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class FeatureEvent {
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
private final UUID playerId;
|
||||
|
||||
private final FeatureType type;
|
||||
|
||||
private final float value;
|
||||
|
||||
public FeatureEvent(long timestamp, UUID playerId, FeatureType type, float value) {
|
||||
this.timestamp = timestamp;
|
||||
this.playerId = playerId;
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public UUID getPlayerId() {
|
||||
return this.playerId;
|
||||
}
|
||||
|
||||
public FeatureType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package me.konsolas.aac.analysis.feature;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum FeatureType {
|
||||
|
||||
AAC_NORM_MOVE(2),
|
||||
AAC_NORM_ATTACK(1),
|
||||
AAC_NORM_INTERACT(3),
|
||||
AAC_DELAYS_BREAK(100),
|
||||
AAC_DELAYS_PLACE(101),
|
||||
AAC_DELAYS_CONSUME(102),
|
||||
AAC_DELAYS_BOW(103),
|
||||
AAC_DELAYS_REGEN(104),
|
||||
AAC_DELAYS_UNSNEAK(105),
|
||||
AAC_DELAYS_RELEASE(106),
|
||||
AAC_DELAYS_BREAKDELAY(108),
|
||||
AAC_MOVE_NOFALL(300),
|
||||
AAC_MOVE_VEL(301),
|
||||
AAC_MOVE_VEHICLE(302),
|
||||
AAC_MOVE_ELYTRA(303),
|
||||
AAC_MOVE_WATER(304),
|
||||
AAC_MOVE_LAVA(305),
|
||||
AAC_MOVE_NOSLOW(306),
|
||||
AAC_MOVE_GENERIC(307),
|
||||
AAC_MOVE_TIMER(308),
|
||||
AAC_MOVE_INF(309),
|
||||
AAC_AIMBOT_PITCH_SPREAD(401),
|
||||
AAC_AIMBOT_SENS_MISMATCH(402),
|
||||
AAC_AIMBOT_SENS_X_OOR(403),
|
||||
AAC_AIMBOT_SENS_Y_OOR(404),
|
||||
AAC_AIMBOT_BAD_DELTA_X(405),
|
||||
AAC_AIMBOT_BAD_DELTA_Y(406),
|
||||
AAC_AIMBOT_HEAD_SNAP(407),
|
||||
AAC_AIMBOT_ZERO_POINT(408),
|
||||
AAC_CLICK_CPS(500),
|
||||
AAC_CLICK_VAR(501),
|
||||
AAC_CLICK_TICK_DELAY(502),
|
||||
AAC_CLICK_NOSWING(503),
|
||||
AAC_CLICK_TIMING(504),
|
||||
AAC_HITBOX_COUNT(601),
|
||||
AAC_HITBOX_MISS(602),
|
||||
AAC_HITBOX_WALLS(603),
|
||||
AAC_HITBOX_REACH(604),
|
||||
AAC_INTERACT_GEN(700),
|
||||
AAC_INTERACT_BREAK(701),
|
||||
AAC_INTERACT_PLACE(702),
|
||||
AAC_MISC_ABILITIES(801),
|
||||
AAC_MISC_PITCH(802),
|
||||
AAC_MISC_ROTATION(803);
|
||||
|
||||
private final int id;
|
||||
|
||||
private static final Int2ObjectMap BY_ID = new Int2ObjectOpenHashMap();
|
||||
|
||||
private FeatureType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public static FeatureType fromId(int value) {
|
||||
return (FeatureType) BY_ID.get(value);
|
||||
}
|
||||
|
||||
static {
|
||||
for (FeatureType feature : values()) {
|
||||
if (BY_ID.put(feature.getId(), feature) != null) {
|
||||
throw new IllegalStateException("duplicate mapping for id " + feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package me.konsolas.aac.analysis.statistics;
|
||||
|
||||
import java.util.List;
|
||||
import org.hipparchus.distribution.continuous.TDistribution;
|
||||
|
||||
public class DistributionAnalysis {
|
||||
|
||||
public static int calculateCount(List values, double value) {
|
||||
int value2 = 0;
|
||||
while (checkDistributionAnalysis(values, value)) {
|
||||
value2++;
|
||||
}
|
||||
return value2;
|
||||
}
|
||||
|
||||
private static boolean checkDistributionAnalysis(List<Double> values, double value) {
|
||||
double value2 = 0.0;
|
||||
double value3 = 0.0;
|
||||
int size2 = values.size();
|
||||
if (size2 <= 6) {
|
||||
return false;
|
||||
} else {
|
||||
for (double value4 : values) {
|
||||
value2 += value4;
|
||||
value3 += value4 * value4;
|
||||
}
|
||||
double value5 = value2 / size2;
|
||||
double value6 = value3 / size2 - value5 * value5;
|
||||
double value7 = Math.sqrt(value6);
|
||||
double value8 = 0.0;
|
||||
int value9 = -1;
|
||||
for (int index = 0; index < values.size(); index++) {
|
||||
double value10 = (Double) values.get(index);
|
||||
double value11 = Math.abs(value5 - value10);
|
||||
if (value11 > value8) {
|
||||
value8 = value11;
|
||||
value9 = index;
|
||||
}
|
||||
}
|
||||
double value12 = value8 / value7;
|
||||
double value13 = size2 - 2.0;
|
||||
double value14 = value13;
|
||||
TDistribution tDistribution = new TDistribution(value14);
|
||||
double value15 = value / (2 * size2);
|
||||
double value16 = tDistribution.inverseCumulativeProbability(value15);
|
||||
double value17 = value16 * value16;
|
||||
double value18 = (size2 - 1) / Math.sqrt(size2) * Math.sqrt(value17 / (size2 - 2 + value17));
|
||||
if (value12 > value18) {
|
||||
values.remove(value9);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.konsolas.aac.analysis.statistics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import org.hipparchus.stat.StatUtils;
|
||||
import org.hipparchus.stat.inference.TTest;
|
||||
|
||||
public class StatisticalAnalysis {
|
||||
|
||||
private static final TTest T_TEST = new TTest();
|
||||
|
||||
public static Optional<StatisticalTestResult> testMean(FeatureType feature, List<FeatureEvent> values, double value, boolean matches) {
|
||||
ArrayList<Double> values2 = new ArrayList();
|
||||
for (FeatureEvent event : values) {
|
||||
if (event.getType() == feature) {
|
||||
values2.add((double) event.getValue());
|
||||
}
|
||||
}
|
||||
int size2 = values2.size();
|
||||
if (values2.size() <= 6) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
int value2 = DistributionAnalysis.calculateCount(values2, (double) 0.05);
|
||||
double[] values3 = values2.stream().mapToDouble(Double::doubleValue).toArray();
|
||||
double value3 = StatUtils.mean(values3);
|
||||
if (matches) {
|
||||
if (value3 <= value) {
|
||||
return Optional.empty();
|
||||
}
|
||||
} else if (value3 >= value) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(new StatisticalTestResult(T_TEST.tTest(value, values3) / 2.0, size2, value2, value3));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package me.konsolas.aac.analysis.statistics;
|
||||
|
||||
public class StatisticalTestResult {
|
||||
|
||||
private final double pValue;
|
||||
|
||||
private final int sampleCount;
|
||||
|
||||
private final int outlierCount;
|
||||
|
||||
private final double mean;
|
||||
|
||||
public StatisticalTestResult(double pValue, int sampleCount, int outlierCount, double mean) {
|
||||
this.pValue = pValue;
|
||||
this.sampleCount = sampleCount;
|
||||
this.outlierCount = outlierCount;
|
||||
this.mean = mean;
|
||||
}
|
||||
|
||||
public double getPValue() {
|
||||
return this.pValue;
|
||||
}
|
||||
|
||||
public int getSampleCount() {
|
||||
return this.sampleCount;
|
||||
}
|
||||
|
||||
public int getOutlierCount() {
|
||||
return this.outlierCount;
|
||||
}
|
||||
|
||||
public double getMean() {
|
||||
return this.mean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.konsolas.aac.api;
|
||||
|
||||
import java.util.Set;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface AACAPI {
|
||||
|
||||
void addExemption(Player player, AACExemption aACExemption);
|
||||
|
||||
void removeExemption(Player player, AACExemption aACExemption);
|
||||
|
||||
Set getExemptions(Player player);
|
||||
|
||||
void registerCustomFeatureProvider(AACCustomFeatureProvider aACCustomFeatureProvider);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.konsolas.aac.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AACCustomFeature {
|
||||
|
||||
private final String info;
|
||||
|
||||
private final double score;
|
||||
|
||||
private final Map tooltip = new LinkedHashMap();
|
||||
|
||||
public AACCustomFeature(String text, String info, double score, Map entries) {
|
||||
this.info = info;
|
||||
this.score = score;
|
||||
this.tooltip.put("id", text);
|
||||
this.tooltip.putAll(entries);
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return this.info;
|
||||
}
|
||||
|
||||
public Map getTooltip() {
|
||||
return Collections.unmodifiableMap(this.tooltip);
|
||||
}
|
||||
|
||||
public double getScore() {
|
||||
return this.score;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.konsolas.aac.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public interface AACCustomFeatureProvider {
|
||||
|
||||
Collection getFeaturesAsync(OfflinePlayer offlinePlayer);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package me.konsolas.aac.api;
|
||||
|
||||
public class AACExemption {
|
||||
|
||||
private final String reason;
|
||||
|
||||
public AACExemption(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.konsolas.aac.api.internal;
|
||||
|
||||
import java.util.Set;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.api.AACAPI;
|
||||
import me.konsolas.aac.api.AACCustomFeatureProvider;
|
||||
import me.konsolas.aac.api.AACExemption;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class AACApiImplementation implements AACAPI {
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
public AACApiImplementation(AAC plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExemption(Player player, AACExemption aACExemption) {
|
||||
this.plugin.getPacketDispatcher().addExemption(player, aACExemption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExemption(Player player, AACExemption aACExemption) {
|
||||
this.plugin.getPacketDispatcher().removeExemption(player, aACExemption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getExemptions(Player player) {
|
||||
return this.plugin.getPacketDispatcher().getExemptions(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCustomFeatureProvider(AACCustomFeatureProvider aACCustomFeatureProvider) {
|
||||
this.plugin.getAnalysisManager().registerFeatureProvider(aACCustomFeatureProvider);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.konsolas.aac.checks;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.database.PlayerDatabase;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public abstract class BaseCheck {
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
private final Player player;
|
||||
|
||||
private final ConfigurationSection configuration;
|
||||
|
||||
public BaseCheck(AAC plugin, Player player, String text) {
|
||||
this.plugin = plugin;
|
||||
this.player = player;
|
||||
this.configuration = text == null ? plugin.getConfig().getConfigurationSection("checks") : plugin.getConfig().getConfigurationSection("checks." + text);
|
||||
if (this.configuration == null) {
|
||||
throw new IllegalArgumentException("Missing configuration section for checks." + text);
|
||||
}
|
||||
}
|
||||
|
||||
protected ConfigurationSection getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
public AAC getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
protected void recordFeature(FeatureType feature, float value) {
|
||||
PlayerDatabase database = this.getPlugin().getDatabase();
|
||||
Player player = this.getPlayer();
|
||||
database.recordFeature(player, feature, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package me.konsolas.aac.checks;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Play.Client;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerDigType;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.checks.combat.AimbotCheck;
|
||||
import me.konsolas.aac.checks.combat.AutoClickerCheck;
|
||||
import me.konsolas.aac.checks.combat.HitboxCheck;
|
||||
import me.konsolas.aac.checks.interaction.InteractCheck;
|
||||
import me.konsolas.aac.checks.movement.MovementCheck;
|
||||
import me.konsolas.aac.checks.player.ActionDelayCheck;
|
||||
import me.konsolas.aac.checks.player.ActionStateTracker;
|
||||
import me.konsolas.aac.checks.player.MiscellaneousCheck;
|
||||
import me.konsolas.aac.protocol.EntityVelocityPacket;
|
||||
import me.konsolas.aac.protocol.PacketReader;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockDamageEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
|
||||
import org.bukkit.event.entity.EntityShootBowEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
|
||||
public class PlayerChecks extends BaseCheck {
|
||||
|
||||
private long lastTick;
|
||||
|
||||
private final ActionDelayCheck delayCheck;
|
||||
|
||||
private final MovementCheck movementCheck;
|
||||
|
||||
private final AimbotCheck aimbotCheck;
|
||||
|
||||
private final AutoClickerCheck autoClickerCheck;
|
||||
|
||||
private final HitboxCheck hitboxCheck;
|
||||
|
||||
private final InteractCheck interactCheck;
|
||||
|
||||
private final MiscellaneousCheck miscellaneousCheck;
|
||||
|
||||
private final ActionStateTracker actionStateCheck;
|
||||
|
||||
public PlayerChecks(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, (String) (null));
|
||||
this.lastTick = 0L;
|
||||
this.delayCheck = this.getConfiguration().getBoolean("delays.enabled") ? new ActionDelayCheck(aacPlugin, player) : null;
|
||||
this.movementCheck = this.getConfiguration().getBoolean("move.enabled") ? new MovementCheck(aacPlugin, player) : null;
|
||||
this.aimbotCheck = this.getConfiguration().getBoolean("aimbot.enabled") ? new AimbotCheck(aacPlugin, player) : null;
|
||||
this.autoClickerCheck = this.getConfiguration().getBoolean("autoclicker.enabled") ? new AutoClickerCheck(aacPlugin, player) : null;
|
||||
this.hitboxCheck = this.getConfiguration().getBoolean("hitbox.enabled") ? new HitboxCheck(aacPlugin, player) : null;
|
||||
this.interactCheck = this.getConfiguration().getBoolean("interact.enabled") ? new InteractCheck(aacPlugin, player) : null;
|
||||
this.miscellaneousCheck = this.getConfiguration().getBoolean("misc.enabled") ? new MiscellaneousCheck(aacPlugin, player) : null;
|
||||
this.actionStateCheck = new ActionStateTracker(aacPlugin, player);
|
||||
}
|
||||
|
||||
public void updateLocation(Location location) {
|
||||
if (this.movementCheck != null) {
|
||||
this.movementCheck.updateLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLocationWithMovementCheck(Location location) {
|
||||
if (this.movementCheck != null) {
|
||||
this.movementCheck.updateLocationWithFallDistance(location);
|
||||
}
|
||||
if (this.aimbotCheck != null) {
|
||||
this.aimbotCheck.updateLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
public void onEvent(Event event) {
|
||||
if (event instanceof PlayerMoveEvent) {
|
||||
this.actionStateCheck.recordMovement();
|
||||
}
|
||||
if (event instanceof BlockPlaceEvent) {
|
||||
BlockPlaceEvent blockPlaceEvent = (BlockPlaceEvent) event;
|
||||
if (this.delayCheck != null && this.delayCheck.isActionDelay()) {
|
||||
blockPlaceEvent.setCancelled(true);
|
||||
}
|
||||
} else if (event instanceof BlockDamageEvent) {
|
||||
BlockDamageEvent blockDamageEvent = (BlockDamageEvent) event;
|
||||
if (this.delayCheck != null) {
|
||||
ActionDelayCheck actionDelayCheck = this.delayCheck;
|
||||
boolean instaBreak = blockDamageEvent.getInstaBreak();
|
||||
boolean matches = instaBreak;
|
||||
if (actionDelayCheck.checkActionDelay(matches)) {
|
||||
blockDamageEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
} else if (event instanceof BlockBreakEvent) {
|
||||
BlockBreakEvent blockBreakEvent = (BlockBreakEvent) event;
|
||||
if (this.delayCheck != null) {
|
||||
ActionDelayCheck actionDelayCheck2 = this.delayCheck;
|
||||
Block block = blockBreakEvent.getBlock();
|
||||
if (actionDelayCheck2.checkActionDelay(block)) {
|
||||
blockBreakEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
if (this.interactCheck != null) {
|
||||
InteractCheck interactCheck2 = this.interactCheck;
|
||||
Block block2 = blockBreakEvent.getBlock();
|
||||
if (interactCheck2.checkInteract(block2)) {
|
||||
blockBreakEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
} else if (event instanceof PlayerInteractEvent) {
|
||||
PlayerInteractEvent playerInteractEvent = (PlayerInteractEvent) event;
|
||||
if (this.delayCheck != null) {
|
||||
ActionDelayCheck actionDelayCheck3 = this.delayCheck;
|
||||
Action action = playerInteractEvent.getAction();
|
||||
Action action2 = action;
|
||||
actionDelayCheck3.onAction(action2);
|
||||
}
|
||||
if (this.interactCheck != null && this.interactCheck.onPlayerInteractEvent(playerInteractEvent)) {
|
||||
playerInteractEvent.setCancelled(true);
|
||||
}
|
||||
this.actionStateCheck.recordInteraction();
|
||||
} else if (event instanceof PlayerItemConsumeEvent) {
|
||||
PlayerItemConsumeEvent playerItemConsumeEvent = (PlayerItemConsumeEvent) event;
|
||||
if (this.delayCheck != null) {
|
||||
ActionDelayCheck actionDelayCheck4 = this.delayCheck;
|
||||
Material type = playerItemConsumeEvent.getItem().getType();
|
||||
Material material = type;
|
||||
if (actionDelayCheck4.checkActionDelay(material)) {
|
||||
playerItemConsumeEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
} else if (event instanceof EntityShootBowEvent) {
|
||||
EntityShootBowEvent entityShootBowEvent = (EntityShootBowEvent) event;
|
||||
if (this.delayCheck != null && entityShootBowEvent.getBow() != null && entityShootBowEvent.getBow().getType() == Material.BOW) {
|
||||
ActionDelayCheck actionDelayCheck5 = this.delayCheck;
|
||||
float force = entityShootBowEvent.getForce();
|
||||
if (actionDelayCheck5.checkActionDelay(force)) {
|
||||
entityShootBowEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
} else if (event instanceof EntityRegainHealthEvent) {
|
||||
EntityRegainHealthEvent entityRegainHealthEvent = (EntityRegainHealthEvent) event;
|
||||
if (this.delayCheck != null) {
|
||||
ActionDelayCheck actionDelayCheck6 = this.delayCheck;
|
||||
RegainReason regainReason = entityRegainHealthEvent.getRegainReason();
|
||||
if (actionDelayCheck6.onEntityRegainHealthEventRegainReason(regainReason)) {
|
||||
entityRegainHealthEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePacket(PacketEvent event) {
|
||||
if (this.movementCheck != null) {
|
||||
this.movementCheck.handlePacket(event);
|
||||
}
|
||||
if (this.hitboxCheck != null) {
|
||||
this.hitboxCheck.handlePacket(event);
|
||||
}
|
||||
if (event.getPacketType() == Client.ENTITY_ACTION) {
|
||||
if (event.getPacket().getPlayerActions().read(0) == PlayerAction.STOP_SNEAKING && this.delayCheck != null) {
|
||||
this.delayCheck.tickWithCheckFastSneak();
|
||||
}
|
||||
} else if (event.getPacketType() == Client.BLOCK_DIG) {
|
||||
if (event.getPacket().getPlayerDigTypes().read(0) == PlayerDigType.RELEASE_USE_ITEM && this.delayCheck != null) {
|
||||
this.delayCheck.tick();
|
||||
}
|
||||
if (this.autoClickerCheck != null) {
|
||||
AutoClickerCheck autoClickerCheck2 = this.autoClickerCheck;
|
||||
BlockPosition blockPosition = (BlockPosition) event.getPacket().getBlockPositionModifier().read(0);
|
||||
PlayerDigType playerDigType = (PlayerDigType) event.getPacket().getPlayerDigTypes().read(0);
|
||||
BlockPosition blockPosition2 = blockPosition;
|
||||
autoClickerCheck2.updateBlockPosition(blockPosition2, playerDigType);
|
||||
}
|
||||
} else {
|
||||
PacketType packetType = event.getPacketType();
|
||||
if (PacketReader.checkPacketReader(packetType)) {
|
||||
if (this.autoClickerCheck != null) {
|
||||
this.autoClickerCheck.tickWithTickDelayCheck();
|
||||
}
|
||||
if (this.interactCheck != null) {
|
||||
InteractCheck interactCheck2 = this.interactCheck;
|
||||
PacketContainer packet = event.getPacket();
|
||||
PacketContainer packet2 = packet;
|
||||
interactCheck2.handlePacket(packet2);
|
||||
}
|
||||
} else if (event.getPacketType() == Client.BLOCK_PLACE || event.getPacketType() == PacketReader.USE_ITEM) {
|
||||
PacketContainer packet3 = event.getPacket();
|
||||
EntityVelocityPacket entityVelocityPacket = EntityVelocityPacket.create(packet3);
|
||||
if (entityVelocityPacket != null && this.autoClickerCheck != null) {
|
||||
this.autoClickerCheck.tickWithCheckTiming();
|
||||
}
|
||||
} else if (event.getPacketType() == Client.ABILITIES && this.miscellaneousCheck != null) {
|
||||
long value = this.lastTick;
|
||||
this.miscellaneousCheck.updateTime(value);
|
||||
}
|
||||
}
|
||||
if (event.getPacketType() == Client.LOOK || event.getPacketType() == Client.POSITION_LOOK) {
|
||||
float value2 = (Float) event.getPacket().getFloat().read(0);
|
||||
float value3 = (Float) event.getPacket().getFloat().read(1);
|
||||
if (this.aimbotCheck != null) {
|
||||
this.aimbotCheck.analyzeRotation(value3, value2);
|
||||
}
|
||||
if (this.miscellaneousCheck != null) {
|
||||
long value4 = this.lastTick;
|
||||
this.miscellaneousCheck.updateTime(value4, value3, value2);
|
||||
}
|
||||
}
|
||||
PacketContainer packet4 = event.getPacket();
|
||||
if (PacketReader.checkPacketReader(packet4)) {
|
||||
if (this.aimbotCheck != null) {
|
||||
this.aimbotCheck.tick();
|
||||
}
|
||||
if (this.autoClickerCheck != null) {
|
||||
this.autoClickerCheck.updateCount();
|
||||
}
|
||||
this.actionStateCheck.recordAttack();
|
||||
}
|
||||
if (event.getPacketType() == Client.ARM_ANIMATION && this.autoClickerCheck != null) {
|
||||
this.autoClickerCheck.tick();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTime(long value) {
|
||||
this.lastTick++;
|
||||
if (this.movementCheck != null) {
|
||||
this.movementCheck.tick();
|
||||
}
|
||||
if (this.hitboxCheck != null) {
|
||||
this.hitboxCheck.tick();
|
||||
}
|
||||
this.actionStateCheck.updateTime(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package me.konsolas.aac.checks;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
public class ViolationBuffer {
|
||||
|
||||
private float level;
|
||||
|
||||
private long lastUpdate;
|
||||
|
||||
private final boolean mitigate;
|
||||
|
||||
private final float threshold;
|
||||
|
||||
private final float maximum;
|
||||
|
||||
private final float decay;
|
||||
|
||||
public ViolationBuffer(boolean mitigate, float threshold, float value2, float decay) {
|
||||
this.mitigate = mitigate;
|
||||
this.threshold = threshold;
|
||||
this.maximum = Math.max(value2, 1.0F);
|
||||
this.decay = decay;
|
||||
}
|
||||
|
||||
public boolean checkViolationBuffer(float value) {
|
||||
long now = System.currentTimeMillis();
|
||||
this.level = Math.min(Math.max(Math.max(this.level - this.decay * (float) (now - this.lastUpdate) / 1000.0F, 0.0F) + value, 0.0F), this.maximum);
|
||||
this.lastUpdate = now;
|
||||
return this.mitigate && this.level >= this.threshold;
|
||||
}
|
||||
|
||||
public float getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public static ViolationBuffer create(Map entries) {
|
||||
return new ViolationBuffer((Boolean) entries.get("mitigate"), ((Number) entries.get("threshold")).floatValue(), ((Number) entries.get("max")).floatValue(), ((Number) entries.get("decay")).floatValue());
|
||||
}
|
||||
|
||||
public static ViolationBuffer create(ConfigurationSection section, String text) {
|
||||
Map values = ((ConfigurationSection) Objects.requireNonNull(section.get(text))).getValues(false);
|
||||
Map entries = values;
|
||||
return create(entries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import me.konsolas.aac.util.math.MouseSensitivityMath;
|
||||
import me.konsolas.aac.util.math.MouseStepEstimator;
|
||||
import me.konsolas.aac.util.time.ExpiringCounter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class AimbotCheck extends BaseCheck {
|
||||
|
||||
private long lastAttack;
|
||||
|
||||
private final ExpiringCounter sampleTimer;
|
||||
|
||||
private float lastPitch;
|
||||
|
||||
private float lastYaw;
|
||||
|
||||
private float previousPitch;
|
||||
|
||||
private float previousYaw;
|
||||
|
||||
private final double[] yawSamples;
|
||||
|
||||
private final double[] pitchSamples;
|
||||
|
||||
private int sampleIndex;
|
||||
|
||||
private boolean checkMouseDelta;
|
||||
|
||||
private boolean checkSensitivity;
|
||||
|
||||
private double yawSensitivity;
|
||||
|
||||
private double pitchSensitivity;
|
||||
|
||||
private float lastSensitivity;
|
||||
|
||||
private final SensitivityCheck sensitivityCheck;
|
||||
|
||||
private final MouseDeltaCheck mouseDeltaCheck;
|
||||
|
||||
private final HeadSnapCheck headSnapCheck;
|
||||
|
||||
private final PitchSpreadCheck pitchSpreadCheck;
|
||||
|
||||
private final boolean checkZeroPoint;
|
||||
|
||||
public AimbotCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("aimbot"));
|
||||
this.sampleTimer = new ExpiringCounter();
|
||||
this.yawSamples = new double[50];
|
||||
this.pitchSamples = new double[50];
|
||||
this.sampleIndex = 0;
|
||||
this.checkMouseDelta = false;
|
||||
this.sensitivityCheck = this.getConfiguration().getBoolean("check_sensitivity") ? new SensitivityCheck(aacPlugin, player) : null;
|
||||
this.mouseDeltaCheck = this.getConfiguration().getBoolean("check_mouse_delta") ? new MouseDeltaCheck(aacPlugin, player) : null;
|
||||
this.headSnapCheck = this.getConfiguration().getBoolean("check_head_snap") ? new HeadSnapCheck(aacPlugin, player) : null;
|
||||
this.pitchSpreadCheck = this.getConfiguration().getBoolean("check_pitch_spread") ? new PitchSpreadCheck(aacPlugin, player) : null;
|
||||
this.checkZeroPoint = this.getConfiguration().getBoolean("check_zero_point");
|
||||
Location location = player.getLocation();
|
||||
Location location2 = location;
|
||||
this.updateLocation(location2);
|
||||
}
|
||||
|
||||
public void updateLocation(Location location) {
|
||||
this.previousYaw = this.lastYaw = location.getYaw();
|
||||
this.previousPitch = this.lastPitch = location.getPitch();
|
||||
}
|
||||
|
||||
public void analyzeRotation(float value, float value2) {
|
||||
if (this.getPlayer().getVehicle() != null) {
|
||||
this.previousYaw = this.lastYaw = value;
|
||||
this.previousPitch = this.lastPitch = value2;
|
||||
} else {
|
||||
float value3 = Math.abs(value2 - this.previousYaw);
|
||||
float value4 = Math.abs(value - this.previousPitch);
|
||||
if (value3 > 0.1 && value4 > 0.1 && value3 < 180.0F && Math.abs(value) < 89.0F && Math.abs(this.previousPitch) < 89.0F) {
|
||||
this.yawSamples[this.sampleIndex] = value3;
|
||||
this.pitchSamples[this.sampleIndex] = value4;
|
||||
if (++this.sampleIndex == 50) {
|
||||
this.sampleIndex = 0;
|
||||
this.checkMouseDelta = true;
|
||||
this.checkSensitivity = true;
|
||||
}
|
||||
}
|
||||
this.previousPitch = value;
|
||||
this.previousYaw = value2;
|
||||
if (System.currentTimeMillis() - this.lastAttack <= 500L) {
|
||||
if (this.pitchSpreadCheck != null) {
|
||||
this.pitchSpreadCheck.updateValue(value4, value3);
|
||||
}
|
||||
if (this.headSnapCheck != null) {
|
||||
this.headSnapCheck.updateValue(value4, value3);
|
||||
}
|
||||
if (this.checkMouseDelta && this.sensitivityCheck != null) {
|
||||
this.updateRotation(value3, value4);
|
||||
}
|
||||
if (this.checkMouseDelta && this.checkZeroPoint) {
|
||||
float value5 = (float) Math.abs(this.lastYaw - Math.round(this.lastYaw / this.yawSensitivity) * this.yawSensitivity);
|
||||
float value6 = (float) Math.abs(value - Math.round(value2 / this.yawSensitivity) * this.yawSensitivity);
|
||||
if (value6 < 1.0E-5 && value5 > 0.001 && this.sampleTimer.tryAcquire()) {
|
||||
float value7 = (float) this.yawSensitivity;
|
||||
FeatureType feature = FeatureType.AAC_AIMBOT_ZERO_POINT;
|
||||
this.recordFeature(feature, value7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRotation(float value, float value2) {
|
||||
if (this.checkSensitivity) {
|
||||
this.checkSensitivity = false;
|
||||
double[] values = this.yawSamples;
|
||||
this.yawSensitivity = MouseStepEstimator.estimateStep(values);
|
||||
double[] values2 = this.pitchSamples;
|
||||
this.pitchSensitivity = MouseStepEstimator.estimateStep(values2);
|
||||
double value6 = this.yawSensitivity;
|
||||
double value7 = MouseSensitivityMath.estimateSensitivity(value6);
|
||||
double value8 = this.pitchSensitivity;
|
||||
double value9 = MouseSensitivityMath.estimateAbsoluteSensitivity(value8);
|
||||
int value10 = MouseSensitivityMath.sensitivityPercent(value7);
|
||||
int value11 = MouseSensitivityMath.sensitivityPercent(value9);
|
||||
if (!this.sensitivityCheck.checkHeadSnap(value10, value11)) {
|
||||
this.sampleIndex = 0;
|
||||
this.checkMouseDelta = false;
|
||||
return;
|
||||
}
|
||||
this.lastSensitivity = (float) value7;
|
||||
}
|
||||
double value12 = value;
|
||||
double value13 = this.yawSensitivity;
|
||||
double value14 = value12;
|
||||
int value15 = MouseSensitivityMath.mouseCounts(value14, value13);
|
||||
double value16 = value2;
|
||||
double value17 = this.pitchSensitivity;
|
||||
double value18 = value16;
|
||||
int value19 = MouseSensitivityMath.absoluteMouseCounts(value18, value17);
|
||||
float value20 = this.lastSensitivity;
|
||||
this.mouseDeltaCheck.updateCount(value15, value19, value, value2, value20);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
this.lastAttack = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerDigType;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.hipparchus.stat.StatUtils;
|
||||
|
||||
public class AutoClickerCheck extends BaseCheck {
|
||||
|
||||
private boolean checkTiming;
|
||||
|
||||
private long lastClick;
|
||||
|
||||
private long lastSwing;
|
||||
|
||||
private long lastTick;
|
||||
|
||||
private final double[] clickSamples;
|
||||
|
||||
private int sampleIndex;
|
||||
|
||||
private final ClickTickDelayCheck tickDelayCheck;
|
||||
|
||||
private final ClickTimingCheck timingCheck;
|
||||
|
||||
private final NoSwingCheck noSwingCheck;
|
||||
|
||||
public AutoClickerCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("autoclicker"));
|
||||
this.checkTiming = false;
|
||||
this.sampleIndex = 0;
|
||||
this.tickDelayCheck = this.getConfiguration().getBoolean("check_tick_delay") ? new ClickTickDelayCheck(aacPlugin, player) : null;
|
||||
this.timingCheck = this.getConfiguration().getBoolean("check_no_swing") ? new ClickTimingCheck(aacPlugin, player) : null;
|
||||
this.noSwingCheck = this.getConfiguration().getBoolean("check_timing") ? new NoSwingCheck(aacPlugin, player) : null;
|
||||
this.clickSamples = new double[this.getConfiguration().getInt("sample_size")];
|
||||
}
|
||||
|
||||
public void updateCount() {
|
||||
if (this.tickDelayCheck != null) {
|
||||
this.tickDelayCheck.tickWithClicksThisTick();
|
||||
}
|
||||
if (this.timingCheck != null) {
|
||||
this.timingCheck.tick();
|
||||
}
|
||||
if (this.noSwingCheck != null) {
|
||||
this.noSwingCheck.tick();
|
||||
}
|
||||
this.lastTick = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void tickWithTickDelayCheck() {
|
||||
if (this.tickDelayCheck != null) {
|
||||
this.tickDelayCheck.tick();
|
||||
}
|
||||
if (this.noSwingCheck != null) {
|
||||
this.noSwingCheck.tickWithLastSwing();
|
||||
}
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
long now = System.currentTimeMillis();
|
||||
if (this.timingCheck != null) {
|
||||
this.timingCheck.tickWithPreviousAttack();
|
||||
}
|
||||
if (!this.checkTiming && now - this.lastClick >= 100L && System.currentTimeMillis() - this.lastTick <= 1000L) {
|
||||
if (this.lastSwing != 0L) {
|
||||
double value = (now - this.lastSwing) / 1000.0;
|
||||
this.clickSamples[this.sampleIndex++] = value;
|
||||
if (this.sampleIndex >= this.clickSamples.length) {
|
||||
this.sampleIndex = 0;
|
||||
double[] values = this.clickSamples;
|
||||
double value2 = StatUtils.mean(values);
|
||||
double[] values2 = this.clickSamples;
|
||||
double value3 = StatUtils.variance(values2);
|
||||
float value4 = (float) (1.0 / value2);
|
||||
FeatureType feature = FeatureType.AAC_CLICK_CPS;
|
||||
this.recordFeature(feature, value4);
|
||||
float value5 = (float) value3;
|
||||
FeatureType feature2 = FeatureType.AAC_CLICK_VAR;
|
||||
this.recordFeature(feature2, value5);
|
||||
}
|
||||
}
|
||||
this.lastSwing = now;
|
||||
} else {
|
||||
this.lastSwing = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateBlockPosition(BlockPosition blockPosition, PlayerDigType playerDigType) {
|
||||
this.checkTiming = playerDigType == PlayerDigType.START_DESTROY_BLOCK;
|
||||
}
|
||||
|
||||
public void tickWithCheckTiming() {
|
||||
this.checkTiming = false;
|
||||
this.lastClick = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ClickTickDelayCheck extends BaseCheck {
|
||||
|
||||
private int previousClicks;
|
||||
|
||||
private int clicksThisTick;
|
||||
|
||||
private int previousDelta;
|
||||
|
||||
private int violations;
|
||||
|
||||
public ClickTickDelayCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("autoclicker"));
|
||||
}
|
||||
|
||||
public void tickWithClicksThisTick() {
|
||||
int value = this.clicksThisTick - this.previousClicks;
|
||||
if (value == this.previousDelta) {
|
||||
this.violations++;
|
||||
if (this.violations >= 16) {
|
||||
float value2 = this.violations;
|
||||
FeatureType feature = FeatureType.AAC_CLICK_TICK_DELAY;
|
||||
this.recordFeature(feature, value2);
|
||||
}
|
||||
} else {
|
||||
this.violations = 0;
|
||||
}
|
||||
this.previousDelta = value;
|
||||
this.previousClicks = this.clicksThisTick;
|
||||
this.clicksThisTick = 0;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
this.clicksThisTick++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ClickTimingCheck extends BaseCheck {
|
||||
|
||||
boolean previousAttack;
|
||||
|
||||
public ClickTimingCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("autoclicker"));
|
||||
this.previousAttack = false;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (this.previousAttack) {
|
||||
FeatureType feature = FeatureType.AAC_CLICK_NOSWING;
|
||||
this.recordFeature(feature, (float) 0.0F);
|
||||
} else {
|
||||
this.previousAttack = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void tickWithPreviousAttack() {
|
||||
this.previousAttack = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class HeadSnapCheck extends BaseCheck {
|
||||
|
||||
private float lastYaw;
|
||||
|
||||
private float lastPitch;
|
||||
|
||||
private final float[] yawSamples;
|
||||
|
||||
private final float[] pitchSamples;
|
||||
|
||||
private int sampleIndex;
|
||||
|
||||
private float previousDelta;
|
||||
|
||||
public HeadSnapCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("aimbot"));
|
||||
this.yawSamples = new float[20];
|
||||
this.pitchSamples = new float[20];
|
||||
this.sampleIndex = 0;
|
||||
this.previousDelta = 0.0F;
|
||||
}
|
||||
|
||||
public void updateValue(float value, float value2) {
|
||||
if (value2 > 0.0 && value > 0.0) {
|
||||
float value3 = Math.abs(value2 - this.lastPitch);
|
||||
float value4 = Math.abs(value - this.lastYaw);
|
||||
this.lastPitch = value2;
|
||||
this.lastYaw = value;
|
||||
float value5 = Math.abs(value3 - value2);
|
||||
float value6 = Math.abs(value4 - value);
|
||||
this.pitchSamples[this.sampleIndex] = value5;
|
||||
this.yawSamples[this.sampleIndex] = value6;
|
||||
if (++this.sampleIndex == 20) {
|
||||
this.sampleIndex = 0;
|
||||
for (int index = 0; index < 20; index++) {
|
||||
if (this.yawSamples[index] == 0.0F || this.pitchSamples[index] == 0.0F) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
float value7 = 0.0F;
|
||||
float value8 = 0.0F;
|
||||
float value9 = 0.0F;
|
||||
float value10 = 0.0F;
|
||||
for (int index2 = 0; index2 < 20; index2++) {
|
||||
value7 += this.pitchSamples[index2];
|
||||
value8 += this.pitchSamples[index2] * this.pitchSamples[index2];
|
||||
value9 += this.yawSamples[index2];
|
||||
value10 += this.yawSamples[index2] * this.yawSamples[index2];
|
||||
}
|
||||
float value11 = (float) (value8 / 20.0F - Math.pow(value7 / 20.0F, 2.0));
|
||||
float value12 = (float) (value10 / 20.0F - Math.pow(value9 / 20.0F, 2.0));
|
||||
if (value11 > 50.0F && value12 > 3.0F) {
|
||||
if (++this.previousDelta > 3.0F) {
|
||||
float value13 = value11 + value12;
|
||||
FeatureType feature = FeatureType.AAC_AIMBOT_HEAD_SNAP;
|
||||
this.recordFeature(feature, value13);
|
||||
} else {
|
||||
this.previousDelta = Math.max(this.previousDelta - 0.1F, 0.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import me.konsolas.aac.checks.ViolationBuffer;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.NativeAccessProvider;
|
||||
import me.konsolas.aac.debug.DebugManager;
|
||||
import me.konsolas.aac.movement.PlayerPose;
|
||||
import me.konsolas.aac.protocol.PacketReader;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.timings.EntityTracker;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.util.time.ExpiringCounter;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Enderman;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class HitboxCheck extends BaseCheck {
|
||||
|
||||
private static final Vector previousEyePosition = new Vector(0.1, 0.1, 0.1);
|
||||
|
||||
private static final Vector eyePosition = new Vector(0.13, 0.13, 0.13);
|
||||
|
||||
private static final Vector lookDirection = new Vector(0.5, 0.5, 0.5);
|
||||
|
||||
private final BlockingQueue<TeleportState> pendingAttacks;
|
||||
|
||||
private Location lastLocation;
|
||||
|
||||
private final ExpiringCounter attackTimer;
|
||||
|
||||
private final ViolationBuffer hitboxBuffer;
|
||||
|
||||
private final boolean checkBlocks;
|
||||
|
||||
public HitboxCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("hitbox"));
|
||||
this.pendingAttacks = new ArrayBlockingQueue(10);
|
||||
this.attackTimer = new ExpiringCounter(100L);
|
||||
this.checkBlocks = this.getConfiguration().getBoolean("check_blocks");
|
||||
ConfigurationSection configuration = this.getConfiguration();
|
||||
ConfigurationSection section = configuration;
|
||||
this.hitboxBuffer = ViolationBuffer.create(section, (String) "vl");
|
||||
}
|
||||
|
||||
public void handlePacket(PacketEvent event) {
|
||||
PacketContainer packet = event.getPacket();
|
||||
PacketType type = packet.getType();
|
||||
if (PacketReader.checkPacketReader(type)) {
|
||||
if (this.lastLocation == null || this.lastLocation.getWorld() != this.getPlayer().getWorld()) {
|
||||
this.lastLocation = this.getPlayer().getLocation();
|
||||
}
|
||||
Location location = this.lastLocation;
|
||||
this.lastLocation = PacketReader.findLocation(packet, location);
|
||||
if (this.getPlayer().getVehicle() != null) {
|
||||
Location location2 = this.getPlayer().getLocation();
|
||||
this.lastLocation.setX(location2.getX());
|
||||
this.lastLocation.setY(location2.getY());
|
||||
this.lastLocation.setZ(location2.getZ());
|
||||
}
|
||||
for (TeleportState teleportState : this.pendingAttacks) {
|
||||
Location location3 = this.lastLocation;
|
||||
TeleportState.updateTeleportState(teleportState, location3);
|
||||
}
|
||||
} else if (PacketReader.checkPacketReader(packet)) {
|
||||
int value2 = (Integer) packet.getIntegers().read(0);
|
||||
if (value2 < 0) {
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "hitbox: ignoring invalid eid " + value2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!this.pendingAttacks.offer(new TeleportState(value2, this.lastLocation))) {
|
||||
this.hitboxBuffer.checkViolationBuffer((float) 1.0F);
|
||||
FeatureType feature = FeatureType.AAC_HITBOX_COUNT;
|
||||
this.recordFeature(feature, (float) 1.0F);
|
||||
}
|
||||
if (this.hitboxBuffer.checkViolationBuffer((float) 0.0F)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RayTraceCandidate findRayTraceCandidate(Location location, Iterable<AxisAlignedBoundingBox> iterable, Vector vector) {
|
||||
Vector direction = location.getDirection();
|
||||
Vector vector2 = location.toVector();
|
||||
double value;
|
||||
BlockRayTraceResult blockRayTraceResult;
|
||||
if (this.checkBlocks) {
|
||||
NativeAccess instance = NativeAccessProvider.getInstance();
|
||||
Player player = this.getPlayer();
|
||||
blockRayTraceResult = instance.rayTrace(player, vector2, direction, 4.5);
|
||||
value = blockRayTraceResult.isBlockRayTraceResult() ? blockRayTraceResult.getVector().distance(vector2) : Double.POSITIVE_INFINITY;
|
||||
} else {
|
||||
blockRayTraceResult = null;
|
||||
value = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
double value2 = Double.POSITIVE_INFINITY;
|
||||
for (AxisAlignedBoundingBox box : iterable) {
|
||||
if (box != null) {
|
||||
double value3 = box.expand(vector).rayIntersectionDistance(vector2, direction);
|
||||
if (value3 < value2) {
|
||||
value2 = value3;
|
||||
if (value3 < Math.min(3.0, value)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new RayTraceCandidate(value2, value, blockRayTraceResult);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (!this.pendingAttacks.isEmpty()) {
|
||||
ArrayList<TeleportState> values = new ArrayList();
|
||||
while (!this.pendingAttacks.isEmpty() && (TeleportState.findLocation((TeleportState) this.pendingAttacks.element()) != null || TeleportState.checkTeleportState((TeleportState) this.pendingAttacks.element()))) {
|
||||
values.add((TeleportState) this.pendingAttacks.poll());
|
||||
}
|
||||
for (TeleportState teleportState : this.pendingAttacks) {
|
||||
TeleportState.checkTeleportState(teleportState, true);
|
||||
}
|
||||
AAC plugin = this.getPlugin();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
ProtocolVersion version = plugin.getProtocolVersion(player2);
|
||||
ArrayList<Location> values2 = new ArrayList();
|
||||
for (TeleportState teleportState2 : values) {
|
||||
if (TeleportState.findLocationWithOrigin(teleportState2) != null) {
|
||||
Vector vector = this.getPlayer().getVehicle() != null ? lookDirection : (TeleportState.findLocation(teleportState2) == null ? eyePosition : previousEyePosition);
|
||||
Entity entityFromID = ProtocolLibrary.getProtocolManager().getEntityFromID(this.getPlayer().getWorld(), TeleportState.calculateCount(teleportState2));
|
||||
if (entityFromID instanceof LivingEntity && !(entityFromID instanceof Enderman)) {
|
||||
EntityTracker entityTracker = this.getPlugin().getEntityTracker();
|
||||
LivingEntity livingEntity = (LivingEntity) entityFromID;
|
||||
entityTracker.updateLivingEntity(livingEntity);
|
||||
EntityTracker entityTracker2 = this.getPlugin().getEntityTracker();
|
||||
LivingEntity livingEntity2 = (LivingEntity) entityFromID;
|
||||
List values3 = entityTracker2.findList(livingEntity2);
|
||||
if (values3 != null) {
|
||||
values2.clear();
|
||||
values2.add(TeleportState.findLocationWithOrigin(teleportState2));
|
||||
if (TeleportState.findLocation(teleportState2) != null) {
|
||||
values2.add(TeleportState.findLocation(teleportState2));
|
||||
values2.add(TeleportState.findLocation(teleportState2).clone().setDirection(TeleportState.findLocationWithOrigin(teleportState2).getDirection()));
|
||||
values2.add(TeleportState.findLocationWithOrigin(teleportState2).clone().setDirection(TeleportState.findLocation(teleportState2).getDirection()));
|
||||
}
|
||||
RayTraceCandidate rayTraceCandidate = null;
|
||||
for (PlayerPose playerPose : PlayerPose.POSES) {
|
||||
double value = playerPose.calculateValue(version);
|
||||
for (Location location : values2) {
|
||||
location = location.clone().add(0.0, value, 0.0);
|
||||
RayTraceCandidate rayTraceCandidate2 = this.findRayTraceCandidate(location, values3, vector);
|
||||
if (RayTraceCandidate.checkRayTraceCandidate(rayTraceCandidate2, rayTraceCandidate)) {
|
||||
rayTraceCandidate = rayTraceCandidate2;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert rayTraceCandidate != null;
|
||||
if (!Double.isFinite(RayTraceCandidate.calculateValueWithDistance(rayTraceCandidate))) {
|
||||
float value2 = RayTraceCandidate.calculateValue(rayTraceCandidate);
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
player = this.getPlayer();
|
||||
Player player3 = player;
|
||||
if (debugManager.isDebugging(player3)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "hitbox: no intersection: %.3f%+.3f", new Object[] { this.hitboxBuffer.getLevel(), value2 });
|
||||
}
|
||||
this.hitboxBuffer.checkViolationBuffer(value2);
|
||||
if (this.attackTimer.tryAcquire()) {
|
||||
FeatureType feature = FeatureType.AAC_HITBOX_MISS;
|
||||
this.recordFeature(feature, value2);
|
||||
}
|
||||
} else if (RayTraceCandidate.calculateValueWithDistance(rayTraceCandidate) > RayTraceCandidate.calculateValueWithTolerance(rayTraceCandidate)) {
|
||||
float value3 = RayTraceCandidate.calculateValue(rayTraceCandidate);
|
||||
DebugManager debugManager2 = this.getPlugin().getDebugManager();
|
||||
player = this.getPlayer();
|
||||
Player player4 = player;
|
||||
if (debugManager2.isDebugging(player4)) {
|
||||
assert RayTraceCandidate.findBlockRayTraceResult(rayTraceCandidate) != null;
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "hitbox: through block: %s (%.3f > %.3f): %.3f%+.3f", new Object[] { RayTraceCandidate.findBlockRayTraceResult(rayTraceCandidate).getBlockVector().toLocation(this.getPlayer().getWorld()).getBlock().getType(), RayTraceCandidate.calculateValueWithDistance(rayTraceCandidate), RayTraceCandidate.calculateValueWithTolerance(rayTraceCandidate), this.hitboxBuffer.getLevel(), value3 });
|
||||
}
|
||||
this.hitboxBuffer.checkViolationBuffer(value3);
|
||||
if (this.attackTimer.tryAcquire()) {
|
||||
FeatureType feature2 = FeatureType.AAC_HITBOX_WALLS;
|
||||
this.recordFeature(feature2, value3);
|
||||
}
|
||||
} else {
|
||||
float value4 = RayTraceCandidate.calculateValue(rayTraceCandidate);
|
||||
DebugManager debugManager3 = this.getPlugin().getDebugManager();
|
||||
player = this.getPlayer();
|
||||
Player player5 = player;
|
||||
if (debugManager3.isDebugging(player5)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "hitbox: normal: %.3f: %.3f%+.3f", new Object[] { RayTraceCandidate.calculateValueWithDistance(rayTraceCandidate), this.hitboxBuffer.getLevel(), value4 });
|
||||
}
|
||||
this.hitboxBuffer.checkViolationBuffer(value4);
|
||||
if (this.attackTimer.tryAcquire()) {
|
||||
FeatureType feature3 = FeatureType.AAC_HITBOX_REACH;
|
||||
this.recordFeature(feature3, value4);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DebugManager debugManager4 = this.getPlugin().getDebugManager();
|
||||
player = this.getPlayer();
|
||||
Player player6 = player;
|
||||
if (debugManager4.isDebugging(player6)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "hitbox: no data for eid " + TeleportState.calculateCount(teleportState2) + " in " + Objects.requireNonNull(TeleportState.findLocationWithOrigin(teleportState2).getWorld()).getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import me.konsolas.aac.util.time.ExpiringCounter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class MouseDeltaCheck extends BaseCheck {
|
||||
|
||||
private final ExpiringCounter sampleTimer;
|
||||
|
||||
public MouseDeltaCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("aimbot"));
|
||||
this.sampleTimer = new ExpiringCounter();
|
||||
}
|
||||
|
||||
public void updateCount(int value, int value2, float value3, float value4, float value5) {
|
||||
float value6 = value5 * 0.6F + 0.2F;
|
||||
float value7 = value6 * value6 * value6 * 8.0F;
|
||||
float value8 = value * value7 * 0.15F;
|
||||
float value9 = value2 * value7 * 0.15F;
|
||||
float value10 = Math.abs(value3 - value8);
|
||||
float value11 = Math.abs(value4 - value9);
|
||||
if (value10 > 0.01 && this.sampleTimer.tryAcquire()) {
|
||||
FeatureType feature = FeatureType.AAC_AIMBOT_BAD_DELTA_X;
|
||||
this.recordFeature(feature, value10);
|
||||
}
|
||||
if (value11 > 0.01 && this.sampleTimer.tryAcquire()) {
|
||||
FeatureType feature2 = FeatureType.AAC_AIMBOT_BAD_DELTA_Y;
|
||||
this.recordFeature(feature2, value11);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NoSwingCheck extends BaseCheck {
|
||||
|
||||
private long lastSwing;
|
||||
|
||||
private long lastAttack;
|
||||
|
||||
public NoSwingCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("autoclicker"));
|
||||
}
|
||||
|
||||
public void tickWithLastSwing() {
|
||||
long now = System.currentTimeMillis();
|
||||
long value = now - this.lastSwing;
|
||||
this.lastSwing = System.currentTimeMillis();
|
||||
if (value < 25L) {
|
||||
this.lastAttack = now;
|
||||
}
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
long now = System.currentTimeMillis();
|
||||
long value = now - this.lastSwing;
|
||||
long value2 = now - this.lastAttack;
|
||||
if (value < 5L && value2 > 50L) {
|
||||
float value3 = (float) value / 1000.0F;
|
||||
FeatureType feature = FeatureType.AAC_CLICK_TIMING;
|
||||
this.recordFeature(feature, value3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PitchSpreadCheck extends BaseCheck {
|
||||
|
||||
private final float[] pitchSamples;
|
||||
|
||||
private int sampleIndex;
|
||||
|
||||
private float lastPitch;
|
||||
|
||||
private int sampleCount;
|
||||
|
||||
public PitchSpreadCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("aimbot"));
|
||||
this.pitchSamples = new float[20];
|
||||
this.sampleIndex = 0;
|
||||
this.lastPitch = 0.0F;
|
||||
}
|
||||
|
||||
public void updateValue(float value, float value2) {
|
||||
if (value2 > 0.0 && value > 0.0) {
|
||||
this.pitchSamples[this.sampleIndex++] = value;
|
||||
if (this.sampleIndex == this.pitchSamples.length) {
|
||||
float value3 = 0.0F;
|
||||
float value4 = 0.0F;
|
||||
for (float value5 : this.pitchSamples) {
|
||||
value3 += value5;
|
||||
value4 += value5 * value5;
|
||||
}
|
||||
float value6 = value3 / this.pitchSamples.length;
|
||||
float value7 = value4 / this.pitchSamples.length - value6 * value6;
|
||||
double value8 = Math.abs(value6 - this.lastPitch);
|
||||
if (!(value8 > 1.0) || !(value7 > 36.0)) {
|
||||
this.sampleCount = Math.max(this.sampleCount - 1, 0);
|
||||
} else if ((this.sampleCount += 4) > 20) {
|
||||
FeatureType feature = FeatureType.AAC_AIMBOT_PITCH_SPREAD;
|
||||
this.recordFeature(feature, value7);
|
||||
}
|
||||
this.sampleIndex = 0;
|
||||
this.lastPitch = value6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
|
||||
class RayTraceCandidate {
|
||||
|
||||
private final double distance;
|
||||
|
||||
private final double tolerance;
|
||||
|
||||
private final BlockRayTraceResult result;
|
||||
|
||||
RayTraceCandidate(double distance, double tolerance, BlockRayTraceResult result) {
|
||||
this.distance = distance;
|
||||
this.tolerance = tolerance;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
private float calculateValue() {
|
||||
if (!Double.isFinite(this.distance)) {
|
||||
return 1.0F;
|
||||
} else {
|
||||
return this.distance > this.tolerance ? (float) (this.distance - this.tolerance) + 1.0F : (float) this.distance - 3.0F;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkRayTraceCandidate(RayTraceCandidate rayTraceCandidate) {
|
||||
if (rayTraceCandidate == null) {
|
||||
return true;
|
||||
} else if (Double.isFinite(this.distance) && !Double.isFinite(rayTraceCandidate.distance)) {
|
||||
return true;
|
||||
} else {
|
||||
return this.distance <= this.tolerance && rayTraceCandidate.distance > rayTraceCandidate.tolerance ? true : this.calculateValue() < rayTraceCandidate.calculateValue();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean checkRayTraceCandidate(RayTraceCandidate rayTraceCandidate, RayTraceCandidate rayTraceCandidate2) {
|
||||
return rayTraceCandidate.checkRayTraceCandidate(rayTraceCandidate2);
|
||||
}
|
||||
|
||||
static double calculateValueWithDistance(RayTraceCandidate rayTraceCandidate) {
|
||||
return rayTraceCandidate.distance;
|
||||
}
|
||||
|
||||
static float calculateValue(RayTraceCandidate rayTraceCandidate) {
|
||||
return rayTraceCandidate.calculateValue();
|
||||
}
|
||||
|
||||
static double calculateValueWithTolerance(RayTraceCandidate rayTraceCandidate) {
|
||||
return rayTraceCandidate.tolerance;
|
||||
}
|
||||
|
||||
static BlockRayTraceResult findBlockRayTraceResult(RayTraceCandidate rayTraceCandidate) {
|
||||
return rayTraceCandidate.result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class SensitivityCheck extends BaseCheck {
|
||||
|
||||
private boolean previousSnap;
|
||||
|
||||
public SensitivityCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("aimbot"));
|
||||
}
|
||||
|
||||
public boolean checkHeadSnap(int value, int value2) {
|
||||
if (value >= 0 && value <= 200) {
|
||||
if (value2 >= 0 && value2 <= 200) {
|
||||
if (value != value2) {
|
||||
if (this.previousSnap) {
|
||||
FeatureType feature = FeatureType.AAC_AIMBOT_SENS_MISMATCH;
|
||||
float value3 = Math.abs(value - value2);
|
||||
FeatureType feature2 = feature;
|
||||
this.recordFeature(feature2, value3);
|
||||
}
|
||||
this.previousSnap = true;
|
||||
return false;
|
||||
} else {
|
||||
this.previousSnap = false;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (this.previousSnap) {
|
||||
float value4 = value2;
|
||||
FeatureType feature3 = FeatureType.AAC_AIMBOT_SENS_Y_OOR;
|
||||
this.recordFeature(feature3, value4);
|
||||
}
|
||||
this.previousSnap = true;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (this.previousSnap) {
|
||||
float value5 = value;
|
||||
FeatureType feature4 = FeatureType.AAC_AIMBOT_SENS_X_OOR;
|
||||
this.recordFeature(feature4, value5);
|
||||
}
|
||||
this.previousSnap = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package me.konsolas.aac.checks.combat;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
class TeleportState {
|
||||
|
||||
private final int teleportId;
|
||||
|
||||
private final Location origin;
|
||||
|
||||
private Location destination;
|
||||
|
||||
private boolean confirmed;
|
||||
|
||||
TeleportState(int teleportId, Location origin) {
|
||||
this.teleportId = teleportId;
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
private void updateLocation(Location location) {
|
||||
if (this.destination == null) {
|
||||
this.destination = location;
|
||||
}
|
||||
}
|
||||
|
||||
static void updateTeleportState(TeleportState teleportState, Location location) {
|
||||
teleportState.updateLocation(location);
|
||||
}
|
||||
|
||||
static Location findLocation(TeleportState teleportState) {
|
||||
return teleportState.destination;
|
||||
}
|
||||
|
||||
static boolean checkTeleportState(TeleportState teleportState) {
|
||||
return teleportState.confirmed;
|
||||
}
|
||||
|
||||
static boolean checkTeleportState(TeleportState teleportState, boolean matches) {
|
||||
return teleportState.confirmed = matches;
|
||||
}
|
||||
|
||||
static Location findLocationWithOrigin(TeleportState teleportState) {
|
||||
return teleportState.origin;
|
||||
}
|
||||
|
||||
static int calculateCount(TeleportState teleportState) {
|
||||
return teleportState.teleportId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package me.konsolas.aac.checks.interaction;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import me.konsolas.aac.checks.ViolationBuffer;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.NativeAccessProvider;
|
||||
import me.konsolas.aac.debug.DebugManager;
|
||||
import me.konsolas.aac.movement.PlayerPose;
|
||||
import me.konsolas.aac.protocol.PacketReader;
|
||||
import me.konsolas.aac.util.collection.FixedSizeList;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import me.konsolas.aac.world.MaterialAlias;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.util.BlockVector;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class InteractCheck extends BaseCheck {
|
||||
|
||||
private final ViolationBuffer interactionBuffer;
|
||||
|
||||
private final double maximumAngleDifference;
|
||||
|
||||
private final Set exemptMaterials;
|
||||
|
||||
private final FixedSizeList interactionHistory;
|
||||
|
||||
private static final double[] angleSamples = new double[] { 0.0, 0.5, 1.0 };
|
||||
|
||||
private static final Vector[] lookDirections;
|
||||
|
||||
private static final Set interactedBlocks;
|
||||
|
||||
public InteractCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("interact"));
|
||||
this.exemptMaterials = EnumSet.noneOf(Material.class);
|
||||
this.interactionHistory = new FixedSizeList(5);
|
||||
ConfigurationSection configuration = this.getConfiguration();
|
||||
ConfigurationSection section = configuration;
|
||||
this.interactionBuffer = ViolationBuffer.create(section, (String) "vl");
|
||||
this.maximumAngleDifference = this.getConfiguration().getDouble("max_angle_diff");
|
||||
for (String text : this.getConfiguration().getStringList("exempt")) {
|
||||
Material material = Material.matchMaterial(text);
|
||||
this.exemptMaterials.add(material);
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePacket(PacketContainer packet) {
|
||||
Location location = (Location) this.interactionHistory.latest();
|
||||
if (location == null || location.getWorld() != this.getPlayer().getWorld()) {
|
||||
if (location != null) {
|
||||
this.interactionHistory.advance();
|
||||
}
|
||||
location = this.getPlayer().getLocation();
|
||||
}
|
||||
Location location2 = PacketReader.findLocation(packet, location);
|
||||
if (this.getPlayer().getVehicle() != null) {
|
||||
Location location3 = this.getPlayer().getLocation();
|
||||
location2.setX(location3.getX());
|
||||
location2.setY(location3.getY());
|
||||
location2.setZ(location3.getZ());
|
||||
}
|
||||
this.interactionHistory.add(location2);
|
||||
}
|
||||
|
||||
public boolean checkInteract(Block block) {
|
||||
if (!this.exemptMaterials.contains(block.getType())) {
|
||||
if (this.checkInteract(block, (BlockFace) (Object) null)) {
|
||||
if (this.interactionBuffer.checkViolationBuffer((float) 1.0F)) {
|
||||
FeatureType feature = FeatureType.AAC_INTERACT_BREAK;
|
||||
this.recordFeature(feature, (float) 1.0F);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onPlayerInteractEvent(PlayerInteractEvent playerInteractEvent) {
|
||||
if (playerInteractEvent.getAction() == Action.RIGHT_CLICK_BLOCK && playerInteractEvent.getClickedBlock() != null && !this.exemptMaterials.contains(playerInteractEvent.getClickedBlock().getType())) {
|
||||
if (playerInteractEvent.getClickedBlock().isLiquid() && playerInteractEvent.getItem() != null && interactedBlocks.contains(playerInteractEvent.getItem().getType())) {
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "interact: " + playerInteractEvent.getItem().getType() + " on " + playerInteractEvent.getClickedBlock().getType() + ": allowed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
BlockFace blockFace = playerInteractEvent.getBlockFace();
|
||||
Block clickedBlock = playerInteractEvent.getClickedBlock();
|
||||
if (this.checkInteract(clickedBlock, blockFace)) {
|
||||
if (this.interactionBuffer.checkViolationBuffer((float) 1.0F)) {
|
||||
FeatureType feature = playerInteractEvent.isBlockInHand() ? FeatureType.AAC_INTERACT_PLACE : FeatureType.AAC_INTERACT_GEN;
|
||||
this.recordFeature(feature, (float) 1.0F);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static float calculateValue(Vector vector, Vector vector2) {
|
||||
Vector vector3 = vector.clone().setY(0);
|
||||
Vector vector4 = vector2.clone().setY(0);
|
||||
double value = vector3.dot(vector4) / (vector3.length() * vector4.length());
|
||||
value = Math.min(Math.max(value, -1.0), 1.0);
|
||||
double value2 = Math.acos(value);
|
||||
value = Math.sqrt(vector.getX() * vector.getX() + vector.getZ() * vector.getZ());
|
||||
double value3 = Math.sqrt(vector2.getX() * vector2.getX() + vector2.getZ() * vector2.getZ());
|
||||
double value4 = Math.abs(Math.atan(-vector.getY() / value) - Math.atan(-vector2.getY() / value3));
|
||||
return (float) Math.sqrt(value2 * value2 + value4 * value4);
|
||||
}
|
||||
|
||||
private boolean checkInteract(Block block, BlockFace blockFace) {
|
||||
Location location = block.getLocation();
|
||||
BlockVector blockVector = location.toVector().toBlockVector();
|
||||
for (PlayerPose playerPose : PlayerPose.POSES) {
|
||||
AAC plugin = this.getPlugin();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
Vector vector = new Vector(0.0F, playerPose.calculateValue(plugin.getProtocolVersion(player2)), 0.0F);
|
||||
if (!this.checkInteract(block, blockFace, blockVector, vector)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkInteract(Block block, BlockFace blockFace, BlockVector blockVector, Vector vector) {
|
||||
if (this.interactionHistory.isEmpty()) {
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "interact: warn: no pos history: " + block.getType() + ":" + blockFace + " at " + blockVector);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
Collection<Vector> values = this.findCollection(blockFace, blockVector, vector);
|
||||
if (values.isEmpty()) {
|
||||
DebugManager debugManager2 = this.getPlugin().getDebugManager();
|
||||
Player player3 = this.getPlayer();
|
||||
Player player4 = player3;
|
||||
if (debugManager2.isDebugging(player4)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "interact: occluded:" + block.getType() + ":" + blockFace + " at " + blockVector);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
float value = Float.POSITIVE_INFINITY;
|
||||
for (Vector vector2 : values) {
|
||||
for (Location location : (Iterable<Location>) (Iterable<?>) (this.interactionHistory)) {
|
||||
if (location != null) {
|
||||
float value2 = calculateValue(location.getDirection(), vector2);
|
||||
if (value2 < value) {
|
||||
value = value2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DebugManager debugManager3 = this.getPlugin().getDebugManager();
|
||||
Player player5 = this.getPlayer();
|
||||
Player player6 = player5;
|
||||
if (debugManager3.isDebugging(player6)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "interact: angle: %.3f rad " + block.getType() + ":" + blockFace + " at " + blockVector, new Object[] { value });
|
||||
}
|
||||
return value > this.maximumAngleDifference;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Collection findCollection(BlockFace blockFace, BlockVector blockVector, Vector vector) {
|
||||
ArrayList values = new ArrayList();
|
||||
for (Location location : (Iterable<Location>) (Iterable<?>) (this.interactionHistory)) {
|
||||
if (location != null) {
|
||||
Vector vector2 = location.toVector().add(vector);
|
||||
for (Location location2 : (Iterable<Location>) (Iterable<?>) (this.interactionHistory)) {
|
||||
if (location2 != null) {
|
||||
Vector direction = location2.getDirection();
|
||||
NativeAccess instance = NativeAccessProvider.getInstance();
|
||||
Player player = this.getPlayer();
|
||||
BlockRayTraceResult blockRayTraceResult = instance.rayTrace(player, vector2, direction, 5.0);
|
||||
if (blockRayTraceResult.isBlockRayTraceResult() && blockRayTraceResult.getBlockVector().equals(blockVector) && (blockFace == null || blockRayTraceResult.getFace() == blockFace)) {
|
||||
values.add(direction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!values.isEmpty()) {
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player2 = this.getPlayer();
|
||||
Player player3 = player2;
|
||||
if (debugManager.isDebugging(player3)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "interact: exact: " + values.iterator().next());
|
||||
}
|
||||
return values;
|
||||
} else {
|
||||
for (Location location3 : (Iterable<Location>) (Iterable<?>) (this.interactionHistory)) {
|
||||
if (location3 != null) {
|
||||
Vector vector3 = location3.toVector().add(vector);
|
||||
for (Vector vector4 : lookDirections) {
|
||||
Vector vector5 = vector4.clone().add(blockVector).subtract(vector3);
|
||||
if (vector5.lengthSquared() > 0.0) {
|
||||
NativeAccess instance2 = NativeAccessProvider.getInstance();
|
||||
Player player4 = this.getPlayer();
|
||||
BlockRayTraceResult blockRayTraceResult2 = instance2.rayTrace(player4, vector3, vector5, 5.0);
|
||||
if (blockRayTraceResult2.isBlockRayTraceResult() && blockRayTraceResult2.getBlockVector().equals(blockVector) && (blockFace == null || blockRayTraceResult2.getFace() == blockFace)) {
|
||||
values.add(vector5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DebugManager debugManager2 = this.getPlugin().getDebugManager();
|
||||
Player player5 = this.getPlayer();
|
||||
Player player6 = player5;
|
||||
if (debugManager2.isDebugging(player6)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "interact: approx: " + values.size());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
ArrayList<Vector> values2 = new ArrayList();
|
||||
for (double value : angleSamples) {
|
||||
for (double value2 : angleSamples) {
|
||||
for (double value3 : angleSamples) {
|
||||
values2.add(new Vector(value, value2, value3));
|
||||
}
|
||||
}
|
||||
}
|
||||
lookDirections = values2.toArray(new Vector[0]);
|
||||
interactedBlocks = EnumSet.of(MaterialAlias.LILY_PAD.getMaterial());
|
||||
for (Material material : Material.values()) {
|
||||
if (material.name().contains("BOAT")) {
|
||||
interactedBlocks.add(material);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package me.konsolas.aac.checks.interaction;
|
||||
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerDigType;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public interface InteractionTracker {
|
||||
|
||||
default void updatePlayer(Player player, Location location) {
|
||||
}
|
||||
|
||||
default void computeTick() {
|
||||
}
|
||||
|
||||
default void tick() {
|
||||
}
|
||||
|
||||
default void updateEnumWrappersPlayerDigType(PlayerDigType playerDigType, BlockPosition blockPosition) {
|
||||
}
|
||||
|
||||
default void findTick() {
|
||||
}
|
||||
|
||||
default void updateBlockPosition(BlockPosition blockPosition, BlockPosition blockPosition2, Hand hand) {
|
||||
}
|
||||
|
||||
default void processTick() {
|
||||
}
|
||||
|
||||
default void collectTick() {
|
||||
}
|
||||
|
||||
default void inspectTick() {
|
||||
}
|
||||
|
||||
default void updateVector(Vector vector) {
|
||||
}
|
||||
|
||||
default void updateAxisAlignedBoundingBox(AxisAlignedBoundingBox box) {
|
||||
}
|
||||
|
||||
default void readTick() {
|
||||
}
|
||||
|
||||
default void measureTick() {
|
||||
}
|
||||
|
||||
default void evaluateTick() {
|
||||
}
|
||||
|
||||
default void updateBlock(Block block) {
|
||||
}
|
||||
|
||||
default void updateAction(Action action, Block block, BlockFace blockFace) {
|
||||
}
|
||||
|
||||
default void resolveTick() {
|
||||
}
|
||||
|
||||
default void updateMaterial(Material material, double value) {
|
||||
}
|
||||
|
||||
default void deriveTick() {
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,313 @@
|
||||
package me.konsolas.aac.checks.player;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import me.konsolas.aac.checks.ViolationBuffer;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.NativeAccessProvider;
|
||||
import me.konsolas.aac.debug.DebugManager;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.world.MaterialAlias;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class ActionDelayCheck extends BaseCheck {
|
||||
|
||||
private static final ItemStack lastItem = MaterialAlias.BONE_MEAL.getItemStack();
|
||||
|
||||
private final ViolationBuffer delayBuffer;
|
||||
|
||||
private final boolean checkFastUse;
|
||||
|
||||
private final boolean checkFastBow;
|
||||
|
||||
private final boolean checkFastPlace;
|
||||
|
||||
private final boolean checkFastBreak;
|
||||
|
||||
private final boolean checkRegeneration;
|
||||
|
||||
private final boolean checkFastSneak;
|
||||
|
||||
private final boolean checkRelease;
|
||||
|
||||
private final boolean usingItem;
|
||||
|
||||
private long lastPlace;
|
||||
|
||||
private long lastBreak;
|
||||
|
||||
private long lastUse;
|
||||
|
||||
private long lastBow;
|
||||
|
||||
private long lastHeal;
|
||||
|
||||
private long lastSneak;
|
||||
|
||||
private long lastRelease;
|
||||
|
||||
public ActionDelayCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("delays"));
|
||||
ConfigurationSection configuration = this.getConfiguration();
|
||||
ConfigurationSection section = configuration;
|
||||
this.delayBuffer = ViolationBuffer.create(section, (String) "vl");
|
||||
this.checkFastUse = this.getConfiguration().getBoolean("fast_break");
|
||||
this.checkFastBow = this.getConfiguration().getBoolean("fast_place");
|
||||
this.checkFastPlace = this.getConfiguration().getBoolean("fast_use");
|
||||
this.checkFastBreak = this.getConfiguration().getBoolean("fast_bow");
|
||||
this.checkRegeneration = this.getConfiguration().getBoolean("regen");
|
||||
this.checkFastSneak = this.getConfiguration().getBoolean("fast_sneak");
|
||||
this.checkRelease = this.getConfiguration().getBoolean("fast_release");
|
||||
this.usingItem = this.getConfiguration().getBoolean("break_delay");
|
||||
}
|
||||
|
||||
public boolean checkActionDelay(boolean matches) {
|
||||
if (!matches) {
|
||||
this.lastPlace = System.currentTimeMillis();
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "delays: damaged block");
|
||||
}
|
||||
} else {
|
||||
this.lastPlace = 0L;
|
||||
DebugManager debugManager2 = this.getPlugin().getDebugManager();
|
||||
Player player3 = this.getPlayer();
|
||||
Player player4 = player3;
|
||||
if (debugManager2.isDebugging(player4)) {
|
||||
this.getPlugin().getDebugManager().log(this.getPlayer(), "delays: damaged block (instabreak)");
|
||||
}
|
||||
}
|
||||
if (this.usingItem) {
|
||||
long value = System.currentTimeMillis() - this.lastBreak;
|
||||
if (value < 500L) {
|
||||
float value2 = matches ? 0.05F : 0.1F;
|
||||
float value3 = value2 - (float) value / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value4 = value3 / value2;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_BREAKDELAY;
|
||||
this.recordFeature(feature, value4);
|
||||
}
|
||||
DebugManager debugManager3 = this.getPlugin().getDebugManager();
|
||||
Player player5 = this.getPlayer();
|
||||
Player player6 = player5;
|
||||
if (debugManager3.isDebugging(player6)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: breakdelay: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value3 });
|
||||
}
|
||||
return this.delayBuffer.checkViolationBuffer(value3);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean checkActionDelay(Block block) {
|
||||
if (this.checkFastUse && this.lastPlace != 0L) {
|
||||
this.lastBreak = System.currentTimeMillis();
|
||||
long value = this.lastBreak - this.lastPlace;
|
||||
NativeAccess instance = NativeAccessProvider.getInstance();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
double blockDamage = instance.getBlockDamage(player2, block);
|
||||
if (blockDamage >= 1.0) {
|
||||
return false;
|
||||
} else {
|
||||
float value2 = (float) (1.0 / blockDamage * 0.05F);
|
||||
float value3 = value2 - (float) value / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value4 = value3 / value2;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_BREAK;
|
||||
this.recordFeature(feature, value4);
|
||||
}
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
player = this.getPlayer();
|
||||
Player player3 = player;
|
||||
if (debugManager.isDebugging(player3)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: fastbreak: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value3 });
|
||||
}
|
||||
return this.delayBuffer.checkViolationBuffer(value3);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActionDelay() {
|
||||
if (!this.checkFastBow) {
|
||||
return false;
|
||||
} else {
|
||||
for (ItemStack itemStack : NativeAccessProvider.getInstance().getHandItems(this.getPlayer())) {
|
||||
if (itemStack != null && (itemStack.getType() == Material.FLINT_AND_STEEL || itemStack.isSimilar(lastItem))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
long value = this.lastUse;
|
||||
long value2 = now - value;
|
||||
this.lastUse = now;
|
||||
if (value == 0L) {
|
||||
return false;
|
||||
} else {
|
||||
float value3 = 0.05F - (float) value2 / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value4 = value3 / 0.05F;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_PLACE;
|
||||
this.recordFeature(feature, value4);
|
||||
}
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: fastplace: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value3 });
|
||||
}
|
||||
return this.delayBuffer.checkViolationBuffer(value3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onAction(Action action) {
|
||||
if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) {
|
||||
this.lastBow = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkActionDelay(Material material) {
|
||||
if (!this.checkFastPlace) {
|
||||
return false;
|
||||
} else {
|
||||
long value = System.currentTimeMillis() - this.lastBow;
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_13;
|
||||
float value2 = serverVersion.isAtLeast(version) && material == Material.DRIED_KELP ? 0.75F : 1.5F;
|
||||
float value3 = value2 - (float) value / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value4 = value3 / value2;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_CONSUME;
|
||||
this.recordFeature(feature, value4);
|
||||
}
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: fastuse: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value3 });
|
||||
}
|
||||
return this.delayBuffer.checkViolationBuffer(value3);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkActionDelay(float value) {
|
||||
if (this.checkFastBreak) {
|
||||
long value2 = System.currentTimeMillis() - this.lastBow;
|
||||
float value3 = calculateValue(value);
|
||||
float value4 = value3 - (float) value2 / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value5 = value4 / value3;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_BOW;
|
||||
this.recordFeature(feature, value5);
|
||||
}
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: fastbow: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value4 });
|
||||
}
|
||||
return this.delayBuffer.checkViolationBuffer(value4);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static float calculateValue(float value) {
|
||||
return 0.1F + 0.95F * value;
|
||||
}
|
||||
|
||||
public boolean onEntityRegainHealthEventRegainReason(RegainReason regainReason) {
|
||||
if (this.checkRegeneration && regainReason == RegainReason.SATIATED) {
|
||||
long now = System.currentTimeMillis();
|
||||
long value = this.lastHeal;
|
||||
long value2 = now - value;
|
||||
this.lastHeal = now;
|
||||
if (value == 0L) {
|
||||
return false;
|
||||
} else {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_9;
|
||||
float value3 = serverVersion.isAtLeast(version) && this.getPlayer().getFoodLevel() == 20 ? 0.5F : 4.0F;
|
||||
float value4 = value3 - (float) value2 / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value5 = value4 / value3;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_REGEN;
|
||||
this.recordFeature(feature, value5);
|
||||
}
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: regen: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value4 });
|
||||
}
|
||||
return this.delayBuffer.checkViolationBuffer(value4);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void tickWithCheckFastSneak() {
|
||||
if (this.checkFastSneak) {
|
||||
long now = System.currentTimeMillis();
|
||||
long value = this.lastSneak;
|
||||
long value2 = now - value;
|
||||
this.lastSneak = now;
|
||||
if (value == 0L) {
|
||||
return;
|
||||
}
|
||||
float value3 = 0.1F;
|
||||
float value4 = value3 - (float) value2 / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value5 = value4 / value3;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_UNSNEAK;
|
||||
this.recordFeature(feature, value5);
|
||||
}
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: sneak: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value4 });
|
||||
}
|
||||
this.delayBuffer.checkViolationBuffer(value4);
|
||||
}
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (this.checkRelease) {
|
||||
long now = System.currentTimeMillis();
|
||||
long value = this.lastRelease;
|
||||
long value2 = now - value;
|
||||
this.lastRelease = now;
|
||||
if (value == 0L) {
|
||||
return;
|
||||
}
|
||||
float value3 = 0.1F;
|
||||
float value4 = value3 - (float) value2 / 1000.0F;
|
||||
if (this.delayBuffer.getLevel() > 0.5F) {
|
||||
float value5 = value4 / value3;
|
||||
FeatureType feature = FeatureType.AAC_DELAYS_RELEASE;
|
||||
this.recordFeature(feature, value5);
|
||||
}
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
Player player = this.getPlayer();
|
||||
Player player2 = player;
|
||||
if (debugManager.isDebugging(player2)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(this.getPlayer(), "delays: block: %.3f%+.3f", new Object[] { this.delayBuffer.getLevel(), value4 });
|
||||
}
|
||||
this.delayBuffer.checkViolationBuffer(value4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package me.konsolas.aac.checks.player;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ActionStateTracker extends BaseCheck {
|
||||
|
||||
private boolean movedThisSecond;
|
||||
|
||||
private boolean attackedThisSecond;
|
||||
|
||||
private boolean interactedThisSecond;
|
||||
|
||||
private int movementSamples;
|
||||
|
||||
private int attackSamples;
|
||||
|
||||
private int interactionSamples;
|
||||
|
||||
public ActionStateTracker(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, (String) (null));
|
||||
this.movedThisSecond = false;
|
||||
this.attackedThisSecond = false;
|
||||
this.interactedThisSecond = false;
|
||||
}
|
||||
|
||||
public void updateTime(long value) {
|
||||
if (value % 20L == 0L) {
|
||||
if (this.movedThisSecond) {
|
||||
this.movementSamples++;
|
||||
}
|
||||
if (this.attackedThisSecond) {
|
||||
this.attackSamples++;
|
||||
}
|
||||
if (this.interactedThisSecond) {
|
||||
this.interactionSamples++;
|
||||
}
|
||||
this.movedThisSecond = false;
|
||||
this.attackedThisSecond = false;
|
||||
this.interactedThisSecond = false;
|
||||
}
|
||||
if (value % 200L == 0L) {
|
||||
if (this.movementSamples > 0) {
|
||||
float value2 = this.movementSamples * 20.0F / 1200.0F;
|
||||
FeatureType feature = FeatureType.AAC_NORM_MOVE;
|
||||
this.recordFeature(feature, value2);
|
||||
this.movementSamples = 0;
|
||||
}
|
||||
if (this.attackSamples > 0) {
|
||||
float value3 = this.attackSamples * 20.0F / 1200.0F;
|
||||
FeatureType feature2 = FeatureType.AAC_NORM_ATTACK;
|
||||
this.recordFeature(feature2, value3);
|
||||
this.attackSamples = 0;
|
||||
}
|
||||
if (this.interactionSamples > 0) {
|
||||
float value4 = this.interactionSamples * 20.0F / 1200.0F;
|
||||
FeatureType feature3 = FeatureType.AAC_NORM_INTERACT;
|
||||
this.recordFeature(feature3, value4);
|
||||
this.interactionSamples = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void recordMovement() {
|
||||
this.movedThisSecond = true;
|
||||
}
|
||||
|
||||
public void recordAttack() {
|
||||
this.attackedThisSecond = true;
|
||||
}
|
||||
|
||||
public void recordInteraction() {
|
||||
this.interactedThisSecond = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package me.konsolas.aac.checks.player;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.checks.BaseCheck;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class MiscellaneousCheck extends BaseCheck {
|
||||
|
||||
private long lastRotation;
|
||||
|
||||
private float previousYaw;
|
||||
|
||||
private float previousPitch;
|
||||
|
||||
private final boolean checkAbilities;
|
||||
|
||||
private final boolean checkPitch;
|
||||
|
||||
private final boolean checkRotationRate;
|
||||
|
||||
public MiscellaneousCheck(AAC aacPlugin, Player player) {
|
||||
super(aacPlugin, player, ("misc"));
|
||||
this.checkAbilities = this.getConfiguration().getBoolean("invalid_pitch");
|
||||
this.checkPitch = this.getConfiguration().getBoolean("player_abilities");
|
||||
this.checkRotationRate = this.getConfiguration().getBoolean("rotation_rate");
|
||||
}
|
||||
|
||||
public void updateTime(long value) {
|
||||
if (this.getPlayer().getAllowFlight()) {
|
||||
this.lastRotation = value;
|
||||
} else if (this.checkPitch && value > 100L && value - this.lastRotation > 100L) {
|
||||
FeatureType feature = FeatureType.AAC_MISC_ABILITIES;
|
||||
this.recordFeature(feature, (float) 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTime(long value, float value2, float value3) {
|
||||
float value4 = value2 - this.previousYaw;
|
||||
float value5 = value3 - this.previousPitch;
|
||||
this.previousYaw = value2;
|
||||
this.previousPitch = value3;
|
||||
if (this.checkRotationRate && (value4 > 1.0E8F || value5 > 1.0E8F)) {
|
||||
FeatureType feature = FeatureType.AAC_MISC_ROTATION;
|
||||
float value6 = Math.max(value4, value5);
|
||||
FeatureType feature2 = feature;
|
||||
this.recordFeature(feature2, value6);
|
||||
}
|
||||
if (this.checkAbilities && value > 100L && (value2 > 90.1F || value2 < -90.1F)) {
|
||||
FeatureType feature3 = FeatureType.AAC_MISC_PITCH;
|
||||
this.recordFeature(feature3, value2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
package me.konsolas.aac.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.PlayerAnalysisReport;
|
||||
import me.konsolas.aac.analysis.RiskLevel;
|
||||
import me.konsolas.aac.api.AACExemption;
|
||||
import me.konsolas.aac.util.chat.ChatMessages;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent.Action;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
import org.bukkit.util.StringUtil;
|
||||
|
||||
public class AACCommandExecutor implements TabExecutor {
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
private final ChatColor ACCENT_COLOR;
|
||||
|
||||
private final Map<CommandSender, MitigationMode> alertModes = new WeakHashMap();
|
||||
|
||||
private final Map spectateLocations = new WeakHashMap();
|
||||
|
||||
private static final List SUBCOMMANDS = Arrays.asList("help", "status", "exempt", "spec", "spectate", "check", "reset", "debug", "reload");
|
||||
|
||||
private static final List ALERT_MODES = Arrays.asList("normal", "reduced", "off");
|
||||
|
||||
public AACCommandExecutor(AAC plugin) {
|
||||
this.plugin = plugin;
|
||||
String string = plugin.getConfig().getString("aac_prefix_colour");
|
||||
if (string != null && string.length() == 1 && "0123456789abcdef".contains(string)) {
|
||||
this.ACCENT_COLOR = ChatColor.getByChar(string.charAt(0));
|
||||
} else {
|
||||
plugin.getLogger().warning("Invalid colour code " + string + ", defaulting to 'a'");
|
||||
this.ACCENT_COLOR = ChatColor.GREEN;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onCommand(CommandSender sender, Command command, String text, String[] texts) {
|
||||
if (texts.length == 0) {
|
||||
BaseComponent baseComponent = this.getBaseComponent();
|
||||
baseComponent.addExtra("AAC " + AAC.class.getPackage().getImplementationVersion() + " - ");
|
||||
TextComponent textComponent = new TextComponent("https://www.spigotmc.org/resources/6442/");
|
||||
textComponent.setClickEvent(new ClickEvent(Action.OPEN_URL, "https://www.spigotmc.org/resources/6442/"));
|
||||
baseComponent.addExtra(textComponent);
|
||||
ChatMessages.send(sender, baseComponent);
|
||||
return false;
|
||||
} else {
|
||||
String text2 = texts[0].toLowerCase();
|
||||
switch(text2) {
|
||||
case "help":
|
||||
if (sender.hasPermission("aac.status")) {
|
||||
BaseComponent baseComponent2 = this.getBaseComponent();
|
||||
baseComponent2.addExtra("/aac status - list online players");
|
||||
ChatMessages.send(sender, baseComponent2);
|
||||
BaseComponent baseComponent3 = this.getBaseComponent();
|
||||
baseComponent3.addExtra("/aac status <player> - view latest analysis for <player>");
|
||||
ChatMessages.send(sender, baseComponent3);
|
||||
}
|
||||
if (sender.hasPermission("aac.exempt")) {
|
||||
BaseComponent baseComponent4 = this.getBaseComponent();
|
||||
baseComponent4.addExtra("/aac exempt <player> - exempt/unexempt <player> from AAC checking");
|
||||
ChatMessages.send(sender, baseComponent4);
|
||||
}
|
||||
if (sender.hasPermission("aac.spectate")) {
|
||||
BaseComponent baseComponent5 = this.getBaseComponent();
|
||||
baseComponent5.addExtra("/aac spectate <player> - switch modes and teleport to <player>");
|
||||
ChatMessages.send(sender, baseComponent5);
|
||||
BaseComponent baseComponent6 = this.getBaseComponent();
|
||||
baseComponent6.addExtra("/aac spectate - teleport back after spectating a player");
|
||||
ChatMessages.send(sender, baseComponent6);
|
||||
}
|
||||
if (sender.hasPermission("aac.sudo")) {
|
||||
BaseComponent baseComponent7 = this.getBaseComponent();
|
||||
baseComponent7.addExtra("/aac sudo <player> <command> - force <player> to execute <command>");
|
||||
ChatMessages.send(sender, baseComponent7);
|
||||
}
|
||||
if (sender.hasPermission("aac.check")) {
|
||||
BaseComponent baseComponent8 = this.getBaseComponent();
|
||||
baseComponent8.addExtra("/aac check <player> - force AAC to analyse <player>");
|
||||
ChatMessages.send(sender, baseComponent8);
|
||||
}
|
||||
if (sender.hasPermission("aac.reset")) {
|
||||
BaseComponent baseComponent9 = this.getBaseComponent();
|
||||
baseComponent9.addExtra("/aac reset <player> - clear AAC's database entries for <player>");
|
||||
ChatMessages.send(sender, baseComponent9);
|
||||
}
|
||||
if (sender.hasPermission("aac.alert")) {
|
||||
BaseComponent baseComponent10 = this.getBaseComponent();
|
||||
baseComponent10.addExtra("/aac alerts <off/normal/reduced> - set your alert level (reduced = only severe alerts)");
|
||||
ChatMessages.send(sender, baseComponent10);
|
||||
BaseComponent baseComponent11 = this.getBaseComponent();
|
||||
baseComponent11.addExtra("/aac alerts - check your current alert level");
|
||||
ChatMessages.send(sender, baseComponent11);
|
||||
}
|
||||
if (sender.hasPermission("aac.debug")) {
|
||||
BaseComponent baseComponent12 = this.getBaseComponent();
|
||||
baseComponent12.addExtra("/aac debug [players...] - toggle your debug output for [players...]");
|
||||
ChatMessages.send(sender, baseComponent12);
|
||||
}
|
||||
if (sender.hasPermission("aac.reload")) {
|
||||
BaseComponent baseComponent13 = this.getBaseComponent();
|
||||
baseComponent13.addExtra("/aac reload - reload AAC's checks, API hooks, and configuration file");
|
||||
ChatMessages.send(sender, baseComponent13);
|
||||
}
|
||||
return true;
|
||||
case "status":
|
||||
if (!sender.hasPermission("aac.status")) {
|
||||
sender.sendMessage("/aac status missing permission node aac.status");
|
||||
return false;
|
||||
}
|
||||
if (texts.length > 1) {
|
||||
for (int index = 1; index < texts.length; index++) {
|
||||
OfflinePlayer offlinePlayer = this.plugin.getServer().getOfflinePlayer(texts[index]);
|
||||
CompletableFuture<PlayerAnalysisReport> completableFuture;
|
||||
if (this.plugin.getAnalysisManager().getReport(offlinePlayer) == null) {
|
||||
completableFuture = this.plugin.getAnalysisManager().analyzeAsync(offlinePlayer);
|
||||
} else {
|
||||
completableFuture = CompletableFuture.completedFuture(this.plugin.getAnalysisManager().getReport(offlinePlayer));
|
||||
}
|
||||
completableFuture.whenComplete((result, result2) -> {
|
||||
this.updateCommandSender(sender, offlinePlayer);
|
||||
if (!offlinePlayer.isOnline() || !this.plugin.getPacketDispatcher().getExemptions((Player) offlinePlayer).isEmpty()) {
|
||||
TextComponent textComponent2 = new TextComponent(" This player is currently exempt from AAC checking");
|
||||
textComponent2.setColor(ChatColor.DARK_AQUA);
|
||||
ChatMessages.send(sender, textComponent2);
|
||||
if (offlinePlayer.isOnline()) {
|
||||
for (AACExemption aACExemption : this.plugin.getPacketDispatcher().getExemptions((Player) offlinePlayer)) {
|
||||
TextComponent textComponent3 = new TextComponent(" - ");
|
||||
TextComponent textComponent4 = new TextComponent(aACExemption.getReason());
|
||||
textComponent4.setColor(ChatColor.GRAY);
|
||||
textComponent3.addExtra(textComponent4);
|
||||
ChatMessages.send(sender, textComponent3);
|
||||
}
|
||||
} else {
|
||||
TextComponent textComponent5 = new TextComponent(" - ");
|
||||
TextComponent textComponent6 = new TextComponent("The player is currently offline");
|
||||
textComponent6.setColor(ChatColor.GRAY);
|
||||
textComponent5.addExtra(textComponent6);
|
||||
ChatMessages.send(sender, textComponent5);
|
||||
}
|
||||
}
|
||||
for (BaseComponent baseComponent16 : this.plugin.getAnalysisManager().getReport(offlinePlayer).toChatComponents()) {
|
||||
ChatMessages.send(sender, baseComponent16);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
BaseComponent baseComponent17 = this.getBaseComponent();
|
||||
ArrayList<Player> values = new ArrayList(Bukkit.getOnlinePlayers());
|
||||
values.sort(Comparator.<Player>comparingDouble(result3 -> this.plugin.getAnalysisManager().getReport(result3).getScore()).reversed());
|
||||
for (Player player : values) {
|
||||
baseComponent17.addExtra(this.findBaseComponent(player));
|
||||
baseComponent17.addExtra(" ");
|
||||
}
|
||||
ChatMessages.send(sender, baseComponent17);
|
||||
}
|
||||
return true;
|
||||
case "exempt":
|
||||
if (!sender.hasPermission("aac.exempt")) {
|
||||
sender.sendMessage("/aac exempt missing permission node aac.exempt");
|
||||
return false;
|
||||
} else if (texts.length <= 1) {
|
||||
sender.sendMessage("/aac exempt <player> [players...] missing player argument");
|
||||
return false;
|
||||
} else {
|
||||
for (int index2 = 1; index2 < texts.length; index2++) {
|
||||
Player player2 = this.plugin.getServer().getPlayer(texts[index2]);
|
||||
if (player2 == null) {
|
||||
sender.sendMessage("/aac exempt unknown player " + texts[index2]);
|
||||
} else {
|
||||
BaseComponent baseComponent18 = this.getBaseComponent();
|
||||
if (this.plugin.toggleExemption(player2)) {
|
||||
baseComponent18.addExtra("Added command exemption to ");
|
||||
} else {
|
||||
baseComponent18.addExtra("Removed command exemption from ");
|
||||
}
|
||||
baseComponent18.addExtra(this.findBaseComponent(player2));
|
||||
ChatMessages.send(sender, baseComponent18);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "spectate":
|
||||
case "spec":
|
||||
if (!sender.hasPermission("aac.spectate")) {
|
||||
sender.sendMessage("/aac spectate missing permission node aac.spectate");
|
||||
return false;
|
||||
} else if (!(sender instanceof Player)) {
|
||||
sender.sendMessage("/aac spectate sender must be a player");
|
||||
return false;
|
||||
} else if (texts.length > 2) {
|
||||
sender.sendMessage("/aac spectate <player> too many arguments");
|
||||
return false;
|
||||
} else {
|
||||
Player player3 = (Player) sender;
|
||||
if (texts.length >= 2) {
|
||||
Player player4 = this.plugin.getServer().getPlayer(texts[1]);
|
||||
if (player4 == null) {
|
||||
sender.sendMessage("/aac spectate unknown player " + texts[1]);
|
||||
return false;
|
||||
}
|
||||
for (String text3 : this.plugin.getConfig().getStringList("pre_spectate")) {
|
||||
this.plugin.getServer().dispatchCommand(Bukkit.getConsoleSender(), this.plugin.resolvePlaceholders(player3, text3));
|
||||
}
|
||||
this.spectateLocations.putIfAbsent(player3, player3.getLocation());
|
||||
player3.teleport(player4, TeleportCause.COMMAND);
|
||||
BaseComponent baseComponent19 = this.getBaseComponent();
|
||||
baseComponent19.addExtra("Teleported to ");
|
||||
baseComponent19.addExtra(this.findBaseComponent(player4));
|
||||
baseComponent19.addExtra(" - ");
|
||||
baseComponent19.addExtra(this.findBaseComponent(player3));
|
||||
ChatMessages.send(sender, baseComponent19);
|
||||
return true;
|
||||
} else {
|
||||
Location location = (Location) this.spectateLocations.get(player3);
|
||||
if (location == null) {
|
||||
sender.sendMessage("/aac spectate missing location to go back to");
|
||||
return false;
|
||||
}
|
||||
for (String text4 : this.plugin.getConfig().getStringList("post_spectate")) {
|
||||
this.plugin.getServer().dispatchCommand(Bukkit.getConsoleSender(), this.plugin.resolvePlaceholders(player3, text4));
|
||||
}
|
||||
player3.teleport(location, TeleportCause.COMMAND);
|
||||
this.spectateLocations.remove(player3);
|
||||
BaseComponent baseComponent20 = this.getBaseComponent();
|
||||
baseComponent20.addExtra("Teleported back");
|
||||
ChatMessages.send(sender, baseComponent20);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case "sudo":
|
||||
if (!sender.hasPermission("aac.sudo")) {
|
||||
sender.sendMessage("/aac sudo missing permission node aac.sudo");
|
||||
return false;
|
||||
} else if (texts.length <= 1) {
|
||||
sender.sendMessage("/aac sudo <player> <command> [args...] missing player");
|
||||
return false;
|
||||
} else if (texts.length <= 2) {
|
||||
sender.sendMessage("/aac sudo <player> <command> [args...] missing command");
|
||||
return false;
|
||||
} else {
|
||||
Player player5 = this.plugin.getServer().getPlayer(texts[1]);
|
||||
if (player5 == null) {
|
||||
sender.sendMessage("/aac sudo unknown player " + texts[1]);
|
||||
return false;
|
||||
}
|
||||
String text5 = Arrays.stream(texts).skip(2L).collect(Collectors.joining(" "));
|
||||
BaseComponent baseComponent21 = this.getBaseComponent();
|
||||
baseComponent21.addExtra("/" + text5);
|
||||
ChatMessages.send(player5, baseComponent21);
|
||||
BaseComponent baseComponent22 = this.getBaseComponent();
|
||||
baseComponent22.addExtra(this.findBaseComponent(player5));
|
||||
baseComponent22.addExtra(" - /" + text5);
|
||||
ChatMessages.send(sender, baseComponent22);
|
||||
this.plugin.getServer().dispatchCommand(player5, text5);
|
||||
return true;
|
||||
}
|
||||
case "reset":
|
||||
if (!sender.hasPermission("aac.reset")) {
|
||||
sender.sendMessage("/aac reset missing permission node aac.reset");
|
||||
return false;
|
||||
} else {
|
||||
if (texts.length <= 1) {
|
||||
sender.sendMessage("/aac reset <player> [players...] missing player argument");
|
||||
return false;
|
||||
}
|
||||
for (int index3 = 1; index3 < texts.length; index3++) {
|
||||
OfflinePlayer offlinePlayer2 = this.plugin.getServer().getOfflinePlayer(texts[index3]);
|
||||
this.plugin.getDatabase().resetPlayerAsync(offlinePlayer2).thenCompose(result4 -> this.plugin.getAnalysisManager().analyzeAsync(offlinePlayer2).thenApply(result5 -> result4)).whenComplete((result, result2) -> {
|
||||
BaseComponent baseComponent23 = this.getBaseComponent();
|
||||
if (result2 == null) {
|
||||
TextComponent textComponent7 = new TextComponent(Integer.toString(result));
|
||||
textComponent7.setColor(ChatColor.GRAY);
|
||||
baseComponent23.addExtra("Cleared ");
|
||||
baseComponent23.addExtra(textComponent7);
|
||||
baseComponent23.addExtra(" entries for ");
|
||||
baseComponent23.addExtra(this.findBaseComponent(offlinePlayer2));
|
||||
baseComponent23.addExtra(" from database");
|
||||
ChatMessages.send(sender, baseComponent23);
|
||||
} else {
|
||||
baseComponent23.addExtra("Failed to reset ");
|
||||
baseComponent23.addExtra(this.findBaseComponent(offlinePlayer2));
|
||||
result2.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "check":
|
||||
if (!sender.hasPermission("aac.check")) {
|
||||
sender.sendMessage("/aac check missing permission node aac.check");
|
||||
return false;
|
||||
} else {
|
||||
if (texts.length <= 1) {
|
||||
sender.sendMessage("/aac check <player> [players...] missing player argument");
|
||||
return false;
|
||||
}
|
||||
for (int index4 = 1; index4 < texts.length; index4++) {
|
||||
OfflinePlayer offlinePlayer3 = this.plugin.getServer().getOfflinePlayer(texts[index4]);
|
||||
this.plugin.getAnalysisManager().analyzeAsync(offlinePlayer3).whenComplete((result, result2) -> {
|
||||
BaseComponent baseComponent23 = this.getBaseComponent();
|
||||
if (result2 == null) {
|
||||
baseComponent23.addExtra("Report ready for ");
|
||||
baseComponent23.addExtra(this.findBaseComponent(offlinePlayer3));
|
||||
ChatMessages.send(sender, baseComponent23);
|
||||
} else {
|
||||
baseComponent23.addExtra("Failed to generate report for ");
|
||||
baseComponent23.addExtra(this.findBaseComponent(offlinePlayer3));
|
||||
result2.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "alerts":
|
||||
if (!sender.hasPermission("aac.alerts")) {
|
||||
sender.sendMessage("/aac alerts missing permission node aac.alerts");
|
||||
return false;
|
||||
} else if (texts.length > 2) {
|
||||
sender.sendMessage("/aac alerts <off/normal/reduced> too many arguments");
|
||||
return false;
|
||||
} else {
|
||||
if (texts.length == 1) {
|
||||
BaseComponent baseComponent24 = this.getBaseComponent();
|
||||
baseComponent24.addExtra("Your alert level is " + this.alertModes.getOrDefault(sender, MitigationMode.NORMAL));
|
||||
ChatMessages.send(sender, baseComponent24);
|
||||
} else {
|
||||
String text6 = texts[1].toLowerCase();
|
||||
switch(text6) {
|
||||
case "off":
|
||||
this.alertModes.put(sender, MitigationMode.OFF);
|
||||
break;
|
||||
case "normal":
|
||||
this.alertModes.remove(sender);
|
||||
break;
|
||||
case "reduced":
|
||||
this.alertModes.put(sender, MitigationMode.REDUCED);
|
||||
break;
|
||||
default:
|
||||
sender.sendMessage("/aac alerts <off/normal/reduced> invalid alert level");
|
||||
return false;
|
||||
}
|
||||
BaseComponent baseComponent25 = this.getBaseComponent();
|
||||
baseComponent25.addExtra("Alert level updated to " + this.alertModes.getOrDefault(sender, MitigationMode.NORMAL));
|
||||
ChatMessages.send(sender, baseComponent25);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "debug":
|
||||
if (!sender.hasPermission("aac.debug")) {
|
||||
sender.sendMessage("/aac debug missing permission node aac.debug");
|
||||
return false;
|
||||
}
|
||||
if (texts.length == 1) {
|
||||
this.plugin.getDebugManager().updateCommandSender(sender);
|
||||
} else {
|
||||
for (int index5 = 1; index5 < texts.length; index5++) {
|
||||
OfflinePlayer offlinePlayer4 = this.plugin.getServer().getOfflinePlayer(texts[index5]);
|
||||
this.plugin.getDebugManager().updateCommandSender(sender, offlinePlayer4);
|
||||
}
|
||||
}
|
||||
BaseComponent baseComponent26 = this.getBaseComponent();
|
||||
baseComponent26.addExtra("Currently debugging:");
|
||||
if (this.plugin.getDebugManager().checkDebugManager(sender)) {
|
||||
baseComponent26.addExtra(" ");
|
||||
TextComponent textComponent8 = new TextComponent("[global]");
|
||||
textComponent8.setColor(ChatColor.GRAY);
|
||||
baseComponent26.addExtra(textComponent8);
|
||||
} else if (!this.plugin.getDebugManager().findSet(sender).isEmpty()) {
|
||||
for (OfflinePlayer offlinePlayer5 : this.plugin.getDebugManager().findSet(sender)) {
|
||||
baseComponent26.addExtra(" ");
|
||||
baseComponent26.addExtra(this.findBaseComponent(offlinePlayer5));
|
||||
}
|
||||
} else {
|
||||
baseComponent26.addExtra(" ");
|
||||
TextComponent textComponent9 = new TextComponent("[none]");
|
||||
textComponent9.setColor(ChatColor.GRAY);
|
||||
baseComponent26.addExtra(textComponent9);
|
||||
}
|
||||
ChatMessages.send(sender, baseComponent26);
|
||||
return true;
|
||||
case "reload":
|
||||
if (!sender.hasPermission("aac.reload")) {
|
||||
sender.sendMessage("/aac reload missing permission node aac.reload");
|
||||
return false;
|
||||
}
|
||||
this.plugin.getLogger().info("Reload requested by " + sender.getName());
|
||||
Bukkit.getServer().getPluginManager().disablePlugin(this.plugin);
|
||||
Bukkit.getServer().getPluginManager().enablePlugin(this.plugin);
|
||||
BaseComponent baseComponent27 = this.getBaseComponent();
|
||||
baseComponent27.addExtra("Reload complete");
|
||||
ChatMessages.send(sender, baseComponent27);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePlayer(OfflinePlayer offlinePlayer) {
|
||||
RiskLevel riskLevel = this.plugin.getAnalysisManager().getReport(offlinePlayer).getRiskLevel();
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.hasPermission("aac.alerts") && this.alertModes.getOrDefault(player, MitigationMode.NORMAL).checkMitigationMode(riskLevel)) {
|
||||
this.updateCommandSender(player, offlinePlayer);
|
||||
}
|
||||
}
|
||||
if (Bukkit.getConsoleSender().hasPermission("aac.alerts") && this.alertModes.getOrDefault(Bukkit.getConsoleSender(), MitigationMode.NORMAL).checkMitigationMode(riskLevel)) {
|
||||
ConsoleCommandSender consoleSender = Bukkit.getConsoleSender();
|
||||
this.updateCommandSender(consoleSender, offlinePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCommandSender(CommandSender sender, OfflinePlayer offlinePlayer) {
|
||||
BaseComponent baseComponent = this.getBaseComponent();
|
||||
baseComponent.addExtra(this.findBaseComponent(offlinePlayer));
|
||||
if (offlinePlayer.isOnline()) {
|
||||
baseComponent.addExtra(" - ");
|
||||
Player player = (Player) offlinePlayer;
|
||||
baseComponent.addExtra(this.findBaseComponent(player, sender));
|
||||
}
|
||||
ChatMessages.send(sender, baseComponent);
|
||||
}
|
||||
|
||||
private BaseComponent getBaseComponent() {
|
||||
TextComponent textComponent = new TextComponent(" ");
|
||||
TextComponent textComponent2 = new TextComponent("AAC ");
|
||||
textComponent2.setColor(this.ACCENT_COLOR);
|
||||
textComponent2.setBold(true);
|
||||
textComponent.addExtra(textComponent2);
|
||||
return textComponent;
|
||||
}
|
||||
|
||||
private BaseComponent findBaseComponent(OfflinePlayer offlinePlayer) {
|
||||
TextComponent textComponent = new TextComponent(offlinePlayer.getName());
|
||||
textComponent.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/aac status " + offlinePlayer.getName()));
|
||||
textComponent.setHoverEvent(new HoverEvent(net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT, new BaseComponent[] { new TextComponent("/aac status " + offlinePlayer.getName()) }));
|
||||
textComponent.setBold(false);
|
||||
PlayerAnalysisReport report = this.plugin.getAnalysisManager().getReport(offlinePlayer);
|
||||
if (report != null) {
|
||||
report.getRiskLevel().updateBaseComponent(textComponent);
|
||||
} else {
|
||||
RiskLevel.NEUTRAL.updateBaseComponent(textComponent);
|
||||
}
|
||||
if (!offlinePlayer.isOnline() || !this.plugin.getPacketDispatcher().getExemptions((Player) offlinePlayer).isEmpty()) {
|
||||
RiskLevel.IGNORED.updateBaseComponent(textComponent);
|
||||
}
|
||||
return textComponent;
|
||||
}
|
||||
|
||||
private BaseComponent findBaseComponent(Player player, CommandSender sender) {
|
||||
TextComponent textComponent = new TextComponent("Spectate");
|
||||
textComponent.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/aac spec " + player.getName()));
|
||||
textComponent.setHoverEvent(new HoverEvent(net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT, new BaseComponent[] { new TextComponent("/aac spec " + player.getName()) }));
|
||||
if (!(sender instanceof Player) || !sender.hasPermission("aac.spectate")) {
|
||||
textComponent.setStrikethrough(true);
|
||||
}
|
||||
return textComponent;
|
||||
}
|
||||
|
||||
private BaseComponent createBackLink(CommandSender sender) {
|
||||
TextComponent textComponent = new TextComponent("Go Back");
|
||||
textComponent.setClickEvent(new ClickEvent(Action.RUN_COMMAND, "/aac spec"));
|
||||
textComponent.setHoverEvent(new HoverEvent(net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT, new BaseComponent[] { new TextComponent("/aac spec") }));
|
||||
if (!(sender instanceof Player) || !sender.hasPermission("aac.spectate")) {
|
||||
textComponent.setStrikethrough(true);
|
||||
}
|
||||
return textComponent;
|
||||
}
|
||||
|
||||
public List onTabComplete(CommandSender sender, Command command, String text, String[] texts) {
|
||||
ArrayList values = new ArrayList();
|
||||
if (texts.length == 0) {
|
||||
return SUBCOMMANDS;
|
||||
} else if (texts.length == 1) {
|
||||
StringUtil.copyPartialMatches(texts[0], SUBCOMMANDS, values);
|
||||
return values;
|
||||
} else if ("alerts".equals(texts[0])) {
|
||||
StringUtil.copyPartialMatches(texts[0], ALERT_MODES, values);
|
||||
return values;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.konsolas.aac.command;
|
||||
|
||||
import me.konsolas.aac.analysis.RiskLevel;
|
||||
|
||||
enum MitigationMode {
|
||||
|
||||
OFF, NORMAL, REDUCED;
|
||||
|
||||
boolean checkMitigationMode(RiskLevel riskLevel) {
|
||||
switch(this) {
|
||||
case OFF:
|
||||
return false;
|
||||
case NORMAL:
|
||||
return riskLevel == RiskLevel.SUSPICIOUS || riskLevel == RiskLevel.HIGH;
|
||||
case REDUCED:
|
||||
return riskLevel == RiskLevel.HIGH;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.konsolas.aac.compat;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public interface NativeAccess {
|
||||
|
||||
ItemStack getActiveItem(Player player);
|
||||
|
||||
Collection<ItemStack> getHandItems(Player player);
|
||||
|
||||
boolean isGliding(Player player);
|
||||
|
||||
float getBlockFriction(Block block);
|
||||
|
||||
AxisAlignedBoundingBox getBoundingBox(Entity entity);
|
||||
|
||||
List getBlockCollisionBoxes(Block block, Entity entity);
|
||||
|
||||
BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value);
|
||||
|
||||
float getBlockDamage(Player player, Block block);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package me.konsolas.aac.compat;
|
||||
|
||||
import me.konsolas.aac.compat.v1_11.Minecraft111Access;
|
||||
import me.konsolas.aac.compat.v1_13.Minecraft113Access;
|
||||
import me.konsolas.aac.compat.v1_14.Minecraft114Access;
|
||||
import me.konsolas.aac.compat.v1_16.Minecraft116Access;
|
||||
import me.konsolas.aac.compat.v1_17.Minecraft117Access;
|
||||
import me.konsolas.aac.compat.v1_8.Minecraft18Access;
|
||||
import me.konsolas.aac.compat.v1_9.Minecraft19Access;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
|
||||
public final class NativeAccessProvider {
|
||||
|
||||
private static final NativeAccess INSTANCE;
|
||||
|
||||
public static NativeAccess getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
static {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
switch(serverVersion) {
|
||||
case V1_8:
|
||||
INSTANCE = new Minecraft18Access();
|
||||
break;
|
||||
case V1_9:
|
||||
case V1_10:
|
||||
INSTANCE = new Minecraft19Access(serverVersion);
|
||||
break;
|
||||
case V1_11:
|
||||
case V1_12:
|
||||
INSTANCE = new Minecraft111Access(serverVersion);
|
||||
break;
|
||||
case V1_13:
|
||||
INSTANCE = new Minecraft113Access(serverVersion);
|
||||
break;
|
||||
case V1_14:
|
||||
case V1_15:
|
||||
INSTANCE = new Minecraft114Access(serverVersion);
|
||||
break;
|
||||
case V1_16:
|
||||
INSTANCE = new Minecraft116Access(serverVersion);
|
||||
break;
|
||||
case V1_17:
|
||||
INSTANCE = new Minecraft117Access();
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package me.konsolas.aac.compat.integration;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public final class OptionalIntegrations {
|
||||
|
||||
private OptionalIntegrations() {
|
||||
}
|
||||
|
||||
private static Class<?> apiClass(String pluginName, String className) throws ClassNotFoundException {
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin(pluginName);
|
||||
return Class.forName(className, true, plugin.getClass().getClassLoader());
|
||||
}
|
||||
|
||||
public static int viaVersion(Player player) {
|
||||
try {
|
||||
Class<?> via = apiClass("ViaVersion", "com.viaversion.viaversion.api.Via");
|
||||
Object api = via.getMethod("getAPI").invoke(null);
|
||||
for (Method method : api.getClass().getMethods()) if (method.getName().equals("getPlayerVersion") && method.getParameterTypes().length == 1) {
|
||||
Class<?> parameter = method.getParameterTypes()[0];
|
||||
if (parameter == UUID.class)
|
||||
return ((Number) method.invoke(api, player.getUniqueId())).intValue();
|
||||
if (parameter.isInstance(player))
|
||||
return ((Number) method.invoke(api, player)).intValue();
|
||||
}
|
||||
throw new NoSuchMethodException("ViaAPI.getPlayerVersion");
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
throw new IllegalStateException("Failed to query ViaVersion", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static int protocolSupportVersion(Player player) {
|
||||
try {
|
||||
Class<?> api = apiClass("ProtocolSupport", "protocolsupport.api.ProtocolSupportAPI");
|
||||
Object version = api.getMethod("getProtocolVersion", Player.class).invoke(null, player);
|
||||
return ((Number) version.getClass().getMethod("getId").invoke(version)).intValue();
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
throw new IllegalStateException("Failed to query ProtocolSupport", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isFloodgatePlayer(UUID playerId) {
|
||||
try {
|
||||
Class<?> api = apiClass("floodgate", "org.geysermc.floodgate.api.FloodgateApi");
|
||||
Object instance = api.getMethod("getInstance").invoke(null);
|
||||
return (Boolean) api.getMethod("isFloodgatePlayer", UUID.class).invoke(instance, playerId);
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
throw new IllegalStateException("Failed to query Floodgate", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package me.konsolas.aac.compat.nms;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.AbstractObjectCollection;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
public class BlockBreakingHelper {
|
||||
|
||||
private static final Method GET_HANDLE_METHOD;
|
||||
|
||||
private static final Method GET_HANDLE_METHOD2;
|
||||
|
||||
private static final Constructor CONSTRUCTOR;
|
||||
|
||||
private static final Method GET_TYPE_METHOD;
|
||||
|
||||
private static Method GET_BLOCK_METHOD;
|
||||
|
||||
private static Method GET_DAMAGE_METHOD;
|
||||
|
||||
private static Method A_METHOD;
|
||||
|
||||
private static Method GET_DAMAGE_METHOD2;
|
||||
|
||||
public static long calculateTime(Block block, Player player) {
|
||||
return (long) (1.0F / getWorld(block, player));
|
||||
}
|
||||
|
||||
public static float getWorld(Block block, Player player) {
|
||||
if (ProtocolVersion.getServerVersion() == ProtocolVersion.V1_8) {
|
||||
try {
|
||||
Object result = CONSTRUCTOR.newInstance(block.getX(), block.getY(), block.getZ());
|
||||
Object result2 = GET_HANDLE_METHOD2.invoke(block.getWorld());
|
||||
Object result3 = GET_TYPE_METHOD.invoke(result2, result);
|
||||
Object result4 = GET_BLOCK_METHOD.invoke(result3);
|
||||
return (Float) GET_DAMAGE_METHOD.invoke(result4, GET_HANDLE_METHOD.invoke(player), result2, result);
|
||||
} catch (InstantiationException | InvocationTargetException | IllegalAccessException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
} else {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
try {
|
||||
Object result5 = CONSTRUCTOR.newInstance(block.getX(), block.getY(), block.getZ());
|
||||
Object result6 = GET_HANDLE_METHOD2.invoke(block.getWorld());
|
||||
Object result7 = GET_TYPE_METHOD.invoke(result6, result5);
|
||||
return (Float) GET_DAMAGE_METHOD2.invoke(result7, GET_HANDLE_METHOD.invoke(player), result6, result5);
|
||||
} catch (InstantiationException | InvocationTargetException | IllegalAccessException exception2) {
|
||||
throw new RuntimeException(exception2);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Object result8 = CONSTRUCTOR.newInstance(block.getX(), block.getY(), block.getZ());
|
||||
Object result9 = GET_HANDLE_METHOD2.invoke(block.getWorld());
|
||||
Object result10 = GET_TYPE_METHOD.invoke(result9, result8);
|
||||
return (Float) A_METHOD.invoke(result10, GET_HANDLE_METHOD.invoke(player), result9, result8);
|
||||
} catch (InstantiationException | InvocationTargetException | IllegalAccessException exception3) {
|
||||
throw new RuntimeException(exception3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getAmplifier(Player player) {
|
||||
for (PotionEffect potionEffect : player.getActivePotionEffects()) {
|
||||
if (potionEffect.getType().equals(PotionEffectType.FAST_DIGGING)) {
|
||||
return potionEffect.getAmplifier() + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_17;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
GET_HANDLE_METHOD = null;
|
||||
GET_HANDLE_METHOD2 = null;
|
||||
CONSTRUCTOR = null;
|
||||
GET_TYPE_METHOD = null;
|
||||
} else {
|
||||
try {
|
||||
GET_HANDLE_METHOD2 = MinecraftReflection.getCraftWorldClass().getMethod("getHandle");
|
||||
GET_HANDLE_METHOD = MinecraftReflection.getCraftPlayerClass().getMethod("getHandle");
|
||||
CONSTRUCTOR = MinecraftReflection.getBlockPositionClass().getConstructor(int.class, int.class, int.class);
|
||||
GET_TYPE_METHOD = MinecraftReflection.getWorldClass().getMethod("getType", MinecraftReflection.getBlockPositionClass());
|
||||
if (ProtocolVersion.getServerVersion() == ProtocolVersion.V1_8) {
|
||||
GET_BLOCK_METHOD = MinecraftReflection.getIBlockDataClass().getMethod("getBlock");
|
||||
GET_DAMAGE_METHOD = MinecraftReflection.getBlockClass().getMethod("getDamage", MinecraftReflection.getEntityHumanClass(), MinecraftReflection.getWorldClass(), MinecraftReflection.getBlockPositionClass());
|
||||
} else {
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version2)) {
|
||||
GET_DAMAGE_METHOD2 = MinecraftReflection.getIBlockDataClass().getMethod("getDamage", MinecraftReflection.getEntityHumanClass(), MinecraftReflection.getIBlockAccessClass(), MinecraftReflection.getBlockPositionClass());
|
||||
} else {
|
||||
A_METHOD = MinecraftReflection.getIBlockDataClass().getMethod("a", MinecraftReflection.getEntityHumanClass(), MinecraftReflection.getWorldClass(), MinecraftReflection.getBlockPositionClass());
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package me.konsolas.aac.compat.nms;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.util.BlockVector;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class EntityRayTracer {
|
||||
|
||||
private static final MethodHandle entityHandle;
|
||||
|
||||
private static final MethodHandle vectorConstructor;
|
||||
|
||||
private static final MethodHandle hitBlockPosition;
|
||||
|
||||
private static final MethodHandle hitDirection;
|
||||
|
||||
private static final MethodHandle directionToBlockFace;
|
||||
|
||||
private static final MethodHandle hitPosition;
|
||||
|
||||
private static final MethodHandle vectorX;
|
||||
|
||||
private static final MethodHandle vectorY;
|
||||
|
||||
private static final MethodHandle vectorZ;
|
||||
|
||||
private static final MethodHandle blockX;
|
||||
|
||||
private static final MethodHandle blockY;
|
||||
|
||||
private static final MethodHandle blockZ;
|
||||
|
||||
private static final MethodHandle rayTrace;
|
||||
|
||||
public static BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
Vector vector3 = vector.clone().add(vector2.clone().normalize().multiply(value));
|
||||
try {
|
||||
Object result = (Object) entityHandle.invoke((World) entity.getWorld());
|
||||
Object result2 = (Object) vectorConstructor.invoke((double) vector.getX(), (double) vector.getY(), (double) vector.getZ());
|
||||
Object result3 = (Object) vectorConstructor.invoke((double) vector3.getX(), (double) vector3.getY(), (double) vector3.getZ());
|
||||
Object result4 = (Object) rayTrace.invoke((Object) result, (Object) result2, (Object) result3);
|
||||
if (result4 == null) {
|
||||
return new BlockRayTraceResult();
|
||||
} else {
|
||||
Object result5 = (Object) hitPosition.invoke((Object) result4);
|
||||
Object result6 = (Object) hitBlockPosition.invoke((Object) result4);
|
||||
Vector vector4 = new Vector((double) vectorX.invoke((Object) result5), (double) vectorY.invoke((Object) result5), (double) vectorZ.invoke((Object) result5));
|
||||
BlockVector blockVector = new BlockVector((int) blockX.invoke((Object) result6), (int) blockY.invoke((Object) result6), (int) blockZ.invoke((Object) result6));
|
||||
BlockFace blockFace = (BlockFace) directionToBlockFace.invoke((Object) (Object) hitDirection.invoke((Object) result4));
|
||||
return new BlockRayTraceResult(vector4, blockVector, blockFace);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
entityHandle = null;
|
||||
vectorConstructor = null;
|
||||
hitBlockPosition = null;
|
||||
hitDirection = null;
|
||||
directionToBlockFace = null;
|
||||
hitPosition = null;
|
||||
vectorX = null;
|
||||
vectorY = null;
|
||||
vectorZ = null;
|
||||
blockX = null;
|
||||
blockY = null;
|
||||
blockZ = null;
|
||||
rayTrace = null;
|
||||
} else {
|
||||
try {
|
||||
Class vec3DClass = MinecraftReflection.getVec3DClass();
|
||||
Class movingObjectPositionClass = MinecraftReflection.getMovingObjectPositionClass();
|
||||
Class blockPositionClass = MinecraftReflection.getBlockPositionClass();
|
||||
Class minecraftClassWithMinecraftPackage = MinecraftReflection.getNmsClass((String) "EnumDirection");
|
||||
Class minecraftClass = MinecraftReflection.getCraftBlockClass();
|
||||
entityHandle = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getCraftWorldClass().getMethod("getHandle")));
|
||||
vectorConstructor = ReflectionAccess.LOOKUP.findConstructor(vec3DClass, MethodType.methodType(void.class, double.class, double.class, double.class));
|
||||
hitPosition = ReflectionAccess.LOOKUP.findGetter(movingObjectPositionClass, "pos", vec3DClass);
|
||||
hitBlockPosition = ReflectionAccess.LOOKUP.unreflect(Arrays.stream(movingObjectPositionClass.getMethods()).filter(result -> result.getReturnType().equals(blockPositionClass)).findFirst().orElseThrow(NoSuchMethodException::new));
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_14;
|
||||
hitDirection = serverVersion.isAtLeast(version2) ? ReflectionAccess.LOOKUP.findVirtual(movingObjectPositionClass, "getDirection", MethodType.methodType(minecraftClassWithMinecraftPackage)) : ReflectionAccess.LOOKUP.findGetter(movingObjectPositionClass, "direction", minecraftClassWithMinecraftPackage);
|
||||
directionToBlockFace = ReflectionAccess.LOOKUP.findStatic(minecraftClass, "notchToBlockFace", MethodType.methodType(BlockFace.class, minecraftClassWithMinecraftPackage));
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version3 = ProtocolVersion.V1_9;
|
||||
if (serverVersion.isAtLeast(version3)) {
|
||||
vectorX = ReflectionAccess.LOOKUP.findGetter(MinecraftReflection.getVec3DClass(), "x", double.class);
|
||||
vectorY = ReflectionAccess.LOOKUP.findGetter(MinecraftReflection.getVec3DClass(), "y", double.class);
|
||||
vectorZ = ReflectionAccess.LOOKUP.findGetter(MinecraftReflection.getVec3DClass(), "z", double.class);
|
||||
} else {
|
||||
vectorX = ReflectionAccess.LOOKUP.findGetter(MinecraftReflection.getVec3DClass(), "a", double.class);
|
||||
vectorY = ReflectionAccess.LOOKUP.findGetter(MinecraftReflection.getVec3DClass(), "b", double.class);
|
||||
vectorZ = ReflectionAccess.LOOKUP.findGetter(MinecraftReflection.getVec3DClass(), "c", double.class);
|
||||
}
|
||||
blockX = ReflectionAccess.LOOKUP.unreflect(blockPositionClass.getMethod("getX"));
|
||||
blockY = ReflectionAccess.LOOKUP.unreflect(blockPositionClass.getMethod("getY"));
|
||||
blockZ = ReflectionAccess.LOOKUP.unreflect(blockPositionClass.getMethod("getZ"));
|
||||
rayTrace = ReflectionAccess.LOOKUP.findVirtual(MinecraftReflection.getWorldClass(), "rayTrace", MethodType.methodType(movingObjectPositionClass, vec3DClass, vec3DClass));
|
||||
} catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException exception) {
|
||||
throw new RuntimeException("LegacyRaytracer: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package me.konsolas.aac.compat.nms;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class LegacyBoundingBoxHelper {
|
||||
|
||||
private static final MethodHandle constructor;
|
||||
|
||||
private static final MethodHandle minXGetter;
|
||||
|
||||
private static final MethodHandle minYGetter;
|
||||
|
||||
private static final MethodHandle minZGetter;
|
||||
|
||||
private static final MethodHandle maxXGetter;
|
||||
|
||||
private static final MethodHandle maxYGetter;
|
||||
|
||||
private static final MethodHandle maxZGetter;
|
||||
|
||||
public static AxisAlignedBoundingBox findAxisAlignedBoundingBox(Object result) {
|
||||
try {
|
||||
return new AxisAlignedBoundingBox((double) minXGetter.invoke((Object) result), (double) minYGetter.invoke((Object) result), (double) minZGetter.invoke((Object) result), (double) maxXGetter.invoke((Object) result), (double) maxYGetter.invoke((Object) result), (double) maxZGetter.invoke((Object) result));
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("LegacyBoundingBoxHelper: fromNMS", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static AxisAlignedBoundingBox findAxisAlignedBoundingBox(Object result, double value, double value2, double value3) {
|
||||
try {
|
||||
return new AxisAlignedBoundingBox((double) minXGetter.invoke((Object) result) + value, (double) minYGetter.invoke((Object) result) + value2, (double) minZGetter.invoke((Object) result) + value3, (double) maxXGetter.invoke((Object) result) + value, (double) maxYGetter.invoke((Object) result) + value2, (double) maxZGetter.invoke((Object) result) + value3);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("LegacyBoundingBoxHelper: fromNMS_offset", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object findObject(double value, double value2, double value3, double value4, double value5, double value6) {
|
||||
try {
|
||||
return (Object) constructor.invoke((double) value, (double) value2, (double) value3, (double) value4, (double) value5, (double) value6);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("LegacyBoundingBoxHelper: cons", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
constructor = null;
|
||||
minXGetter = null;
|
||||
minYGetter = null;
|
||||
minZGetter = null;
|
||||
maxXGetter = null;
|
||||
maxYGetter = null;
|
||||
maxZGetter = null;
|
||||
} else {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String text = name.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
Class classValue = Class.forName(text + ".AxisAlignedBB");
|
||||
constructor = ReflectionAccess.LOOKUP.unreflectConstructor((Constructor<?>) ReflectionAccess.makeAccessible(classValue.getConstructor(double.class, double.class, double.class, double.class, double.class, double.class)));
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version2)) {
|
||||
minXGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("minX")));
|
||||
minYGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("minY")));
|
||||
minZGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("minZ")));
|
||||
maxXGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("maxX")));
|
||||
maxYGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("maxY")));
|
||||
maxZGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("maxZ")));
|
||||
} else {
|
||||
minXGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("a")));
|
||||
minYGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("b")));
|
||||
minZGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("c")));
|
||||
maxXGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("d")));
|
||||
maxYGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("e")));
|
||||
maxZGetter = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue.getDeclaredField("f")));
|
||||
}
|
||||
} catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | NoSuchFieldException exception) {
|
||||
throw new RuntimeException("LegacyBoundingBoxHelper: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package me.konsolas.aac.compat.nms;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public final class MinecraftReflection {
|
||||
|
||||
public static String craftBukkitPackage = Bukkit.getServer().getClass().getPackage().getName();
|
||||
|
||||
public static String minecraftPackage = craftBukkitPackage.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
|
||||
private static final Map classCache = new HashMap();
|
||||
|
||||
private MinecraftReflection() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private static Class resolveClass(String text) {
|
||||
try {
|
||||
if (classCache.containsKey(text)) {
|
||||
return (Class) classCache.get(text);
|
||||
} else {
|
||||
Class classValue = Class.forName(text);
|
||||
classCache.put(text, classValue);
|
||||
return classValue;
|
||||
}
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
throw new IllegalArgumentException("Class not found: " + text, classNotFoundException);
|
||||
}
|
||||
}
|
||||
|
||||
public static Class getNmsClass(String text) {
|
||||
String text2 = minecraftPackage + '.' + text;
|
||||
String text3 = text2;
|
||||
return resolveClass(text3);
|
||||
}
|
||||
|
||||
public static Class getCraftClass(String text) {
|
||||
String text2 = craftBukkitPackage + '.' + text;
|
||||
String text3 = text2;
|
||||
return resolveClass(text3);
|
||||
}
|
||||
|
||||
public static Class getCraftPlayerClass() {
|
||||
return getCraftClass((String) "entity.CraftPlayer");
|
||||
}
|
||||
|
||||
public static Class getCraftWorldClass() {
|
||||
return getCraftClass((String) "CraftWorld");
|
||||
}
|
||||
|
||||
public static Class getCraftBlockClass() {
|
||||
return getCraftClass((String) "block.CraftBlock");
|
||||
}
|
||||
|
||||
public static Class getEntityClass() {
|
||||
return getNmsClass((String) "Entity");
|
||||
}
|
||||
|
||||
public static Class getEntityPlayerClass() {
|
||||
return getNmsClass((String) "EntityPlayer");
|
||||
}
|
||||
|
||||
public static Class getEntityHumanClass() {
|
||||
return getNmsClass((String) "EntityHuman");
|
||||
}
|
||||
|
||||
public static Class getWorldClass() {
|
||||
return getNmsClass((String) "World");
|
||||
}
|
||||
|
||||
public static Class getBlockClass() {
|
||||
return getNmsClass((String) "Block");
|
||||
}
|
||||
|
||||
public static Class getMovingObjectPositionClass() {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_14;
|
||||
Class classValue;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
classValue = getNmsClass((String) "MovingObjectPositionBlock");
|
||||
} else {
|
||||
classValue = getNmsClass((String) "MovingObjectPosition");
|
||||
}
|
||||
return classValue;
|
||||
}
|
||||
|
||||
public static Class getBlockPositionClass() {
|
||||
return getNmsClass((String) "BlockPosition");
|
||||
}
|
||||
|
||||
public static Class getIBlockDataClass() {
|
||||
return getNmsClass((String) "IBlockData");
|
||||
}
|
||||
|
||||
public static Class getIBlockAccessClass() {
|
||||
return getNmsClass((String) "IBlockAccess");
|
||||
}
|
||||
|
||||
public static Class getVec3DClass() {
|
||||
return getNmsClass((String) "Vec3D");
|
||||
}
|
||||
|
||||
public static Class getVoxelShapeCollisionClass() {
|
||||
return getNmsClass((String) "VoxelShapeCollision");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.konsolas.aac.compat.nms;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
|
||||
public class ReflectionAccess {
|
||||
|
||||
public static Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
public static AccessibleObject makeAccessible(AccessibleObject accessibleObject) {
|
||||
accessibleObject.setAccessible(true);
|
||||
return accessibleObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package me.konsolas.aac.compat.v1_11;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.nms.BlockBreakingHelper;
|
||||
import me.konsolas.aac.compat.nms.EntityRayTracer;
|
||||
import me.konsolas.aac.compat.nms.LegacyBoundingBoxHelper;
|
||||
import me.konsolas.aac.compat.nms.MinecraftReflection;
|
||||
import me.konsolas.aac.compat.nms.ReflectionAccess;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Minecraft111Access implements NativeAccess {
|
||||
|
||||
private final MethodHandle declaredField;
|
||||
|
||||
private final Object field;
|
||||
|
||||
private final MethodHandle method;
|
||||
|
||||
private final MethodHandle declaredMethod;
|
||||
|
||||
private final MethodHandle declaredField2;
|
||||
|
||||
private final MethodHandle method2;
|
||||
|
||||
private final MethodHandle method3;
|
||||
|
||||
private final MethodHandle method4;
|
||||
|
||||
private final MethodHandle constructor;
|
||||
|
||||
private final MethodHandle method5;
|
||||
|
||||
private final MethodHandle method6;
|
||||
|
||||
public Minecraft111Access(ProtocolVersion version) {
|
||||
if (version != ProtocolVersion.V1_11 && version != ProtocolVersion.V1_12) {
|
||||
throw new IllegalArgumentException("Invalid version for SALImplV1_9_10: " + version);
|
||||
} else {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String text = name.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
Class classValue = Class.forName(name + ".entity.CraftEntity");
|
||||
Class classValue2 = Class.forName(name + ".CraftWorld");
|
||||
Class classValue3 = Class.forName(name + ".inventory.ItemStack");
|
||||
Class classValue4 = Class.forName(text + ".World");
|
||||
Class classValue5 = Class.forName(text + ".Entity");
|
||||
Class classValue6 = Class.forName(text + ".EntityPlayer");
|
||||
Class classValue7 = Class.forName(text + ".ItemStack");
|
||||
Class classValue8 = Class.forName(text + ".AxisAlignedBB");
|
||||
Class classValue9 = Class.forName(text + ".BlockPosition");
|
||||
Class classValue10 = Class.forName(text + ".IBlockData");
|
||||
this.declaredField = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue6.getSuperclass().getSuperclass().getDeclaredField("activeItem")));
|
||||
this.field = classValue7.getField("a").get(null);
|
||||
this.method = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue3.getMethod("asCraftMirror", classValue7)));
|
||||
this.declaredMethod = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getCraftBlockClass().getDeclaredMethod("getNMSBlock")));
|
||||
this.declaredField2 = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(MinecraftReflection.getBlockClass().getDeclaredField("frictionFactor")));
|
||||
this.method2 = ReflectionAccess.LOOKUP.unreflect(classValue.getMethod("getHandle"));
|
||||
this.method3 = ReflectionAccess.LOOKUP.unreflect(classValue2.getMethod("getHandle"));
|
||||
this.method4 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getEntityClass().getMethod("getBoundingBox")));
|
||||
this.constructor = ReflectionAccess.LOOKUP.unreflectConstructor(classValue9.getConstructor(int.class, int.class, int.class));
|
||||
this.method5 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue4.getMethod("getType", classValue9)));
|
||||
this.method6 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue10.getMethod("a", classValue4, classValue9, classValue8, List.class, classValue5, boolean.class)));
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException | IllegalAccessException exception) {
|
||||
throw new RuntimeException("SALImplV1_11_12: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getActiveItem(Player player) {
|
||||
try {
|
||||
Object result = (Object) this.declaredField.invoke((Object) (Object) this.method2.invoke((Player) player));
|
||||
return Objects.equals(result, this.field) ? null : (ItemStack) this.method.invoke((Object) result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_11_12: getItemInUse", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getHandItems(Player player) {
|
||||
return Arrays.asList(player.getInventory().getItemInMainHand(), player.getInventory().getItemInOffHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(Player player) {
|
||||
return player.isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction(Block block) {
|
||||
try {
|
||||
return (float) this.declaredField2.invoke((Object) (Object) this.declaredMethod.invoke((Block) block));
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_11_12: getFrictionFactor", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox(Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method4.invoke((Object) (Object) this.method2.invoke((Entity) entity));
|
||||
return LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_11_12: getBoundingBox", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getBlockCollisionBoxes(Block block, Entity entity) {
|
||||
ArrayList values;
|
||||
try {
|
||||
Object result = (Object) this.method3.invoke((World) block.getWorld());
|
||||
Object result2 = (Object) this.constructor.invoke((int) block.getX(), (int) block.getY(), (int) block.getZ());
|
||||
double x = block.getX();
|
||||
double y = block.getY();
|
||||
double z = block.getZ();
|
||||
double value = block.getX() + 1.0;
|
||||
double value2 = block.getZ() + 1.0;
|
||||
double value3 = block.getY() + 1.0;
|
||||
double value4 = value;
|
||||
double value5 = z;
|
||||
double value6 = y;
|
||||
double value7 = x;
|
||||
Object result3 = LegacyBoundingBoxHelper.findObject(value7, value6, value5, value4, value3, value2);
|
||||
ArrayList values2 = new ArrayList();
|
||||
this.method6.invoke((Object) (Object) this.method5.invoke((Object) result, (Object) result2), (Object) result, (Object) result2, (Object) result3, (List) values2, (Object) (Object) this.method2.invoke((Entity) entity), (boolean) false);
|
||||
ArrayList values3 = new ArrayList(values2.size());
|
||||
for (Object result4 : values2) {
|
||||
z = -block.getX();
|
||||
double value8 = -block.getZ();
|
||||
double value9 = -block.getY();
|
||||
double value10 = z;
|
||||
values3.add(LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result4, value10, value9, value8));
|
||||
}
|
||||
values = values3;
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_11_12: getCollisionShape", throwable);
|
||||
}
|
||||
if (null != null) {
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
return EntityRayTracer.rayTrace(entity, vector, vector2, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockDamage(Player player, Block block) {
|
||||
return BlockBreakingHelper.getWorld(block, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package me.konsolas.aac.compat.v1_13;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.nms.BlockBreakingHelper;
|
||||
import me.konsolas.aac.compat.nms.LegacyBoundingBoxHelper;
|
||||
import me.konsolas.aac.compat.nms.MinecraftReflection;
|
||||
import me.konsolas.aac.compat.nms.ReflectionAccess;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Minecraft113Access implements NativeAccess {
|
||||
|
||||
private final MethodHandle declaredField;
|
||||
|
||||
private final Object field;
|
||||
|
||||
private final MethodHandle method;
|
||||
|
||||
private final MethodHandle declaredMethod;
|
||||
|
||||
private final MethodHandle declaredField2;
|
||||
|
||||
private final MethodHandle method2;
|
||||
|
||||
private final MethodHandle method3;
|
||||
|
||||
private final MethodHandle constructor;
|
||||
|
||||
private final MethodHandle method4;
|
||||
|
||||
private final MethodHandle method5;
|
||||
|
||||
private final MethodHandle method6;
|
||||
|
||||
public Minecraft113Access(ProtocolVersion version) {
|
||||
if (version != ProtocolVersion.V1_13) {
|
||||
throw new IllegalArgumentException("Invalid version for SALImplV1_13: " + version);
|
||||
} else {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String text = name.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
Class classValue = Class.forName(name + ".entity.CraftEntity");
|
||||
Class classValue2 = Class.forName(name + ".CraftWorld");
|
||||
Class classValue3 = Class.forName(name + ".inventory.ItemStack");
|
||||
Class classValue4 = Class.forName(text + ".World");
|
||||
Class.forName(text + ".Entity");
|
||||
Class classValue5 = Class.forName(text + ".EntityPlayer");
|
||||
Class classValue6 = Class.forName(text + ".ItemStack");
|
||||
Class.forName(text + ".AxisAlignedBB");
|
||||
Class classValue7 = Class.forName(text + ".BlockPosition");
|
||||
Class.forName(text + ".IBlockData");
|
||||
this.declaredField = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue5.getSuperclass().getSuperclass().getDeclaredField("activeItem")));
|
||||
this.field = classValue6.getField("a").get(null);
|
||||
this.method = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue3.getMethod("asCraftMirror", classValue6)));
|
||||
this.declaredMethod = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getCraftBlockClass().getDeclaredMethod("getNMSBlock")));
|
||||
this.declaredField2 = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(MinecraftReflection.getBlockClass().getDeclaredField("frictionFactor")));
|
||||
this.method2 = ReflectionAccess.LOOKUP.unreflect(classValue.getMethod("getHandle"));
|
||||
this.method3 = ReflectionAccess.LOOKUP.unreflect(classValue2.getMethod("getHandle"));
|
||||
this.constructor = ReflectionAccess.LOOKUP.unreflectConstructor(classValue7.getConstructor(int.class, int.class, int.class));
|
||||
this.method4 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue4.getMethod("getType", classValue7)));
|
||||
this.method5 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getIBlockDataClass().getMethod("getCollisionShape", MinecraftReflection.getIBlockAccessClass(), MinecraftReflection.getBlockPositionClass())));
|
||||
this.method6 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getNmsClass((String) "VoxelShape").getMethod("d")));
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException | IllegalAccessException exception) {
|
||||
throw new RuntimeException("SALImplV1_13: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getActiveItem(Player player) {
|
||||
try {
|
||||
Object result = (Object) this.declaredField.invoke((Object) (Object) this.method2.invoke((Player) player));
|
||||
return Objects.equals(result, this.field) ? null : (ItemStack) this.method.invoke((Object) result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_13: getItemInUse", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getHandItems(Player player) {
|
||||
return Arrays.asList(player.getInventory().getItemInMainHand(), player.getInventory().getItemInOffHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(Player player) {
|
||||
return player.isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction(Block block) {
|
||||
try {
|
||||
return (float) this.declaredField2.invoke((Object) (Object) this.declaredMethod.invoke((Block) block));
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_13: getFrictionFactor", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox(Entity entity) {
|
||||
BoundingBox boundingBox = entity.getBoundingBox();
|
||||
return new AxisAlignedBoundingBox(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getBlockCollisionBoxes(Block block, Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method3.invoke((World) block.getWorld());
|
||||
Object result2 = (Object) this.constructor.invoke((int) block.getX(), (int) block.getY(), (int) block.getZ());
|
||||
Object result3 = (Object) this.method5.invoke((Object) (Object) this.method4.invoke((Object) result, (Object) result2), (Object) result, (Object) result2);
|
||||
List values = (List) this.method6.invoke((Object) result3);
|
||||
ArrayList values2 = new ArrayList(values.size());
|
||||
for (Object result4 : values) {
|
||||
values2.add(LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result4));
|
||||
}
|
||||
return values2;
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_13: getCollisionShape", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
RayTraceResult rayTraceResult = entity.getWorld().rayTraceBlocks(vector.toLocation(entity.getWorld()), vector2.clone().normalize(), value);
|
||||
return BlockRayTraceResult.create(rayTraceResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockDamage(Player player, Block block) {
|
||||
return BlockBreakingHelper.getWorld(block, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package me.konsolas.aac.compat.v1_14;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.nms.BlockBreakingHelper;
|
||||
import me.konsolas.aac.compat.nms.LegacyBoundingBoxHelper;
|
||||
import me.konsolas.aac.compat.nms.MinecraftReflection;
|
||||
import me.konsolas.aac.compat.nms.ReflectionAccess;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Minecraft114Access implements NativeAccess {
|
||||
|
||||
private final MethodHandle declaredField;
|
||||
|
||||
private final Object field;
|
||||
|
||||
private final MethodHandle method;
|
||||
|
||||
private final MethodHandle declaredMethod;
|
||||
|
||||
private final MethodHandle declaredField2;
|
||||
|
||||
private final MethodHandle method2;
|
||||
|
||||
private final MethodHandle method3;
|
||||
|
||||
private final MethodHandle constructor;
|
||||
|
||||
private final MethodHandle method4;
|
||||
|
||||
private final MethodHandle method5;
|
||||
|
||||
private final Object method6;
|
||||
|
||||
private final MethodHandle method7;
|
||||
|
||||
public Minecraft114Access(ProtocolVersion version) {
|
||||
if (version != ProtocolVersion.V1_14 && version != ProtocolVersion.V1_15) {
|
||||
throw new IllegalArgumentException("Invalid version for SALImplV1_14_15: " + version);
|
||||
} else {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String text = name.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
Class classValue = Class.forName(name + ".entity.CraftEntity");
|
||||
Class classValue2 = Class.forName(name + ".CraftWorld");
|
||||
Class classValue3 = Class.forName(name + ".inventory.ItemStack");
|
||||
Class classValue4 = Class.forName(text + ".World");
|
||||
Class.forName(text + ".Entity");
|
||||
Class classValue5 = Class.forName(text + ".EntityPlayer");
|
||||
Class classValue6 = Class.forName(text + ".ItemStack");
|
||||
Class.forName(text + ".AxisAlignedBB");
|
||||
Class classValue7 = Class.forName(text + ".BlockPosition");
|
||||
Class.forName(text + ".IBlockData");
|
||||
this.declaredField = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue5.getSuperclass().getSuperclass().getDeclaredField("activeItem")));
|
||||
this.field = classValue6.getField("a").get(null);
|
||||
this.method = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue3.getMethod("asCraftMirror", classValue6)));
|
||||
this.declaredMethod = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getCraftBlockClass().getDeclaredMethod("getNMSBlock")));
|
||||
this.declaredField2 = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(MinecraftReflection.getBlockClass().getDeclaredField("frictionFactor")));
|
||||
this.method2 = ReflectionAccess.LOOKUP.unreflect(classValue.getMethod("getHandle"));
|
||||
this.method3 = ReflectionAccess.LOOKUP.unreflect(classValue2.getMethod("getHandle"));
|
||||
this.constructor = ReflectionAccess.LOOKUP.unreflectConstructor(classValue7.getConstructor(int.class, int.class, int.class));
|
||||
this.method4 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue4.getMethod("getType", classValue7)));
|
||||
this.method5 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getIBlockDataClass().getMethod("b", MinecraftReflection.getIBlockAccessClass(), MinecraftReflection.getBlockPositionClass(), MinecraftReflection.getVoxelShapeCollisionClass())));
|
||||
this.method6 = MinecraftReflection.getVoxelShapeCollisionClass().getMethod("a").invoke(null);
|
||||
this.method7 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getNmsClass((String) "VoxelShape").getMethod("d")));
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException | InvocationTargetException | IllegalAccessException exception) {
|
||||
throw new RuntimeException("SALImplV1_14_15: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getActiveItem(Player player) {
|
||||
try {
|
||||
Object result = (Object) this.declaredField.invoke((Object) (Object) this.method2.invoke((Player) player));
|
||||
return Objects.equals(result, this.field) ? null : (ItemStack) this.method.invoke((Object) result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_14_15: getItemInUse", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getHandItems(Player player) {
|
||||
return Arrays.asList(player.getInventory().getItemInMainHand(), player.getInventory().getItemInOffHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(Player player) {
|
||||
return player.isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction(Block block) {
|
||||
try {
|
||||
return (float) this.declaredField2.invoke((Object) (Object) this.declaredMethod.invoke((Block) block));
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_14_15: getFrictionFactor", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox(Entity entity) {
|
||||
BoundingBox boundingBox = entity.getBoundingBox();
|
||||
return new AxisAlignedBoundingBox(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getBlockCollisionBoxes(Block block, Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method3.invoke((World) block.getWorld());
|
||||
Object result2 = (Object) this.constructor.invoke((int) block.getX(), (int) block.getY(), (int) block.getZ());
|
||||
Object result3 = (Object) this.method5.invoke((Object) (Object) this.method4.invoke((Object) result, (Object) result2), (Object) result, (Object) result2, (Object) this.method6);
|
||||
List values = (List) this.method7.invoke((Object) result3);
|
||||
ArrayList values2 = new ArrayList(values.size());
|
||||
for (Object result4 : values) {
|
||||
values2.add(LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result4));
|
||||
}
|
||||
return values2;
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_14_15: getCollisionShape", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
RayTraceResult rayTraceResult = entity.getWorld().rayTraceBlocks(vector.toLocation(entity.getWorld()), vector2.clone().normalize(), value);
|
||||
return BlockRayTraceResult.create(rayTraceResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockDamage(Player player, Block block) {
|
||||
return BlockBreakingHelper.getWorld(block, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package me.konsolas.aac.compat.v1_16;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.nms.BlockBreakingHelper;
|
||||
import me.konsolas.aac.compat.nms.LegacyBoundingBoxHelper;
|
||||
import me.konsolas.aac.compat.nms.MinecraftReflection;
|
||||
import me.konsolas.aac.compat.nms.ReflectionAccess;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Minecraft116Access implements NativeAccess {
|
||||
|
||||
private final MethodHandle declaredField;
|
||||
|
||||
private final Object field;
|
||||
|
||||
private final MethodHandle method;
|
||||
|
||||
private final MethodHandle declaredMethod;
|
||||
|
||||
private final MethodHandle declaredField2;
|
||||
|
||||
private final MethodHandle method2;
|
||||
|
||||
private final MethodHandle method3;
|
||||
|
||||
private final MethodHandle constructor;
|
||||
|
||||
private final MethodHandle method4;
|
||||
|
||||
private final MethodHandle method5;
|
||||
|
||||
private final Object method6;
|
||||
|
||||
private final MethodHandle method7;
|
||||
|
||||
public Minecraft116Access(ProtocolVersion version) {
|
||||
if (version != ProtocolVersion.V1_16) {
|
||||
throw new IllegalArgumentException("Invalid version for SALImplV1_16: " + version);
|
||||
} else {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String text = name.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
Class classValue = Class.forName(name + ".entity.CraftEntity");
|
||||
Class classValue2 = Class.forName(name + ".CraftWorld");
|
||||
Class classValue3 = Class.forName(name + ".inventory.ItemStack");
|
||||
Class classValue4 = Class.forName(text + ".World");
|
||||
Class.forName(text + ".Entity");
|
||||
Class classValue5 = Class.forName(text + ".EntityPlayer");
|
||||
Class classValue6 = Class.forName(text + ".ItemStack");
|
||||
Class.forName(text + ".AxisAlignedBB");
|
||||
Class classValue7 = Class.forName(text + ".BlockPosition");
|
||||
Class.forName(text + ".IBlockData");
|
||||
this.declaredField = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue5.getSuperclass().getSuperclass().getDeclaredField("activeItem")));
|
||||
this.field = classValue6.getField("a").get(null);
|
||||
this.method = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue3.getMethod("asCraftMirror", classValue6)));
|
||||
this.declaredMethod = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getCraftBlockClass().getDeclaredMethod("getNMSBlock")));
|
||||
this.declaredField2 = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(MinecraftReflection.getBlockClass().getDeclaredField("frictionFactor")));
|
||||
this.method2 = ReflectionAccess.LOOKUP.unreflect(classValue.getMethod("getHandle"));
|
||||
this.method3 = ReflectionAccess.LOOKUP.unreflect(classValue2.getMethod("getHandle"));
|
||||
this.constructor = ReflectionAccess.LOOKUP.unreflectConstructor(classValue7.getConstructor(int.class, int.class, int.class));
|
||||
this.method4 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue4.getMethod("getType", classValue7)));
|
||||
this.method5 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getIBlockDataClass().getMethod("b", MinecraftReflection.getIBlockAccessClass(), MinecraftReflection.getBlockPositionClass(), MinecraftReflection.getVoxelShapeCollisionClass())));
|
||||
this.method6 = MinecraftReflection.getVoxelShapeCollisionClass().getMethod("b").invoke(null);
|
||||
this.method7 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getNmsClass((String) "VoxelShape").getMethod("d")));
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException | InvocationTargetException | IllegalAccessException exception) {
|
||||
throw new RuntimeException("SALImplV1_16: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getActiveItem(Player player) {
|
||||
try {
|
||||
Object result = (Object) this.declaredField.invoke((Object) (Object) this.method2.invoke((Player) player));
|
||||
return Objects.equals(result, this.field) ? null : (ItemStack) this.method.invoke((Object) result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_16: getItemInUse", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getHandItems(Player player) {
|
||||
return Arrays.asList(player.getInventory().getItemInMainHand(), player.getInventory().getItemInOffHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(Player player) {
|
||||
return player.isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction(Block block) {
|
||||
try {
|
||||
return (float) this.declaredField2.invoke((Object) (Object) this.declaredMethod.invoke((Block) block));
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_16: getFrictionFactor", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox(Entity entity) {
|
||||
BoundingBox boundingBox = entity.getBoundingBox();
|
||||
return new AxisAlignedBoundingBox(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getBlockCollisionBoxes(Block block, Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method3.invoke((World) block.getWorld());
|
||||
Object result2 = (Object) this.constructor.invoke((int) block.getX(), (int) block.getY(), (int) block.getZ());
|
||||
Object result3 = (Object) this.method5.invoke((Object) (Object) this.method4.invoke((Object) result, (Object) result2), (Object) result, (Object) result2, (Object) this.method6);
|
||||
List values = (List) this.method7.invoke((Object) result3);
|
||||
ArrayList values2 = new ArrayList(values.size());
|
||||
for (Object result4 : values) {
|
||||
values2.add(LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result4));
|
||||
}
|
||||
return values2;
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_16: getCollisionShape", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
RayTraceResult rayTraceResult = entity.getWorld().rayTraceBlocks(vector.toLocation(entity.getWorld()), vector2.clone().normalize(), value);
|
||||
return BlockRayTraceResult.create(rayTraceResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockDamage(Player player, Block block) {
|
||||
return BlockBreakingHelper.getWorld(block, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package me.konsolas.aac.compat.v1_17;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Minecraft117Access implements NativeAccess {
|
||||
|
||||
@Override
|
||||
public ItemStack getActiveItem(Player player) {
|
||||
return player.getItemInUse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getHandItems(Player player) {
|
||||
return Arrays.asList(player.getInventory().getItemInMainHand(), player.getInventory().getItemInOffHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(Player player) {
|
||||
return player.isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction(Block block) {
|
||||
return block.getType().getSlipperiness();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox(Entity entity) {
|
||||
return AxisAlignedBoundingBox.fromBukkit(entity.getBoundingBox());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getBlockCollisionBoxes(Block block, Entity entity) {
|
||||
Collection<BoundingBox> boundingBoxes = block.getCollisionShape().getBoundingBoxes();
|
||||
ArrayList values = new ArrayList(boundingBoxes.size());
|
||||
for (BoundingBox box : boundingBoxes) {
|
||||
values.add(AxisAlignedBoundingBox.fromBukkit(box));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
RayTraceResult rayTraceResult = entity.getWorld().rayTraceBlocks(vector.toLocation(entity.getWorld()), vector2.clone().normalize(), value);
|
||||
return BlockRayTraceResult.create(rayTraceResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockDamage(Player player, Block block) {
|
||||
return block.getBreakSpeed(player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package me.konsolas.aac.compat.v1_8;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.nms.BlockBreakingHelper;
|
||||
import me.konsolas.aac.compat.nms.EntityRayTracer;
|
||||
import me.konsolas.aac.compat.nms.LegacyBoundingBoxHelper;
|
||||
import me.konsolas.aac.compat.nms.MinecraftReflection;
|
||||
import me.konsolas.aac.compat.nms.ReflectionAccess;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Minecraft18Access implements NativeAccess {
|
||||
|
||||
private final MethodHandle declaredMethod;
|
||||
|
||||
private final MethodHandle declaredMethod2;
|
||||
|
||||
private final MethodHandle declaredField;
|
||||
|
||||
private final MethodHandle method;
|
||||
|
||||
private final MethodHandle method2;
|
||||
|
||||
private final MethodHandle method3;
|
||||
|
||||
private final MethodHandle constructor;
|
||||
|
||||
private final MethodHandle method4;
|
||||
|
||||
private final MethodHandle method5;
|
||||
|
||||
private final MethodHandle method6;
|
||||
|
||||
public Minecraft18Access() {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String text = name.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
Class classValue = Class.forName(name + ".entity.CraftEntity");
|
||||
Class classValue2 = Class.forName(name + ".CraftWorld");
|
||||
Class classValue3 = Class.forName(text + ".World");
|
||||
Class classValue4 = Class.forName(text + ".Entity");
|
||||
Class.forName(text + ".EntityHuman");
|
||||
Class classValue5 = Class.forName(text + ".Block");
|
||||
Class classValue6 = Class.forName(text + ".AxisAlignedBB");
|
||||
Class classValue7 = Class.forName(text + ".BlockPosition");
|
||||
Class classValue8 = Class.forName(text + ".IBlockData");
|
||||
this.declaredMethod = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getEntityPlayerClass().getSuperclass().getDeclaredMethod("bS")));
|
||||
this.declaredMethod2 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getCraftBlockClass().getDeclaredMethod("getNMSBlock")));
|
||||
this.declaredField = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(MinecraftReflection.getBlockClass().getDeclaredField("frictionFactor")));
|
||||
this.method = ReflectionAccess.LOOKUP.unreflect(classValue.getMethod("getHandle"));
|
||||
this.method2 = ReflectionAccess.LOOKUP.unreflect(classValue2.getMethod("getHandle"));
|
||||
this.method3 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getEntityClass().getMethod("getBoundingBox")));
|
||||
this.constructor = ReflectionAccess.LOOKUP.unreflectConstructor(classValue7.getConstructor(int.class, int.class, int.class));
|
||||
this.method4 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue3.getMethod("getType", classValue7)));
|
||||
this.method5 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue8.getMethod("getBlock")));
|
||||
this.method6 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue5.getMethod("a", classValue3, classValue7, classValue8, classValue6, List.class, classValue4)));
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException | IllegalAccessException exception) {
|
||||
throw new RuntimeException("SALImplV1_8: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getActiveItem(Player player) {
|
||||
try {
|
||||
return (boolean) this.declaredMethod.invoke((Object) (Object) this.method.invoke((Player) player)) ? player.getItemInHand() : null;
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_8: getItemInUse", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getHandItems(Player player) {
|
||||
return Collections.singleton(player.getItemInHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction(Block block) {
|
||||
try {
|
||||
return (float) this.declaredField.invoke((Object) (Object) this.declaredMethod2.invoke((Block) block));
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_8: getFrictionFactor", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox(Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method3.invoke((Object) (Object) this.method.invoke((Entity) entity));
|
||||
return LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_8: getBoundingBox", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getBlockCollisionBoxes(Block block, Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method2.invoke((World) block.getWorld());
|
||||
Object result2 = (Object) this.constructor.invoke((int) block.getX(), (int) block.getY(), (int) block.getZ());
|
||||
Object result3 = (Object) this.method4.invoke((Object) result, (Object) result2);
|
||||
double x = block.getX();
|
||||
double y = block.getY();
|
||||
double z = block.getZ();
|
||||
double value = block.getX() + 1.0;
|
||||
double value2 = block.getZ() + 1.0;
|
||||
double value3 = block.getY() + 1.0;
|
||||
double value4 = value;
|
||||
double value5 = z;
|
||||
double value6 = y;
|
||||
double value7 = x;
|
||||
Object result4 = LegacyBoundingBoxHelper.findObject(value7, value6, value5, value4, value3, value2);
|
||||
ArrayList values = new ArrayList();
|
||||
Object result5 = (Object) this.method5.invoke((Object) result3);
|
||||
this.method6.invoke((Object) result5, (Object) result, (Object) result2, (Object) result3, (Object) result4, (List) values, (Object) (Object) this.method.invoke((Entity) entity));
|
||||
ArrayList values2 = new ArrayList(values.size());
|
||||
for (Object result6 : values) {
|
||||
z = -block.getX();
|
||||
double value8 = -block.getZ();
|
||||
double value9 = -block.getY();
|
||||
double value10 = z;
|
||||
values2.add(LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result6, value10, value9, value8));
|
||||
}
|
||||
return values2;
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_8: getCollisionShape", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
return EntityRayTracer.rayTrace(entity, vector, vector2, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockDamage(Player player, Block block) {
|
||||
return BlockBreakingHelper.getWorld(block, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package me.konsolas.aac.compat.v1_9;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import me.konsolas.aac.compat.NativeAccess;
|
||||
import me.konsolas.aac.compat.nms.BlockBreakingHelper;
|
||||
import me.konsolas.aac.compat.nms.EntityRayTracer;
|
||||
import me.konsolas.aac.compat.nms.LegacyBoundingBoxHelper;
|
||||
import me.konsolas.aac.compat.nms.MinecraftReflection;
|
||||
import me.konsolas.aac.compat.nms.ReflectionAccess;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockRayTraceResult;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Minecraft19Access implements NativeAccess {
|
||||
|
||||
private final MethodHandle declaredField;
|
||||
|
||||
private final MethodHandle method;
|
||||
|
||||
private final MethodHandle declaredMethod;
|
||||
|
||||
private final MethodHandle declaredField2;
|
||||
|
||||
private final MethodHandle method2;
|
||||
|
||||
private final MethodHandle method3;
|
||||
|
||||
private final MethodHandle method4;
|
||||
|
||||
private final MethodHandle constructor;
|
||||
|
||||
private final MethodHandle method5;
|
||||
|
||||
private final MethodHandle method6;
|
||||
|
||||
public Minecraft19Access(ProtocolVersion version) {
|
||||
if (version != ProtocolVersion.V1_9 && version != ProtocolVersion.V1_10) {
|
||||
throw new IllegalArgumentException("Invalid version for SALImplV1_9_10: " + version);
|
||||
} else {
|
||||
try {
|
||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String text = name.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
Class classValue = Class.forName(name + ".entity.CraftEntity");
|
||||
Class classValue2 = Class.forName(name + ".CraftWorld");
|
||||
Class classValue3 = Class.forName(name + ".inventory.ItemStack");
|
||||
Class classValue4 = Class.forName(text + ".World");
|
||||
Class classValue5 = Class.forName(text + ".Entity");
|
||||
Class classValue6 = Class.forName(text + ".EntityPlayer");
|
||||
Class classValue7 = Class.forName(text + ".ItemStack");
|
||||
Class classValue8 = Class.forName(text + ".AxisAlignedBB");
|
||||
Class classValue9 = Class.forName(text + ".BlockPosition");
|
||||
Class classValue10 = Class.forName(text + ".IBlockData");
|
||||
this.declaredField = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(classValue6.getSuperclass().getSuperclass().getDeclaredField(version == ProtocolVersion.V1_9 ? "bn" : "bo")));
|
||||
this.method = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue3.getMethod("asCraftMirror", classValue7)));
|
||||
this.declaredMethod = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getCraftBlockClass().getDeclaredMethod("getNMSBlock")));
|
||||
this.declaredField2 = ReflectionAccess.LOOKUP.unreflectGetter((Field) ReflectionAccess.makeAccessible(MinecraftReflection.getBlockClass().getDeclaredField("frictionFactor")));
|
||||
this.method2 = ReflectionAccess.LOOKUP.unreflect(classValue.getMethod("getHandle"));
|
||||
this.method3 = ReflectionAccess.LOOKUP.unreflect(classValue2.getMethod("getHandle"));
|
||||
this.method4 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(MinecraftReflection.getEntityClass().getMethod("getBoundingBox")));
|
||||
this.constructor = ReflectionAccess.LOOKUP.unreflectConstructor(classValue9.getConstructor(int.class, int.class, int.class));
|
||||
this.method5 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue4.getMethod("getType", classValue9)));
|
||||
this.method6 = ReflectionAccess.LOOKUP.unreflect((Method) ReflectionAccess.makeAccessible(classValue10.getMethod("a", classValue4, classValue9, classValue8, List.class, classValue5)));
|
||||
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException | IllegalAccessException exception) {
|
||||
throw new RuntimeException("SALImplV1_9_10: <clinit>", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getActiveItem(Player player) {
|
||||
try {
|
||||
Object result = (Object) this.declaredField.invoke((Object) (Object) this.method2.invoke((Player) player));
|
||||
return result == null ? null : (ItemStack) this.method.invoke((Object) result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_9_10: getItemInUse", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getHandItems(Player player) {
|
||||
return Arrays.asList(player.getInventory().getItemInMainHand(), player.getInventory().getItemInOffHand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGliding(Player player) {
|
||||
return player.isGliding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction(Block block) {
|
||||
try {
|
||||
return (float) this.declaredField2.invoke((Object) (Object) this.declaredMethod.invoke((Block) block));
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_9_10: getFrictionFactor", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox(Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method4.invoke((Object) (Object) this.method2.invoke((Entity) entity));
|
||||
return LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result);
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_9_10: getBoundingBox", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getBlockCollisionBoxes(Block block, Entity entity) {
|
||||
try {
|
||||
Object result = (Object) this.method3.invoke((World) block.getWorld());
|
||||
Object result2 = (Object) this.constructor.invoke((int) block.getX(), (int) block.getY(), (int) block.getZ());
|
||||
double x = block.getX();
|
||||
double y = block.getY();
|
||||
double z = block.getZ();
|
||||
double value = block.getX() + 1.0;
|
||||
double value2 = block.getZ() + 1.0;
|
||||
double value3 = block.getY() + 1.0;
|
||||
double value4 = value;
|
||||
double value5 = z;
|
||||
double value6 = y;
|
||||
double value7 = x;
|
||||
Object result3 = LegacyBoundingBoxHelper.findObject(value7, value6, value5, value4, value3, value2);
|
||||
ArrayList values = new ArrayList();
|
||||
this.method6.invoke((Object) (Object) this.method5.invoke((Object) result, (Object) result2), (Object) result, (Object) result2, (Object) result3, (List) values, (Object) (Object) this.method2.invoke((Entity) entity));
|
||||
ArrayList values2 = new ArrayList(values.size());
|
||||
for (Object result4 : values) {
|
||||
z = -block.getX();
|
||||
double value8 = -block.getZ();
|
||||
double value9 = -block.getY();
|
||||
double value10 = z;
|
||||
values2.add(LegacyBoundingBoxHelper.findAxisAlignedBoundingBox(result4, value10, value9, value8));
|
||||
}
|
||||
return values2;
|
||||
} catch (Throwable throwable) {
|
||||
throw new RuntimeException("SALImplV1_9_10: getCollisionShape", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRayTraceResult rayTrace(Entity entity, Vector vector, Vector vector2, double value) {
|
||||
return EntityRayTracer.rayTrace(entity, vector, vector2, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockDamage(Player player, Block block) {
|
||||
return BlockBreakingHelper.getWorld(block, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package me.konsolas.aac.database;
|
||||
|
||||
import java.sql.Array;
|
||||
import java.sql.Blob;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.NClob;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLClientInfoException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.SQLXML;
|
||||
import java.sql.Savepoint;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Struct;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
class ConnectionDelegate implements Connection {
|
||||
|
||||
private final Connection delegate;
|
||||
|
||||
ConnectionDelegate(Connection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement() throws SQLException {
|
||||
return this.delegate.createStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String text) throws SQLException {
|
||||
return this.delegate.prepareStatement(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String text) throws SQLException {
|
||||
return this.delegate.prepareCall(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nativeSQL(String text) throws SQLException {
|
||||
return this.delegate.nativeSQL(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoCommit(boolean matches) throws SQLException {
|
||||
this.delegate.setAutoCommit(matches);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAutoCommit() throws SQLException {
|
||||
return this.delegate.getAutoCommit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
this.delegate.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
this.delegate.rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return this.delegate.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
return this.delegate.getMetaData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean matches) throws SQLException {
|
||||
this.delegate.setReadOnly(matches);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() throws SQLException {
|
||||
return this.delegate.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCatalog(String text) throws SQLException {
|
||||
this.delegate.setCatalog(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCatalog() throws SQLException {
|
||||
return this.delegate.getCatalog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionIsolation(int value) throws SQLException {
|
||||
this.delegate.setTransactionIsolation(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransactionIsolation() throws SQLException {
|
||||
return this.delegate.getTransactionIsolation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
return this.delegate.getWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
this.delegate.clearWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int value, int value2) throws SQLException {
|
||||
return this.delegate.createStatement(value, value2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String text, int value, int value2) throws SQLException {
|
||||
return this.delegate.prepareStatement(text, value, value2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String text, int value, int value2) throws SQLException {
|
||||
return this.delegate.prepareCall(text, value, value2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getTypeMap() throws SQLException {
|
||||
return this.delegate.getTypeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeMap(Map entries) throws SQLException {
|
||||
this.delegate.setTypeMap(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHoldability(int value) throws SQLException {
|
||||
this.delegate.setHoldability(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHoldability() throws SQLException {
|
||||
return this.delegate.getHoldability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint() throws SQLException {
|
||||
return this.delegate.setSavepoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint(String text) throws SQLException {
|
||||
return this.delegate.setSavepoint(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Savepoint savepoint) throws SQLException {
|
||||
this.delegate.rollback(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
|
||||
this.delegate.releaseSavepoint(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int value, int value2, int value3) throws SQLException {
|
||||
return this.delegate.createStatement(value, value2, value3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String text, int value, int value2, int value3) throws SQLException {
|
||||
return this.delegate.prepareStatement(text, value, value2, value3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String text, int value, int value2, int value3) throws SQLException {
|
||||
return this.delegate.prepareCall(text, value, value2, value3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String text, int value) throws SQLException {
|
||||
return this.delegate.prepareStatement(text, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String text, int[] values) throws SQLException {
|
||||
return this.delegate.prepareStatement(text, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String text, String[] texts) throws SQLException {
|
||||
return this.delegate.prepareStatement(text, texts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clob createClob() throws SQLException {
|
||||
return this.delegate.createClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob createBlob() throws SQLException {
|
||||
return this.delegate.createBlob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NClob createNClob() throws SQLException {
|
||||
return this.delegate.createNClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLXML createSQLXML() throws SQLException {
|
||||
return this.delegate.createSQLXML();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int value) throws SQLException {
|
||||
return this.delegate.isValid(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(String text, String text2) throws SQLClientInfoException {
|
||||
this.delegate.setClientInfo(text, text2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(Properties properties) throws SQLClientInfoException {
|
||||
this.delegate.setClientInfo(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientInfo(String text) throws SQLException {
|
||||
return this.delegate.getClientInfo(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getClientInfo() throws SQLException {
|
||||
return this.delegate.getClientInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array createArrayOf(String text, Object[] results) throws SQLException {
|
||||
return this.delegate.createArrayOf(text, results);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Struct createStruct(String text, Object[] results) throws SQLException {
|
||||
return this.delegate.createStruct(text, results);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSchema(String text) throws SQLException {
|
||||
this.delegate.setSchema(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchema() throws SQLException {
|
||||
return this.delegate.getSchema();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(Executor executor) throws SQLException {
|
||||
this.delegate.abort(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeout(Executor executor, int value) throws SQLException {
|
||||
this.delegate.setNetworkTimeout(executor, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNetworkTimeout() throws SQLException {
|
||||
return this.delegate.getNetworkTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrap(Class classValue) throws SQLException {
|
||||
return this.delegate.unwrap(classValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class classValue) throws SQLException {
|
||||
return this.delegate.isWrapperFor(classValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package me.konsolas.aac.database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Supplier;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
class ConnectionProvider implements AutoCloseable {
|
||||
|
||||
private final Supplier connectionFactory;
|
||||
|
||||
private final DatabaseDialect dialect;
|
||||
|
||||
private Connection connection;
|
||||
|
||||
ConnectionProvider(Supplier connectionFactory, DatabaseDialect dialect) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.dialect = dialect;
|
||||
this.connection = (Connection) connectionFactory.get();
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException {
|
||||
if (this.connection == null || !this.connection.isValid(30)) {
|
||||
Bukkit.getLogger().info("[AAC5] Reconnecting to database...");
|
||||
this.connection = (Connection) this.connectionFactory.get();
|
||||
}
|
||||
return new ConnectionDelegate(this.connection);
|
||||
}
|
||||
|
||||
public DatabaseDialect getDialect() {
|
||||
return this.dialect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
this.connection.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package me.konsolas.aac.database;
|
||||
|
||||
public enum DatabaseDialect {
|
||||
|
||||
SQLITE, MYSQL
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.konsolas.aac.database;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
|
||||
class DatabaseWriter implements Runnable {
|
||||
|
||||
final AAC plugin;
|
||||
|
||||
final PlayerDatabase database;
|
||||
|
||||
DatabaseWriter(PlayerDatabase database, AAC plugin) {
|
||||
this.database = database;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ArrayList values = new ArrayList();
|
||||
while (PlayerDatabase.checkPlayerDatabase(this.database)) {
|
||||
values.add((FeatureEvent) PlayerDatabase.findBlockingQueue(this.database).take());
|
||||
PlayerDatabase.findBlockingQueue(this.database).drainTo(values);
|
||||
try {
|
||||
PlayerDatabase playerDatabase = this.database;
|
||||
PlayerDatabase.updatePlayerDatabase(playerDatabase, values);
|
||||
} catch (SQLException sQLException) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Failed to save events to database", (Throwable) sQLException);
|
||||
}
|
||||
values.clear();
|
||||
Thread.sleep(1000L);
|
||||
}
|
||||
} catch (InterruptedException interruptedException) {
|
||||
this.plugin.getLogger().info(PlayerDatabase.findThread(this.database).getName() + " exiting");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
package me.konsolas.aac.database;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.analysis.feature.FeatureEvent;
|
||||
import me.konsolas.aac.analysis.feature.FeatureType;
|
||||
import me.konsolas.aac.player.PlayerLoginInfo;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PlayerDatabase implements Closeable {
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
private final ConnectionProvider connectionProvider;
|
||||
|
||||
private final BlockingQueue eventQueue = new LinkedBlockingQueue();
|
||||
|
||||
private final Thread writerThread;
|
||||
|
||||
private volatile boolean closed = true;
|
||||
|
||||
private PlayerDatabase(AAC plugin, ConnectionProvider connectionProvider) throws SQLException {
|
||||
this.plugin = plugin;
|
||||
this.connectionProvider = connectionProvider;
|
||||
this.tick();
|
||||
this.writerThread = new Thread(new DatabaseWriter(this, plugin), "AAC Database Thread");
|
||||
this.writerThread.start();
|
||||
}
|
||||
|
||||
public static PlayerDatabase create(AAC aacPlugin, String text) throws SQLException {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
throw new RuntimeException(classNotFoundException);
|
||||
}
|
||||
Supplier supplier = () -> {
|
||||
try {
|
||||
return DriverManager.getConnection("jdbc:sqlite:" + text);
|
||||
} catch (SQLException sQLException) {
|
||||
aacPlugin.getLogger().log(Level.SEVERE, "Could not connect to SQLite database", (Throwable) sQLException);
|
||||
throw new RuntimeException(sQLException);
|
||||
}
|
||||
};
|
||||
DatabaseDialect databaseDialect = DatabaseDialect.SQLITE;
|
||||
Supplier supplier2 = supplier;
|
||||
ConnectionProvider connectionProvider = new ConnectionProvider(supplier2, databaseDialect);
|
||||
ConnectionProvider connectionProvider2 = connectionProvider;
|
||||
return new PlayerDatabase(aacPlugin, connectionProvider2);
|
||||
}
|
||||
|
||||
public static PlayerDatabase create(AAC aacPlugin, String text, int value, String text2, String text3, String text4) throws SQLException {
|
||||
try {
|
||||
Class.forName("com.mysql.jdbc.Driver");
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
throw new RuntimeException(classNotFoundException);
|
||||
}
|
||||
String text5 = "jdbc:mysql://" + text + ":" + value + "/" + text2;
|
||||
Supplier supplier = () -> {
|
||||
try {
|
||||
return DriverManager.getConnection(text5, text3, text4);
|
||||
} catch (SQLException sQLException) {
|
||||
aacPlugin.getLogger().log(Level.SEVERE, "Could not connect to MySQL database", (Throwable) sQLException);
|
||||
throw new RuntimeException(sQLException);
|
||||
}
|
||||
};
|
||||
DatabaseDialect databaseDialect = DatabaseDialect.MYSQL;
|
||||
Supplier supplier2 = supplier;
|
||||
ConnectionProvider connectionProvider = new ConnectionProvider(supplier2, databaseDialect);
|
||||
ConnectionProvider connectionProvider2 = connectionProvider;
|
||||
return new PlayerDatabase(aacPlugin, connectionProvider2);
|
||||
}
|
||||
|
||||
private void tick() throws SQLException {
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
try {
|
||||
Statement statement = connection.createStatement();
|
||||
try {
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS `aac_events` ( `time` DATETIME NOT NULL, `uuid` BINARY(16) NOT NULL, `type` INT NOT NULL, `data` FLOAT NOT NULL);");
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS `aac_players` ( `uuid` BINARY(16) PRIMARY KEY, `join` DATETIME NOT NULL, `addr` VARCHAR(45) NOT NULL, `addr_time` DATETIME NOT NULL, `exempt` BOOLEAN NOT NULL);");
|
||||
} catch (Throwable throwable) {
|
||||
if (statement != null) {
|
||||
try {
|
||||
statement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (statement != null) {
|
||||
statement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
try {
|
||||
connection = this.connectionProvider.getConnection();
|
||||
try {
|
||||
Statement statement2 = connection.createStatement();
|
||||
try {
|
||||
statement2.execute("CREATE INDEX `time_uuid_idx` ON `aac_events` (`time`, `uuid`)");
|
||||
} catch (Throwable throwable5) {
|
||||
if (statement2 != null) {
|
||||
try {
|
||||
statement2.close();
|
||||
} catch (Throwable throwable6) {
|
||||
throwable5.addSuppressed(throwable6);
|
||||
}
|
||||
}
|
||||
throw throwable5;
|
||||
}
|
||||
if (statement2 != null) {
|
||||
statement2.close();
|
||||
}
|
||||
} catch (Throwable throwable7) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable8) {
|
||||
throwable7.addSuppressed(throwable8);
|
||||
}
|
||||
}
|
||||
throw throwable7;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
} catch (SQLException sQLException) {
|
||||
}
|
||||
}
|
||||
|
||||
public int deleteEventsBefore(long value) throws SQLException {
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
int value2;
|
||||
try {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("DELETE FROM `aac_events` WHERE `time` < ?");
|
||||
try {
|
||||
preparedStatement.setTimestamp(1, new Timestamp(value));
|
||||
value2 = preparedStatement.executeUpdate();
|
||||
} catch (Throwable throwable) {
|
||||
if (preparedStatement != null) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
return value2;
|
||||
}
|
||||
|
||||
public void recordFeature(Player player, FeatureType feature, float value) {
|
||||
FeatureEvent event = new FeatureEvent(System.currentTimeMillis(), player.getUniqueId(), feature, value);
|
||||
this.eventQueue.add(event);
|
||||
this.plugin.getAnalysisManager().updatePlayerWithAlertRecipients(player);
|
||||
}
|
||||
|
||||
public CompletableFuture<Integer> resetPlayerAsync(OfflinePlayer offlinePlayer) {
|
||||
UUID uniqueId = offlinePlayer.getUniqueId();
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
Integer value;
|
||||
try {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("DELETE FROM `aac_events` WHERE `uuid` = ?");
|
||||
try {
|
||||
preparedStatement.setBytes(1, encodeUuid(uniqueId));
|
||||
value = preparedStatement.executeUpdate();
|
||||
} catch (Throwable throwable) {
|
||||
if (preparedStatement != null) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
return value;
|
||||
} catch (SQLException sQLException) {
|
||||
throw new CompletionException(sQLException);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateList(List<FeatureEvent> values) throws SQLException {
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
try {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO `aac_events`(`time`, `uuid`, `type`, `data`) VALUES (?, ?, ?, ?);");
|
||||
try {
|
||||
for (FeatureEvent event : values) {
|
||||
preparedStatement.setObject(1, new Timestamp(event.getTimestamp()));
|
||||
UUID playerId = event.getPlayerId();
|
||||
UUID uUID = playerId;
|
||||
preparedStatement.setBytes(2, encodeUuid(uUID));
|
||||
preparedStatement.setInt(3, event.getType().getId());
|
||||
preparedStatement.setFloat(4, event.getValue());
|
||||
preparedStatement.addBatch();
|
||||
}
|
||||
connection.setAutoCommit(false);
|
||||
preparedStatement.executeBatch();
|
||||
connection.setAutoCommit(true);
|
||||
} catch (Throwable throwable) {
|
||||
if (preparedStatement != null) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerLoginInfo findPlayerLoginInfo(UUID uUID) throws SQLException {
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
PlayerLoginInfo playerLoginInfo;
|
||||
decision: {
|
||||
try {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT `join`, `addr`, `addr_time`, `exempt` FROM `aac_players` WHERE `uuid` = ?");
|
||||
decision2: {
|
||||
try {
|
||||
preparedStatement.setBytes(1, encodeUuid(uUID));
|
||||
ResultSet resultSet = preparedStatement.executeQuery();
|
||||
if (!resultSet.next()) {
|
||||
playerLoginInfo = null;
|
||||
break decision2;
|
||||
}
|
||||
playerLoginInfo = new PlayerLoginInfo(uUID, resultSet.getTimestamp(1).getTime(), resultSet.getString(2), resultSet.getTimestamp(3).getTime(), resultSet.getBoolean(4));
|
||||
} catch (Throwable throwable) {
|
||||
if (preparedStatement != null) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
break decision;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
return playerLoginInfo;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
return playerLoginInfo;
|
||||
}
|
||||
|
||||
public PlayerLoginInfo findPlayerLoginInfo(UUID uUID, InetAddress inetAddress) throws SQLException {
|
||||
long now = System.currentTimeMillis();
|
||||
String hostAddress = inetAddress.getHostAddress();
|
||||
PlayerLoginInfo playerLoginInfo = this.findPlayerLoginInfo(uUID);
|
||||
if (playerLoginInfo == null) {
|
||||
playerLoginInfo = new PlayerLoginInfo(uUID, now, hostAddress, now, false);
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
try {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO `aac_players`(`uuid`, `join`, `addr`, `addr_time`, `exempt`) VALUES (?, ?, ?, ?, ?)");
|
||||
try {
|
||||
preparedStatement.setBytes(1, encodeUuid(uUID));
|
||||
preparedStatement.setTimestamp(2, new Timestamp(playerLoginInfo.getFirstSeen()));
|
||||
preparedStatement.setString(3, playerLoginInfo.getAddress());
|
||||
preparedStatement.setTimestamp(4, new Timestamp(playerLoginInfo.getLastSeen()));
|
||||
preparedStatement.setBoolean(5, playerLoginInfo.isExempt());
|
||||
preparedStatement.executeUpdate();
|
||||
} catch (Throwable throwable) {
|
||||
if (preparedStatement != null) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
} else if (!playerLoginInfo.getAddress().equals(hostAddress)) {
|
||||
playerLoginInfo = new PlayerLoginInfo(uUID, playerLoginInfo.getFirstSeen(), hostAddress, now, playerLoginInfo.isExempt());
|
||||
Connection connection2 = this.connectionProvider.getConnection();
|
||||
try {
|
||||
PreparedStatement preparedStatement2 = connection2.prepareStatement("UPDATE `aac_players` SET `addr` = ?, `addr_time` = ? WHERE `uuid` = ?");
|
||||
try {
|
||||
preparedStatement2.setString(1, playerLoginInfo.getAddress());
|
||||
preparedStatement2.setTimestamp(2, new Timestamp(playerLoginInfo.getLastSeen()));
|
||||
preparedStatement2.setBytes(3, encodeUuid(uUID));
|
||||
preparedStatement2.executeUpdate();
|
||||
} catch (Throwable throwable5) {
|
||||
if (preparedStatement2 != null) {
|
||||
try {
|
||||
preparedStatement2.close();
|
||||
} catch (Throwable throwable6) {
|
||||
throwable5.addSuppressed(throwable6);
|
||||
}
|
||||
}
|
||||
throw throwable5;
|
||||
}
|
||||
if (preparedStatement2 != null) {
|
||||
preparedStatement2.close();
|
||||
}
|
||||
} catch (Throwable throwable7) {
|
||||
if (connection2 != null) {
|
||||
try {
|
||||
connection2.close();
|
||||
} catch (Throwable throwable8) {
|
||||
throwable7.addSuppressed(throwable8);
|
||||
}
|
||||
}
|
||||
throw throwable7;
|
||||
}
|
||||
if (connection2 != null) {
|
||||
connection2.close();
|
||||
}
|
||||
}
|
||||
return playerLoginInfo;
|
||||
}
|
||||
|
||||
public void setExempt(UUID uUID, boolean matches) throws SQLException {
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
try {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("UPDATE `aac_players` SET `exempt` = ? WHERE `uuid` = ?");
|
||||
try {
|
||||
preparedStatement.setBoolean(1, matches);
|
||||
preparedStatement.setBytes(2, encodeUuid(uUID));
|
||||
preparedStatement.executeUpdate();
|
||||
} catch (Throwable throwable) {
|
||||
if (preparedStatement != null) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public List getEventsSince(long value, UUID uUID) throws SQLException {
|
||||
Connection connection = this.connectionProvider.getConnection();
|
||||
ArrayList values;
|
||||
try {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT `time`, `type`, `data` FROM `aac_events` WHERE `time` > ? AND `uuid` = ?");
|
||||
try {
|
||||
preparedStatement.setObject(1, new Timestamp(value));
|
||||
preparedStatement.setBytes(2, encodeUuid(uUID));
|
||||
ResultSet resultSet = preparedStatement.executeQuery();
|
||||
ArrayList values2 = new ArrayList();
|
||||
while (resultSet.next()) {
|
||||
long time = resultSet.getTimestamp(1).getTime();
|
||||
int intValue = resultSet.getInt(2);
|
||||
values2.add(new FeatureEvent(time, uUID, FeatureType.fromId(intValue), resultSet.getFloat(3)));
|
||||
}
|
||||
values = values2;
|
||||
} catch (Throwable throwable) {
|
||||
if (preparedStatement != null) {
|
||||
try {
|
||||
preparedStatement.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private static byte[] encodeUuid(UUID uUID) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
|
||||
byteBuffer.putLong(uUID.getMostSignificantBits());
|
||||
byteBuffer.putLong(uUID.getLeastSignificantBits());
|
||||
return byteBuffer.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
this.closed = false;
|
||||
this.writerThread.join();
|
||||
} catch (InterruptedException interruptedException) {
|
||||
this.plugin.getLogger().warning("Interrupted while closing database connection");
|
||||
}
|
||||
try {
|
||||
this.connectionProvider.close();
|
||||
} catch (Exception exception) {
|
||||
this.plugin.getLogger().log(Level.WARNING, "Could not close connection", (Throwable) exception);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean checkPlayerDatabase(PlayerDatabase playerDatabase) {
|
||||
return playerDatabase.closed;
|
||||
}
|
||||
|
||||
static BlockingQueue findBlockingQueue(PlayerDatabase playerDatabase) {
|
||||
return playerDatabase.eventQueue;
|
||||
}
|
||||
|
||||
static void updatePlayerDatabase(PlayerDatabase playerDatabase, List values) throws SQLException {
|
||||
playerDatabase.updateList(values);
|
||||
}
|
||||
|
||||
static Thread findThread(PlayerDatabase playerDatabase) {
|
||||
return playerDatabase.writerThread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package me.konsolas.aac.debug;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import me.konsolas.aac.AAC;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class DebugManager {
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final Map<UUID, Set<CommandSender>> debugSubscriptions = new ConcurrentHashMap();
|
||||
|
||||
private CommandSender consoleSubscriber = null;
|
||||
|
||||
public DebugManager(AAC plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean isDebugging(Player player) {
|
||||
if (this.consoleSubscriber != null) {
|
||||
return true;
|
||||
} else {
|
||||
Set values = (Set) this.debugSubscriptions.get(player.getUniqueId());
|
||||
return values != null && !values.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public void log(Player player, String text) {
|
||||
long now = System.currentTimeMillis();
|
||||
this.executor.execute(() -> this.writeLog(player, text, now));
|
||||
}
|
||||
|
||||
private void writeLog(Player player, String text, long value) {
|
||||
Set<CommandSender> values = (Set) this.debugSubscriptions.get(player.getUniqueId());
|
||||
if (this.consoleSubscriber != null) {
|
||||
if (values == null) {
|
||||
values = Collections.singleton(this.consoleSubscriber);
|
||||
} else {
|
||||
values.add(this.consoleSubscriber);
|
||||
}
|
||||
}
|
||||
if (values != null) {
|
||||
String text2 = Long.toString(value);
|
||||
text = "" + ChatColor.DARK_GRAY + text2.substring(text2.length() - 4) + " " + ChatColor.DARK_GREEN + player.getName() + " " + ChatColor.GRAY + text;
|
||||
for (CommandSender sender : values) {
|
||||
sender.sendMessage(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePlayer(Player player, String text, Object[] results) {
|
||||
this.log(player, String.format(Locale.UK, text, results));
|
||||
}
|
||||
|
||||
public void updateCommandSender(CommandSender sender) {
|
||||
if (this.consoleSubscriber == sender) {
|
||||
this.consoleSubscriber = null;
|
||||
} else {
|
||||
this.consoleSubscriber = sender;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkDebugManager(CommandSender sender) {
|
||||
return sender == this.consoleSubscriber;
|
||||
}
|
||||
|
||||
public void updateCommandSender(CommandSender sender, OfflinePlayer offlinePlayer) {
|
||||
Set values = this.debugSubscriptions.computeIfAbsent(offlinePlayer.getUniqueId(), result -> new HashSet());
|
||||
if (values.remove(sender)) {
|
||||
if (values.isEmpty()) {
|
||||
this.debugSubscriptions.remove(offlinePlayer.getUniqueId());
|
||||
}
|
||||
} else {
|
||||
values.add(sender);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<OfflinePlayer> findSet(CommandSender sender) {
|
||||
HashSet values = new HashSet();
|
||||
for (Entry entry : (Iterable<Entry>) (Iterable<?>) (this.debugSubscriptions.entrySet())) {
|
||||
if (((Set) entry.getValue()).contains(sender)) {
|
||||
values.add(Bukkit.getOfflinePlayer((UUID) entry.getKey()));
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package me.konsolas.aac.metrics;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Timer;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
public class MetricsLite {
|
||||
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
private static boolean logSentData;
|
||||
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
private static String serverUUID;
|
||||
|
||||
public static String userID = "73079";
|
||||
|
||||
public static String nonceID = "-2134699790";
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
private final int pluginId;
|
||||
|
||||
public MetricsLite(Plugin plugin, int pluginId) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
} else {
|
||||
this.plugin = plugin;
|
||||
this.pluginId = pluginId;
|
||||
File file = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File file2 = new File(file, "config.yml");
|
||||
YamlConfiguration yamlConfiguration = YamlConfiguration.loadConfiguration(file2);
|
||||
if (!yamlConfiguration.isSet("serverUuid")) {
|
||||
yamlConfiguration.addDefault("enabled", true);
|
||||
yamlConfiguration.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
yamlConfiguration.addDefault("logFailedRequests", false);
|
||||
yamlConfiguration.addDefault("logSentData", false);
|
||||
yamlConfiguration.addDefault("logResponseStatusText", false);
|
||||
yamlConfiguration.options().header("bStats collects some data for plugin authors like how many servers are using their plugins.\nTo honor their work, you should not disable it.\nThis has nearly no effect on the server performance!\nCheck out https://bStats.org/ to learn more :)").copyDefaults(true);
|
||||
try {
|
||||
yamlConfiguration.save(file2);
|
||||
} catch (IOException iOException) {
|
||||
}
|
||||
}
|
||||
serverUUID = yamlConfiguration.getString("serverUuid");
|
||||
logFailedRequests = yamlConfiguration.getBoolean("logFailedRequests", false);
|
||||
this.enabled = yamlConfiguration.getBoolean("enabled", true);
|
||||
logSentData = yamlConfiguration.getBoolean("logSentData", false);
|
||||
logResponseStatusText = yamlConfiguration.getBoolean("logResponseStatusText", false);
|
||||
if (this.enabled) {
|
||||
boolean matches = false;
|
||||
for (Class classValue : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
classValue.getField("B_STATS_VERSION");
|
||||
matches = true;
|
||||
break;
|
||||
} catch (NoSuchFieldException noSuchFieldException) {
|
||||
}
|
||||
}
|
||||
Bukkit.getServicesManager().register(MetricsLite.class, this, plugin, ServicePriority.Normal);
|
||||
if (!matches) {
|
||||
this.startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
private void startSubmitting() {
|
||||
Timer timer = new Timer(true);
|
||||
timer.scheduleAtFixedRate(new MetricsSubmissionTask(this, timer), 300000L, 1800000L);
|
||||
}
|
||||
|
||||
public JsonObject getPluginData() {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
String name = this.plugin.getDescription().getName();
|
||||
String version = this.plugin.getDescription().getVersion();
|
||||
jsonObject.addProperty("pluginName", name);
|
||||
jsonObject.addProperty("id", this.pluginId);
|
||||
jsonObject.addProperty("pluginVersion", version);
|
||||
jsonObject.add("customCharts", new JsonArray());
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
private JsonObject getServerData() {
|
||||
int value;
|
||||
try {
|
||||
Method method = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
value = method.getReturnType().equals(Collection.class) ? ((Collection) method.invoke(Bukkit.getServer())).size() : ((Player[]) method.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception exception) {
|
||||
value = Bukkit.getOnlinePlayers().size();
|
||||
}
|
||||
int value2 = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
String version = Bukkit.getVersion();
|
||||
String name = Bukkit.getName();
|
||||
String property = System.getProperty("java.version");
|
||||
String property2 = System.getProperty("os.name");
|
||||
String property3 = System.getProperty("os.arch");
|
||||
String property4 = System.getProperty("os.version");
|
||||
int value3 = Runtime.getRuntime().availableProcessors();
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("serverUUID", serverUUID);
|
||||
jsonObject.addProperty("playerAmount", value);
|
||||
jsonObject.addProperty("onlineMode", value2);
|
||||
jsonObject.addProperty("bukkitVersion", version);
|
||||
jsonObject.addProperty("bukkitName", name);
|
||||
jsonObject.addProperty("javaVersion", property);
|
||||
jsonObject.addProperty("osName", property2);
|
||||
jsonObject.addProperty("osArch", property3);
|
||||
jsonObject.addProperty("osVersion", property4);
|
||||
jsonObject.addProperty("coreCount", value3);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
private void submitData() {
|
||||
JsonObject serverData = this.getServerData();
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
for (Class classValue : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
classValue.getField("B_STATS_VERSION");
|
||||
for (RegisteredServiceProvider registeredServiceProvider : (Iterable<RegisteredServiceProvider>) (Iterable<?>) (Bukkit.getServicesManager().getRegistrations(classValue))) {
|
||||
try {
|
||||
Object result = registeredServiceProvider.getService().getMethod("getPluginData").invoke(registeredServiceProvider.getProvider());
|
||||
if (result instanceof JsonObject) {
|
||||
jsonArray.add((JsonObject) result);
|
||||
} else {
|
||||
try {
|
||||
Class classValue2 = Class.forName("org.json.simple.JSONObject");
|
||||
if (result.getClass().isAssignableFrom(classValue2)) {
|
||||
Method declaredMethod = classValue2.getDeclaredMethod("toJSONString");
|
||||
declaredMethod.setAccessible(true);
|
||||
String text = (String) declaredMethod.invoke(result);
|
||||
JsonObject asJsonObject = new JsonParser().parse(text).getAsJsonObject();
|
||||
jsonArray.add(asJsonObject);
|
||||
}
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
if (logFailedRequests) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", (Throwable) classNotFoundException);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NullPointerException exception) {
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException noSuchFieldException) {
|
||||
}
|
||||
}
|
||||
serverData.add("plugins", jsonArray);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
sendData(this.plugin, serverData);
|
||||
} catch (Exception exception2) {
|
||||
if (logFailedRequests) {
|
||||
this.plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + this.plugin.getName(), (Throwable) exception2);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private static void sendData(Plugin plugin, JsonObject jsonObject) throws MalformedURLException, IOException, java.lang.IllegalAccessException {
|
||||
if (jsonObject == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
} else if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
} else {
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + jsonObject);
|
||||
}
|
||||
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) new URL("https://bStats.org/submitData/bukkit").openConnection();
|
||||
byte[] values = compress(jsonObject.toString());
|
||||
httpsURLConnection.setRequestMethod("POST");
|
||||
httpsURLConnection.addRequestProperty("Accept", "application/json");
|
||||
httpsURLConnection.addRequestProperty("Connection", "close");
|
||||
httpsURLConnection.addRequestProperty("Content-Encoding", "gzip");
|
||||
httpsURLConnection.addRequestProperty("Content-Length", String.valueOf(values.length));
|
||||
httpsURLConnection.setRequestProperty("Content-Type", "application/json");
|
||||
httpsURLConnection.setRequestProperty("User-Agent", "MC-Server/1");
|
||||
httpsURLConnection.setDoOutput(true);
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(httpsURLConnection.getOutputStream());
|
||||
try {
|
||||
dataOutputStream.write(values);
|
||||
} catch (Throwable throwable) {
|
||||
try {
|
||||
dataOutputStream.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
dataOutputStream.close();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream()));
|
||||
String text;
|
||||
try {
|
||||
while ((text = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(text);
|
||||
}
|
||||
} catch (Throwable throwable3) {
|
||||
try {
|
||||
bufferedReader.close();
|
||||
} catch (Throwable throwable4) {
|
||||
throwable3.addSuppressed(throwable4);
|
||||
}
|
||||
throw throwable3;
|
||||
}
|
||||
bufferedReader.close();
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + stringBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] compress(String text) throws IOException {
|
||||
if (text == null) {
|
||||
return null;
|
||||
} else {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gZIPOutputStream = new GZIPOutputStream(byteArrayOutputStream);
|
||||
try {
|
||||
gZIPOutputStream.write(text.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Throwable throwable) {
|
||||
try {
|
||||
gZIPOutputStream.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
gZIPOutputStream.close();
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
static Plugin access$000(MetricsLite metricsLite) {
|
||||
return metricsLite.plugin;
|
||||
}
|
||||
|
||||
static void access$100(MetricsLite metricsLite) {
|
||||
metricsLite.submitData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.konsolas.aac.metrics;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
class MetricsSubmissionTask extends TimerTask {
|
||||
|
||||
final Timer timer;
|
||||
|
||||
final MetricsLite metrics;
|
||||
|
||||
MetricsSubmissionTask(MetricsLite metrics, Timer timer) {
|
||||
this.metrics = metrics;
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!MetricsLite.access$000(this.metrics).isEnabled()) {
|
||||
this.timer.cancel();
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(MetricsLite.access$000(this.metrics), this::lambda$run$0);
|
||||
}
|
||||
}
|
||||
|
||||
private void lambda$run$0() {
|
||||
MetricsLite.access$100(this.metrics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import me.konsolas.aac.compat.NativeAccessProvider;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
public class EntityDimensions {
|
||||
|
||||
public static final EntityDimensions DEFAULT = new EntityDimensions(0.6, 1.8);
|
||||
|
||||
private final double width;
|
||||
|
||||
private final double height;
|
||||
|
||||
public EntityDimensions(double width, double height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public static EntityDimensions create(Entity entity) {
|
||||
AxisAlignedBoundingBox boundingBox = NativeAccessProvider.getInstance().getBoundingBox(entity);
|
||||
return new EntityDimensions(Math.max(boundingBox.getMaxX() - boundingBox.getMinX(), boundingBox.getMaxZ() - boundingBox.getMinZ()), boundingBox.getMaxY() - boundingBox.getMinY());
|
||||
}
|
||||
|
||||
public EntityDimensions withValue(double value) {
|
||||
return new EntityDimensions(this.width * value, this.height * value);
|
||||
}
|
||||
|
||||
public EntityDimensions withValue(double value, double value2) {
|
||||
return new EntityDimensions(this.width + value, this.height + value2);
|
||||
}
|
||||
|
||||
public EntityDimensions withEntityDimensions(EntityDimensions entityDimensions) {
|
||||
return new EntityDimensions(Math.max(this.width, entityDimensions.width), Math.max(this.height, entityDimensions.height));
|
||||
}
|
||||
|
||||
public double getWidth() {
|
||||
return this.width;
|
||||
}
|
||||
|
||||
public double getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class EntityPushCalculator {
|
||||
|
||||
public static Vector calculatePush(Player player) {
|
||||
Vector vector = new Vector(0, 0, 0);
|
||||
for (Entity entity : player.getNearbyEntities(1.0, 1.0, 1.0)) {
|
||||
if (!entity.equals(player.getVehicle()) && entity instanceof LivingEntity) {
|
||||
double value = player.getLocation().getX() - entity.getLocation().getX();
|
||||
double value2 = player.getLocation().getZ() - entity.getLocation().getZ();
|
||||
double value3 = Math.max(Math.abs(value), Math.abs(value2));
|
||||
if (value3 > 0.01F) {
|
||||
value3 = Math.sqrt(value3);
|
||||
value /= value3;
|
||||
value2 /= value3;
|
||||
double value4 = Math.min(1.0, 1.0 / value3);
|
||||
value *= value4;
|
||||
value2 *= value4;
|
||||
value *= 0.05;
|
||||
value2 *= 0.05;
|
||||
value *= 1.0;
|
||||
value2 *= 1.0;
|
||||
vector.add(new Vector(value, 0.0, value2));
|
||||
}
|
||||
}
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.MaterialAlias;
|
||||
import me.konsolas.aac.world.fluid.FluidContact;
|
||||
import me.konsolas.aac.world.fluid.FluidType;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class EntityState implements EntityView {
|
||||
|
||||
private static final FluidContact MOVEMENT_COLLISION_RESULT = new FluidContact(false, false, 0.0);
|
||||
|
||||
private static final FluidContact MOVEMENT_COLLISION_RESULT2 = new FluidContact(true, false, 0.0);
|
||||
|
||||
private final ProtocolVersion protocolVersion;
|
||||
|
||||
private final EntityType entityType;
|
||||
|
||||
private final float walkSpeed;
|
||||
|
||||
private final float flySpeed;
|
||||
|
||||
private final double jumpStrength;
|
||||
|
||||
private final double stepHeight;
|
||||
|
||||
private final int levitationAmplifier;
|
||||
|
||||
private final int jumpBoostAmplifier;
|
||||
|
||||
private final boolean slowFalling;
|
||||
|
||||
private final boolean dolphinsGrace;
|
||||
|
||||
private final int depthStriderLevel;
|
||||
|
||||
private final float blockFriction;
|
||||
|
||||
private final AxisAlignedBoundingBox boundingBox;
|
||||
|
||||
private final PlayerPose pose;
|
||||
|
||||
private final boolean climbing;
|
||||
|
||||
private final boolean inScaffolding;
|
||||
|
||||
private final boolean inCobweb;
|
||||
|
||||
private final boolean inSweetBerryBush;
|
||||
|
||||
private final boolean touchingHoney;
|
||||
|
||||
private final boolean inBubbleColumn;
|
||||
|
||||
private final boolean headUnderwater;
|
||||
|
||||
private final FluidContact waterContact;
|
||||
|
||||
private final FluidContact lavaContact;
|
||||
|
||||
private final boolean previousInWater;
|
||||
|
||||
private final boolean previousInLava;
|
||||
|
||||
private final boolean previousFlowingFluid;
|
||||
|
||||
private final int soulSandBlockCount;
|
||||
|
||||
private final boolean onHoney;
|
||||
|
||||
private final boolean aboveShulkerBox;
|
||||
|
||||
public EntityState(Vector vector, EntityView entityView, MovementState movementState, MovementEnvironment movementEnvironment) {
|
||||
this.protocolVersion = movementEnvironment.getProtocolVersion();
|
||||
this.entityType = movementState.getEntityType();
|
||||
this.walkSpeed = movementState.getWalkSpeed();
|
||||
this.flySpeed = movementState.getFlySpeed();
|
||||
this.jumpStrength = movementState.getJumpStrength();
|
||||
this.stepHeight = movementState.getStepHeight();
|
||||
this.levitationAmplifier = movementState.getLevitationAmplifier();
|
||||
this.jumpBoostAmplifier = movementState.getJumpBoostAmplifier();
|
||||
this.slowFalling = movementState.isSlowFalling();
|
||||
this.dolphinsGrace = movementState.isDolphinsGrace();
|
||||
this.depthStriderLevel = movementState.getDepthStriderLevel();
|
||||
double x = vector.getX();
|
||||
double y = vector.getY();
|
||||
double z = vector.getZ();
|
||||
double value = y;
|
||||
double value2 = x;
|
||||
this.blockFriction = movementEnvironment.getBlockFriction(value2, value, z);
|
||||
if (this.entityType == EntityType.PLAYER) {
|
||||
if (movementState.getStableGliding() != null && !movementState.getStableGliding()) {
|
||||
ProtocolVersion version = ProtocolVersion.V1_13;
|
||||
if (this.protocolVersion.isAtLeast(version)) {
|
||||
if (PlayerPose.SWIMMING.checkPlayerPose(vector, movementEnvironment)) {
|
||||
if (entityView == null) {
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, (this.pose = PlayerPose.STANDING).findEntityDimensions(this.protocolVersion));
|
||||
} else {
|
||||
this.pose = entityView.getPose();
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, this.pose.findEntityDimensions(this.protocolVersion));
|
||||
}
|
||||
} else {
|
||||
decision: {
|
||||
decision2: {
|
||||
if (entityView != null && entityView.isInWater() && movementState.isSprinting()) {
|
||||
if (entityView.getPose() == PlayerPose.SWIMMING) {
|
||||
break decision2;
|
||||
}
|
||||
Set values = MaterialAlias.WATER;
|
||||
x = vector.getX();
|
||||
y = vector.getY() + movementState.getEyeHeight();
|
||||
double z2 = vector.getZ();
|
||||
double value3 = z2;
|
||||
double value4 = y;
|
||||
double value5 = x;
|
||||
if (values.contains(movementEnvironment.getMaterial(value5, value4, value3))) {
|
||||
break decision2;
|
||||
}
|
||||
}
|
||||
if (!PlayerPose.SNEAKING.checkPlayerPose(vector, movementEnvironment)) {
|
||||
if (movementState.getStableSneaking() != null && !movementState.getStableSneaking() && !PlayerPose.STANDING.checkPlayerPose(vector, movementEnvironment)) {
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, (this.pose = PlayerPose.STANDING).findEntityDimensions(this.protocolVersion));
|
||||
} else {
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, (this.pose = PlayerPose.SNEAKING).findEntityDimensions(this.protocolVersion));
|
||||
}
|
||||
break decision;
|
||||
}
|
||||
}
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, (this.pose = PlayerPose.SWIMMING).findEntityDimensions(this.protocolVersion));
|
||||
}
|
||||
}
|
||||
} else if (movementState.getStableSneaking() != null && !movementState.getStableSneaking()) {
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, (this.pose = PlayerPose.STANDING).findEntityDimensions(this.protocolVersion));
|
||||
} else {
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, (this.pose = PlayerPose.SNEAKING).findEntityDimensions(this.protocolVersion));
|
||||
}
|
||||
} else {
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, (this.pose = PlayerPose.FALL_FLYING).findEntityDimensions(this.protocolVersion));
|
||||
}
|
||||
} else {
|
||||
this.pose = PlayerPose.STANDING;
|
||||
this.boundingBox = new AxisAlignedBoundingBox(vector, movementState.getEntityDimensions());
|
||||
}
|
||||
boolean matches;
|
||||
decision3: {
|
||||
AxisAlignedBoundingBox box = this.boundingBox.expand(0.001, 0.001, 0.001);
|
||||
double x2 = vector.getX();
|
||||
y = vector.getY();
|
||||
double z3 = vector.getZ();
|
||||
double value6 = z3;
|
||||
double value7 = y;
|
||||
double value8 = x2;
|
||||
this.climbing = movementEnvironment.checkMovementEnvironment(value8, value7, value6);
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_14;
|
||||
if (serverVersion.isAtLeast(version2)) {
|
||||
Predicate predicate = result -> result == Material.SCAFFOLDING;
|
||||
Predicate predicate2 = predicate;
|
||||
if (movementEnvironment.checkMovementEnvironment(box, predicate2)) {
|
||||
matches = true;
|
||||
break decision3;
|
||||
}
|
||||
}
|
||||
matches = false;
|
||||
}
|
||||
AxisAlignedBoundingBox box2;
|
||||
decision4: {
|
||||
this.inScaffolding = matches;
|
||||
box2 = this.boundingBox.expand(-0.001, -0.001, -0.001);
|
||||
Predicate predicate3 = result -> result == MaterialAlias.COBWEB.getMaterial();
|
||||
Predicate predicate4 = predicate3;
|
||||
this.inCobweb = movementEnvironment.checkMovementEnvironment(box2, predicate4);
|
||||
ProtocolVersion serverVersion2 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version3 = ProtocolVersion.V1_14;
|
||||
if (serverVersion2.isAtLeast(version3)) {
|
||||
ProtocolVersion version4 = ProtocolVersion.V1_14;
|
||||
if (this.protocolVersion.isAtLeast(version4)) {
|
||||
Predicate predicate5 = result -> result == Material.SWEET_BERRY_BUSH;
|
||||
Predicate predicate6 = predicate5;
|
||||
if (movementEnvironment.checkMovementEnvironment(box2, predicate6)) {
|
||||
matches = true;
|
||||
break decision4;
|
||||
}
|
||||
}
|
||||
}
|
||||
matches = false;
|
||||
}
|
||||
decision5: {
|
||||
this.inSweetBerryBush = matches;
|
||||
AxisAlignedBoundingBox box3 = this.boundingBox.withMinY(this.boundingBox.getMinY() + 0.0625).expand(-0.001, -0.001, -0.001);
|
||||
ProtocolVersion serverVersion3 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version5 = ProtocolVersion.V1_15;
|
||||
if (serverVersion3.isAtLeast(version5)) {
|
||||
ProtocolVersion version6 = ProtocolVersion.V1_15;
|
||||
if (this.protocolVersion.isAtLeast(version6)) {
|
||||
Predicate predicate7 = result -> result == Material.HONEY_BLOCK;
|
||||
Predicate predicate8 = predicate7;
|
||||
if (movementEnvironment.checkMovementEnvironment(box3, predicate8)) {
|
||||
matches = true;
|
||||
break decision5;
|
||||
}
|
||||
}
|
||||
}
|
||||
matches = false;
|
||||
}
|
||||
decision6: {
|
||||
this.touchingHoney = matches;
|
||||
ProtocolVersion serverVersion4 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version7 = ProtocolVersion.V1_13;
|
||||
if (serverVersion4.isAtLeast(version7)) {
|
||||
AxisAlignedBoundingBox box4 = this.boundingBox;
|
||||
Predicate predicate9 = result -> result == Material.BUBBLE_COLUMN;
|
||||
Predicate predicate10 = predicate9;
|
||||
AxisAlignedBoundingBox box5 = box4;
|
||||
if (movementEnvironment.checkMovementEnvironment(box5, predicate10)) {
|
||||
matches = true;
|
||||
break decision6;
|
||||
}
|
||||
}
|
||||
matches = false;
|
||||
}
|
||||
this.inBubbleColumn = matches;
|
||||
x = vector.getX();
|
||||
y = vector.getY() + 1.0 - 0.1;
|
||||
double z4 = vector.getZ();
|
||||
double value10 = z4;
|
||||
double value11 = y;
|
||||
double value12 = x;
|
||||
this.headUnderwater = movementEnvironment.getFluidType(value12, value11, value10) == FluidType.WATER;
|
||||
AxisAlignedBoundingBox box6 = this.boundingBox.expand(-0.001, -0.001, -0.001);
|
||||
ProtocolVersion version8 = ProtocolVersion.V1_13;
|
||||
if (!this.protocolVersion.isAtLeast(version8)) {
|
||||
box6 = box6.expand(0.0, -0.4, 0.0);
|
||||
}
|
||||
FluidType fluidType = FluidType.WATER;
|
||||
this.waterContact = movementEnvironment.sampleFluidContact(box6, fluidType);
|
||||
ProtocolVersion version9 = ProtocolVersion.V1_13;
|
||||
if (this.protocolVersion.isAtLeast(version9)) {
|
||||
box6 = this.boundingBox.expand(-0.001, -0.001, -0.001);
|
||||
} else {
|
||||
box6 = this.boundingBox.expand(-0.1F, -0.4F, -0.1F);
|
||||
}
|
||||
ProtocolVersion version10 = ProtocolVersion.V1_16;
|
||||
if (this.protocolVersion.isAtLeast(version10)) {
|
||||
FluidType fluidType2 = FluidType.LAVA;
|
||||
this.lavaContact = movementEnvironment.sampleFluidContact(box6, fluidType2);
|
||||
} else {
|
||||
Predicate predicate11 = MaterialAlias.LAVA::contains;
|
||||
Predicate predicate12 = predicate11;
|
||||
this.lavaContact = movementEnvironment.checkMovementEnvironment(box6, predicate12) ? MOVEMENT_COLLISION_RESULT2 : MOVEMENT_COLLISION_RESULT;
|
||||
}
|
||||
Material material;
|
||||
decision7: {
|
||||
this.previousInWater = entityView != null && entityView.isInWater();
|
||||
this.previousInLava = entityView != null && entityView.isInLava();
|
||||
this.previousFlowingFluid = entityView != null && (entityView.isInFlowingWater() || entityView.isInFlowingLava());
|
||||
double x3 = vector.getX();
|
||||
double value13 = vector.getY() - 0.5;
|
||||
double z5 = vector.getZ();
|
||||
double value14 = z5;
|
||||
double value15 = value13;
|
||||
double value16 = x3;
|
||||
material = movementEnvironment.getMaterial(value16, value15, value14);
|
||||
if (movementState.getSoulSpeedLevel() > 0) {
|
||||
double x4 = vector.getX();
|
||||
double value17 = vector.getY() - 0.5;
|
||||
double z6 = vector.getZ();
|
||||
double value18 = z6;
|
||||
double value19 = value17;
|
||||
double value20 = x4;
|
||||
Material material2;
|
||||
if ((material2 = movementEnvironment.getMaterial(value20, value19, value18)) == Material.SOUL_SAND || material2 == Material.SOUL_SOIL) {
|
||||
this.soulSandBlockCount = 0;
|
||||
break decision7;
|
||||
}
|
||||
}
|
||||
ProtocolVersion serverVersion5 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version11 = ProtocolVersion.V1_15;
|
||||
if (serverVersion5.isAtLeast(version11)) {
|
||||
this.soulSandBlockCount = material == Material.SOUL_SAND ? 1 : 0;
|
||||
} else {
|
||||
Predicate predicate13 = result -> result == Material.SOUL_SAND;
|
||||
this.soulSandBlockCount = movementEnvironment.calculateCount(box2, predicate13);
|
||||
}
|
||||
}
|
||||
decision8: {
|
||||
ProtocolVersion serverVersion6 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version12 = ProtocolVersion.V1_15;
|
||||
if (serverVersion6.isAtLeast(version12)) {
|
||||
ProtocolVersion version13 = ProtocolVersion.V1_15;
|
||||
if (this.protocolVersion.isAtLeast(version13) && material == Material.HONEY_BLOCK) {
|
||||
matches = true;
|
||||
break decision8;
|
||||
}
|
||||
}
|
||||
matches = false;
|
||||
}
|
||||
this.onHoney = matches;
|
||||
AxisAlignedBoundingBox box7 = this.boundingBox.offset(0.0, -1.2, 0.0);
|
||||
Predicate predicate14 = MaterialAlias.SHULKER_BOXES::contains;
|
||||
Predicate predicate15 = predicate14;
|
||||
AxisAlignedBoundingBox box8 = box7;
|
||||
this.aboveShulkerBox = movementEnvironment.checkMovementEnvironment(box8, predicate15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolVersion getProtocolVersion() {
|
||||
return this.protocolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType getEntityType() {
|
||||
return this.entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSlowFalling() {
|
||||
return this.slowFalling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWalkSpeed() {
|
||||
return this.walkSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFlySpeed() {
|
||||
return this.flySpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLevitation() {
|
||||
return this.levitationAmplifier >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevitationAmplifier() {
|
||||
return this.levitationAmplifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasJumpBoost() {
|
||||
return this.jumpBoostAmplifier >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJumpBoostAmplifier() {
|
||||
return this.jumpBoostAmplifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInWater() {
|
||||
return this.waterContact.isTouching();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInFlowingWater() {
|
||||
return this.waterContact.isFlowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInLava() {
|
||||
return this.lavaContact.isTouching();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInFlowingLava() {
|
||||
return this.lavaContact.isFlowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreviousInWater() {
|
||||
return this.previousInWater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreviousInLava() {
|
||||
return this.previousInLava;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreviousFlowingFluid() {
|
||||
return this.previousFlowingFluid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWaterDepth() {
|
||||
return this.waterContact.getDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLavaDepth() {
|
||||
return this.lavaContact.getDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDepthStriderLevel() {
|
||||
return this.depthStriderLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDolphinsGrace() {
|
||||
return this.dolphinsGrace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockFriction() {
|
||||
return this.blockFriction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getJumpStrength() {
|
||||
return this.jumpStrength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStepHeight() {
|
||||
return this.stepHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClimbing() {
|
||||
return this.climbing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInScaffolding() {
|
||||
return this.inScaffolding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInCobweb() {
|
||||
return this.inCobweb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInSweetBerryBush() {
|
||||
return this.inSweetBerryBush;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTouchingHoney() {
|
||||
return this.touchingHoney;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInBubbleColumn() {
|
||||
return this.inBubbleColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHeadUnderwater() {
|
||||
return this.headUnderwater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoulSandBlockCount() {
|
||||
return this.soulSandBlockCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnHoney() {
|
||||
return this.onHoney;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAboveShulkerBox() {
|
||||
return this.aboveShulkerBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBoundingBox getBoundingBox() {
|
||||
return this.boundingBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerPose getPose() {
|
||||
return this.pose;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
public interface EntityView {
|
||||
|
||||
ProtocolVersion getProtocolVersion();
|
||||
|
||||
EntityType getEntityType();
|
||||
|
||||
boolean isSlowFalling();
|
||||
|
||||
float getWalkSpeed();
|
||||
|
||||
float getFlySpeed();
|
||||
|
||||
boolean hasLevitation();
|
||||
|
||||
int getLevitationAmplifier();
|
||||
|
||||
boolean hasJumpBoost();
|
||||
|
||||
int getJumpBoostAmplifier();
|
||||
|
||||
boolean isInWater();
|
||||
|
||||
boolean isInFlowingWater();
|
||||
|
||||
boolean isInLava();
|
||||
|
||||
boolean isInFlowingLava();
|
||||
|
||||
boolean isPreviousInWater();
|
||||
|
||||
boolean isPreviousInLava();
|
||||
|
||||
boolean isPreviousFlowingFluid();
|
||||
|
||||
double getWaterDepth();
|
||||
|
||||
double getLavaDepth();
|
||||
|
||||
int getDepthStriderLevel();
|
||||
|
||||
boolean isDolphinsGrace();
|
||||
|
||||
float getBlockFriction();
|
||||
|
||||
double getJumpStrength();
|
||||
|
||||
double getStepHeight();
|
||||
|
||||
boolean isClimbing();
|
||||
|
||||
boolean isInScaffolding();
|
||||
|
||||
boolean isInCobweb();
|
||||
|
||||
boolean isInSweetBerryBush();
|
||||
|
||||
boolean isTouchingHoney();
|
||||
|
||||
boolean isInBubbleColumn();
|
||||
|
||||
boolean isHeadUnderwater();
|
||||
|
||||
int getSoulSandBlockCount();
|
||||
|
||||
boolean isOnHoney();
|
||||
|
||||
boolean isAboveShulkerBox();
|
||||
|
||||
AxisAlignedBoundingBox getBoundingBox();
|
||||
|
||||
PlayerPose getPose();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class InputStateIterator implements Iterator {
|
||||
|
||||
private static final float[][] windowSamples = new float[][] { { 1.0F, 0.0F }, { 1.0F, -1.0F }, { 1.0F, 1.0F }, { 0.0F, 1.0F }, { 0.0F, -1.0F }, { -1.0F, 0.0F }, { -1.0F, 1.0F }, { -1.0F, -1.0F }, { 0.0F, 0.0F } };
|
||||
|
||||
private int index = 0;
|
||||
|
||||
private final List values = new ArrayList();
|
||||
|
||||
public InputStateIterator(PlayerInputState playerInputState, float value, float value2, boolean matches, int value3, boolean matches2, Boolean matches3, Boolean matches4, Boolean matches5, Boolean matches6) {
|
||||
boolean matches7 = playerInputState.isSprinting() && playerInputState.getForward() > 0.0F;
|
||||
for (int index = 0; index <= value3; index++) {
|
||||
float[] values = new float[] { playerInputState.getForward(), playerInputState.getStrafe() };
|
||||
boolean sprinting = playerInputState.isSprinting();
|
||||
float[] values2 = values;
|
||||
this.addMovementCandidates(values2, matches3, matches4, matches5, matches6, index, value, value2, matches2, matches, matches7, sprinting);
|
||||
for (float[] values3 : windowSamples) {
|
||||
if (values3[0] != playerInputState.getForward() || values3[1] != playerInputState.getStrafe()) {
|
||||
boolean sneaking = playerInputState.isSneaking();
|
||||
this.addMovementCandidates(values3, matches3, matches4, matches5, matches6, index, value, value2, matches2, matches, matches7, sneaking);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addMovementCandidates(float[] values, Boolean matches, Boolean matches2, Boolean matches3, Boolean matches4, int value, float value2, float value3, boolean matches5, boolean matches6, boolean matches7, boolean matches8) {
|
||||
if (matches8 && matches5 && (values[0] > 0.0F || matches7 && values[0] >= 0.0F)) {
|
||||
this.addInputVariants(values, true, false, matches, matches2, matches3, matches4, value2, value3, value);
|
||||
if (matches6) {
|
||||
this.addInputVariants(values, true, true, matches, matches2, matches3, matches4, value2, value3, value);
|
||||
}
|
||||
}
|
||||
this.addInputVariants(values, false, false, matches, matches2, matches3, matches4, value2, value3, value);
|
||||
if (matches6) {
|
||||
this.addInputVariants(values, false, true, matches, matches2, matches3, matches4, value2, value3, value);
|
||||
}
|
||||
if (!matches8 && matches5 && (values[0] > 0.0F || matches7 && values[0] >= 0.0F)) {
|
||||
this.addInputVariants(values, true, false, matches, matches2, matches3, matches4, value2, value3, value);
|
||||
if (matches6) {
|
||||
this.addInputVariants(values, true, true, matches, matches2, matches3, matches4, value2, value3, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addInputVariants(float[] values2, boolean matches, boolean matches2, Boolean matches3, Boolean matches4, Boolean matches5, Boolean matches6, float value, float value2, int value3) {
|
||||
boolean matches7 = matches3 == null ? false : matches3;
|
||||
boolean matches8 = matches4 == null ? false : matches4;
|
||||
boolean matches9 = matches5 == null ? true : matches5;
|
||||
boolean matches10 = matches6 == null ? true : matches6;
|
||||
this.values.add(new PlayerInputState(values2[0], values2[1], matches, matches2, matches7, matches8, matches9, matches10, value, value2, value3));
|
||||
if (matches5 == null) {
|
||||
this.values.add(new PlayerInputState(values2[0], values2[1], matches, matches2, matches7, matches8, false, matches10, value, value2, value3));
|
||||
}
|
||||
if (matches6 == null) {
|
||||
this.values.add(new PlayerInputState(values2[0], values2[1], matches, matches2, matches7, matches8, matches9, false, value, value2, value3));
|
||||
}
|
||||
if (matches3 == null) {
|
||||
this.values.add(new PlayerInputState(values2[0], values2[1], matches, matches2, true, matches8, matches9, matches10, value, value2, value3));
|
||||
}
|
||||
if (matches4 == null) {
|
||||
this.values.add(new PlayerInputState(values2[0], values2[1], matches, matches2, matches7, true, matches9, matches10, value, value2, value3));
|
||||
}
|
||||
if (matches3 == null && matches4 == null) {
|
||||
this.values.add(new PlayerInputState(values2[0], values2[1], matches, matches2, true, true, matches9, matches10, value, value2, value3));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.index < this.values.size();
|
||||
}
|
||||
|
||||
public PlayerInputState getPlayerInputState() {
|
||||
return this.index >= this.values.size() ? null : (PlayerInputState) this.values.get(this.index++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object next() {
|
||||
return this.getPlayerInputState();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class KnockbackState {
|
||||
|
||||
private final Location origin;
|
||||
|
||||
private final Location destination;
|
||||
|
||||
private final Vector velocity;
|
||||
|
||||
private final float horizontalMagnitude;
|
||||
|
||||
public KnockbackState(Location origin, Location destination, Vector velocity, float horizontalMagnitude) {
|
||||
this.origin = origin;
|
||||
this.destination = destination;
|
||||
this.velocity = velocity;
|
||||
this.horizontalMagnitude = horizontalMagnitude;
|
||||
}
|
||||
|
||||
public Location getOrigin() {
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
public Location getDestination() {
|
||||
return this.destination;
|
||||
}
|
||||
|
||||
public Vector getVelocity() {
|
||||
return this.velocity;
|
||||
}
|
||||
|
||||
public float getHorizontalMagnitude() {
|
||||
return this.horizontalMagnitude;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class MovementDelta {
|
||||
|
||||
private final Vector expectedMovement;
|
||||
|
||||
private final Vector actualMovement;
|
||||
|
||||
private final boolean horizontalCollision;
|
||||
|
||||
private final boolean verticalCollision;
|
||||
|
||||
private final boolean onGround;
|
||||
|
||||
public MovementDelta(Vector expectedMovement, Vector actualMovement, boolean horizontalCollision, boolean verticalCollision, boolean onGround) {
|
||||
this.expectedMovement = expectedMovement;
|
||||
this.actualMovement = actualMovement;
|
||||
this.horizontalCollision = horizontalCollision;
|
||||
this.verticalCollision = verticalCollision;
|
||||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
public Vector getExpectedMovement() {
|
||||
return this.expectedMovement;
|
||||
}
|
||||
|
||||
public Vector getActualMovement() {
|
||||
return this.actualMovement;
|
||||
}
|
||||
|
||||
public boolean isHorizontalCollision() {
|
||||
return this.horizontalCollision;
|
||||
}
|
||||
|
||||
public boolean isVerticalCollision() {
|
||||
return this.verticalCollision;
|
||||
}
|
||||
|
||||
public boolean isOnGround() {
|
||||
return this.onGround;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object result) {
|
||||
if (this == result) {
|
||||
return true;
|
||||
} else if (result != null && this.getClass() == result.getClass()) {
|
||||
MovementDelta movementDelta = (MovementDelta) result;
|
||||
return this.horizontalCollision == movementDelta.horizontalCollision && this.verticalCollision == movementDelta.verticalCollision && Objects.equals(this.expectedMovement, movementDelta.expectedMovement) && Objects.equals(this.actualMovement, movementDelta.actualMovement);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.expectedMovement, this.actualMovement, this.horizontalCollision, this.verticalCollision);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.collection.BlockGrid;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import me.konsolas.aac.world.BlockSnapshot;
|
||||
import me.konsolas.aac.world.BlockSnapshotCache;
|
||||
import me.konsolas.aac.world.MaterialAlias;
|
||||
import me.konsolas.aac.world.fluid.FluidContact;
|
||||
import me.konsolas.aac.world.fluid.FluidType;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.NumberConversions;
|
||||
|
||||
public class MovementEnvironment {
|
||||
|
||||
private static final Set CLIMBABLE_MATERIALS = EnumSet.noneOf(Material.class);
|
||||
|
||||
private final ProtocolVersion protocolVersion;
|
||||
|
||||
private final AxisAlignedBoundingBox boundingBox;
|
||||
|
||||
private final int minX;
|
||||
|
||||
private final int minY;
|
||||
|
||||
private final int minZ;
|
||||
|
||||
private final int maxX;
|
||||
|
||||
private final int maxY;
|
||||
|
||||
private final int maxZ;
|
||||
|
||||
private final BlockGrid blocks;
|
||||
|
||||
public MovementEnvironment(ProtocolVersion protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.boundingBox = new AxisAlignedBoundingBox(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
this.minX = this.minY = this.minZ = 0;
|
||||
this.maxX = this.maxY = this.maxZ = 0;
|
||||
this.blocks = new BlockGrid(0, 0, 0);
|
||||
}
|
||||
|
||||
public MovementEnvironment(Player player, AxisAlignedBoundingBox box, ProtocolVersion protocolVersion, MovementEnvironment movementEnvironment) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.boundingBox = box.alignToBlocks();
|
||||
World world = player.getWorld();
|
||||
this.minX = NumberConversions.floor(box.getMinX());
|
||||
this.minY = NumberConversions.floor(box.getMinY());
|
||||
this.minZ = NumberConversions.floor(box.getMinZ());
|
||||
this.maxX = NumberConversions.ceil(box.getMaxX());
|
||||
this.maxY = NumberConversions.ceil(box.getMaxY());
|
||||
this.maxZ = NumberConversions.ceil(box.getMaxZ());
|
||||
this.blocks = new BlockGrid(this.maxX - this.minX, this.maxY - this.minY, this.maxZ - this.minZ);
|
||||
for (int value = this.minX; value < this.maxX; value++) {
|
||||
for (int value2 = this.minY; value2 < this.maxY; value2++) {
|
||||
for (int value3 = this.minZ; value3 < this.maxZ; value3++) {
|
||||
BlockSnapshot blockSnapshot;
|
||||
if (movementEnvironment != null && movementEnvironment.containsBlock(value, value2, value3)) {
|
||||
blockSnapshot = movementEnvironment.getBlockSnapshot(value, value2, value3);
|
||||
} else {
|
||||
Block blockAt = world.getBlockAt(value, value2, value3);
|
||||
blockSnapshot = BlockSnapshotCache.findBlockSnapshot(player, blockAt, protocolVersion);
|
||||
}
|
||||
this.blocks.set(value - this.minX, value2 - this.minY, value3 - this.minZ, blockSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateBlock(int value, int value2, int value3) {
|
||||
if (this.containsBlock(value, value2, value3)) {
|
||||
this.blocks.set(value - this.minX, value2 - this.minY, value3 - this.minZ, BlockSnapshotCache.AIR);
|
||||
}
|
||||
}
|
||||
|
||||
public ProtocolVersion getProtocolVersion() {
|
||||
return this.protocolVersion;
|
||||
}
|
||||
|
||||
public AxisAlignedBoundingBox getBoundingBox() {
|
||||
return this.boundingBox;
|
||||
}
|
||||
|
||||
public boolean containsBlock(int value, int value2, int value3) {
|
||||
return value >= this.minX && value < this.maxX && value2 >= this.minY && value2 < this.maxY && value3 >= this.minZ && value3 < this.maxZ;
|
||||
}
|
||||
|
||||
private BlockSnapshot getBlockSnapshot(int value, int value2, int value3) {
|
||||
return value >= this.minX && value < this.maxX && value2 >= this.minY && value2 < this.maxY && value3 >= this.minZ && value3 < this.maxZ ? (BlockSnapshot) this.blocks.get(value - this.minX, value2 - this.minY, value3 - this.minZ) : BlockSnapshotCache.AIR;
|
||||
}
|
||||
|
||||
private BlockSnapshot findBlockSnapshot(double value, double value2, double value3) {
|
||||
int value4 = NumberConversions.floor(value);
|
||||
int value5 = NumberConversions.floor(value2);
|
||||
int value6 = NumberConversions.floor(value3);
|
||||
int value7 = value6;
|
||||
int value8 = value5;
|
||||
int value9 = value4;
|
||||
return this.getBlockSnapshot(value9, value8, value7);
|
||||
}
|
||||
|
||||
public Material getMaterial(double value, double value2, double value3) {
|
||||
return this.findBlockSnapshot(value, value2, value3).getMaterial();
|
||||
}
|
||||
|
||||
public FluidType getFluidType(double value, double value2, double value3) {
|
||||
return this.findBlockSnapshot(value, value2, value3).getFluidType();
|
||||
}
|
||||
|
||||
public Collection<AxisAlignedBoundingBox> getCollisionBoxes(AxisAlignedBoundingBox box) {
|
||||
ArrayList values = new ArrayList();
|
||||
int value = Math.max(this.minX, NumberConversions.floor(box.getMinX()));
|
||||
int value2 = Math.max(this.minY, NumberConversions.floor(box.getMinY() - 1.0));
|
||||
int value3 = Math.max(this.minZ, NumberConversions.floor(box.getMinZ()));
|
||||
int value4 = Math.min(this.maxX, NumberConversions.ceil(box.getMaxX()));
|
||||
int value5 = Math.min(this.maxY, NumberConversions.ceil(box.getMaxY()));
|
||||
int value6 = Math.min(this.maxZ, NumberConversions.ceil(box.getMaxZ()));
|
||||
for (int value7 = value; value7 < value4; value7++) {
|
||||
for (int value8 = value2; value8 < value5; value8++) {
|
||||
for (int value9 = value3; value9 < value6; value9++) {
|
||||
BlockSnapshot blockSnapshot = (BlockSnapshot) this.blocks.get(value7 - this.minX, value8 - this.minY, value9 - this.minZ);
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_14;
|
||||
if (!serverVersion.isAtLeast(version) || blockSnapshot.getMaterial() != Material.SCAFFOLDING) {
|
||||
for (AxisAlignedBoundingBox box2 : blockSnapshot.getCollisionShape().getBoxesAt(value7, value8, value9)) {
|
||||
if (box2.intersects(box)) {
|
||||
values.add(box2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public boolean checkMovementEnvironment(AxisAlignedBoundingBox box, double value, double value2, double value3) {
|
||||
int value4 = Math.max(this.minX, NumberConversions.floor(box.getMinX() + value));
|
||||
int value5 = Math.max(this.minY, NumberConversions.floor(box.getMinY() + value2));
|
||||
int value6 = Math.max(this.minZ, NumberConversions.floor(box.getMinZ() + value3));
|
||||
int value7 = Math.min(this.maxX, NumberConversions.ceil(box.getMaxX() + value));
|
||||
int value8 = Math.min(this.maxY, NumberConversions.ceil(box.getMaxY() + value2));
|
||||
int value9 = Math.min(this.maxZ, NumberConversions.ceil(box.getMaxZ() + value3));
|
||||
for (int value10 = value4; value10 < value7; value10++) {
|
||||
for (int value11 = value5; value11 < value8; value11++) {
|
||||
for (int value12 = value6; value12 < value9; value12++) {
|
||||
for (AxisAlignedBoundingBox box2 : ((BlockSnapshot) this.blocks.get(value10 - this.minX, value11 - this.minY, value12 - this.minZ)).getCollisionShape().getBoxes()) {
|
||||
double value13 = value - value10;
|
||||
double value14 = value2 - value11;
|
||||
double value15 = value3 - value12;
|
||||
double value16 = value14;
|
||||
double value17 = value13;
|
||||
if (box2.intersectsOffset(box, value17, value16, value15)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean collides(AxisAlignedBoundingBox box) {
|
||||
return !this.checkMovementEnvironment(box, (double) 0.0, (double) 0.0, (double) 0.0);
|
||||
}
|
||||
|
||||
public float getBlockFriction(double value, double value2, double value3) {
|
||||
double value4 = value2 - 1.0;
|
||||
double value5 = value4;
|
||||
return this.findBlockSnapshot(value, value5, value3).getFriction();
|
||||
}
|
||||
|
||||
public boolean checkMovementEnvironment(double value, double value2, double value3) {
|
||||
BlockSnapshot blockSnapshot = this.findBlockSnapshot(value, value2, value3);
|
||||
Material material = blockSnapshot.getMaterial();
|
||||
if (!CLIMBABLE_MATERIALS.contains(material)) {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_14;
|
||||
if (!serverVersion.isAtLeast(version) || material != Material.SCAFFOLDING) {
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_9;
|
||||
if (this.protocolVersion.isAtLeast(version2) && MaterialAlias.TRAPDOORS.contains(material) && blockSnapshot.isWaterlogged()) {
|
||||
double value4 = value2 - 1.0;
|
||||
double value5 = value4;
|
||||
BlockSnapshot blockSnapshot2 = this.findBlockSnapshot(value, value5, value3);
|
||||
return blockSnapshot2.getMaterial() == Material.LADDER && blockSnapshot2.getFacing() == blockSnapshot.getFacing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean evaluateCheckMovementEnvironment(AxisAlignedBoundingBox box) {
|
||||
double minY = box.getMinY();
|
||||
AxisAlignedBoundingBox box2 = box.offset(0.0, -0.001, 0.0);
|
||||
for (AxisAlignedBoundingBox box3 : this.getCollisionBoxes(box2)) {
|
||||
if (box3.getMaxY() == minY && box3.intersects(box2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean checkMovementEnvironment(AxisAlignedBoundingBox box) {
|
||||
AxisAlignedBoundingBox box2 = box.expand(0.001, 0.0, 0.001);
|
||||
for (AxisAlignedBoundingBox box3 : this.getCollisionBoxes(box2)) {
|
||||
if (box3.getMaxX() == box.getMinX() || box3.getMinX() == box.getMaxX() || box3.getMaxZ() == box.getMinZ() || box3.getMinZ() == box.getMaxZ() && box3.intersects(box2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean checkMovementEnvironment(AxisAlignedBoundingBox box, Predicate predicate) {
|
||||
int value = NumberConversions.floor(box.getMinX());
|
||||
int value2 = NumberConversions.floor(box.getMinY());
|
||||
int value3 = NumberConversions.floor(box.getMinZ());
|
||||
int value4 = NumberConversions.ceil(box.getMaxX());
|
||||
int value5 = NumberConversions.ceil(box.getMaxY());
|
||||
int value6 = NumberConversions.ceil(box.getMaxZ());
|
||||
for (int value7 = value; value7 < value4; value7++) {
|
||||
for (int value8 = value2; value8 < value5; value8++) {
|
||||
for (int value9 = value3; value9 < value6; value9++) {
|
||||
if (predicate.test(this.getBlockSnapshot(value7, value8, value9).getMaterial())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int calculateCount(AxisAlignedBoundingBox box, Predicate predicate) {
|
||||
int value = NumberConversions.floor(box.getMinX());
|
||||
int value2 = NumberConversions.floor(box.getMinY());
|
||||
int value3 = NumberConversions.floor(box.getMinZ());
|
||||
int value4 = NumberConversions.ceil(box.getMaxX());
|
||||
int value5 = NumberConversions.ceil(box.getMaxY());
|
||||
int value6 = NumberConversions.ceil(box.getMaxZ());
|
||||
int value7 = 0;
|
||||
for (int value8 = value; value8 < value4; value8++) {
|
||||
for (int value9 = value2; value9 < value5; value9++) {
|
||||
for (int value10 = value3; value10 < value6; value10++) {
|
||||
if (predicate.test(this.getBlockSnapshot(value8, value9, value10).getMaterial())) {
|
||||
value7++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value7;
|
||||
}
|
||||
|
||||
private static float calculateValue(int value) {
|
||||
if (value >= 8) {
|
||||
value = 0;
|
||||
}
|
||||
return (value + 1) / 9.0F;
|
||||
}
|
||||
|
||||
public FluidContact sampleFluidContact(AxisAlignedBoundingBox box, FluidType fluidType) {
|
||||
int value = NumberConversions.floor(box.getMinX());
|
||||
int value2 = NumberConversions.floor(box.getMinY());
|
||||
int value3 = NumberConversions.floor(box.getMinZ());
|
||||
int value4 = NumberConversions.floor(box.getMaxX() + 1.0);
|
||||
int value5 = NumberConversions.floor(box.getMaxY() + 1.0);
|
||||
int value6 = NumberConversions.floor(box.getMaxZ() + 1.0);
|
||||
boolean matches = false;
|
||||
double value7 = 0.0;
|
||||
boolean matches2 = false;
|
||||
ProtocolVersion version = ProtocolVersion.V1_13;
|
||||
if (this.protocolVersion.isAtLeast(version)) {
|
||||
for (int value8 = value; value8 < value4; value8++) {
|
||||
for (int value9 = value2; value9 < value5; value9++) {
|
||||
for (int value10 = value3; value10 < value6; value10++) {
|
||||
BlockSnapshot blockSnapshot = this.getBlockSnapshot(value8, value9, value10);
|
||||
if (blockSnapshot.getFluidType() == fluidType && blockSnapshot.getFluidLevel() != -1) {
|
||||
float value11 = value9 + 1;
|
||||
int fluidLevel = blockSnapshot.getFluidLevel();
|
||||
double value12 = value11 - calculateValue(fluidLevel);
|
||||
if (value12 >= box.getMinY()) {
|
||||
matches = true;
|
||||
value7 = Math.max(value12 - box.getMinY(), value7);
|
||||
if (blockSnapshot.getFluidLevel() != 0) {
|
||||
matches2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int value13 = value; value13 < value4; value13++) {
|
||||
for (int value14 = value2; value14 < value5; value14++) {
|
||||
for (int value15 = value3; value15 < value6; value15++) {
|
||||
BlockSnapshot blockSnapshot2 = this.getBlockSnapshot(value13, value14, value15);
|
||||
if (blockSnapshot2.getFluidType() == fluidType && blockSnapshot2.getFluidLevel() != -1) {
|
||||
float value16 = value14 + 1;
|
||||
int fluidLevel2 = blockSnapshot2.getFluidLevel();
|
||||
double value17 = value16 - calculateValue(fluidLevel2);
|
||||
if (value5 >= value17) {
|
||||
matches = true;
|
||||
if (blockSnapshot2.getFluidLevel() != 0) {
|
||||
matches2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new FluidContact(matches, matches2, value7);
|
||||
}
|
||||
|
||||
static {
|
||||
for (String text : Arrays.asList("LADDER", "VINE", "TWISTING_VINES", "TWISTING_VINES_PLANT", "WEEPING_VINES", "WEEPING_VINES_PLANT")) {
|
||||
try {
|
||||
CLIMBABLE_MATERIALS.add(Material.valueOf(text));
|
||||
} catch (IllegalArgumentException illegalArgumentException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class MovementFrame {
|
||||
|
||||
private final PlayerInputState input;
|
||||
|
||||
private final RotationDelta rotationDelta;
|
||||
|
||||
private final Vector position;
|
||||
|
||||
private final MovementDelta movementDelta;
|
||||
|
||||
private final TimedVelocity velocity;
|
||||
|
||||
private final int tick;
|
||||
|
||||
public MovementFrame(PlayerInputState playerInputState, TimedVelocity timedVelocity, Vector vector, RotationDelta rotationDelta, MovementDelta movementDelta) {
|
||||
this(playerInputState, timedVelocity, vector, rotationDelta, movementDelta, 0);
|
||||
}
|
||||
|
||||
private MovementFrame(PlayerInputState input, TimedVelocity velocity, Vector position, RotationDelta rotationDelta, MovementDelta movementDelta, int tick) {
|
||||
this.input = input;
|
||||
this.rotationDelta = rotationDelta;
|
||||
this.position = position;
|
||||
this.movementDelta = movementDelta;
|
||||
this.velocity = velocity;
|
||||
this.tick = tick;
|
||||
}
|
||||
|
||||
public PlayerInputState getInput() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
public RotationDelta getRotationDelta() {
|
||||
return this.rotationDelta;
|
||||
}
|
||||
|
||||
public Vector getVector() {
|
||||
return this.position.clone();
|
||||
}
|
||||
|
||||
public MovementDelta getMovementDelta() {
|
||||
return this.movementDelta;
|
||||
}
|
||||
|
||||
public TimedVelocity getVelocity() {
|
||||
return this.velocity;
|
||||
}
|
||||
|
||||
public int getTick() {
|
||||
return this.tick;
|
||||
}
|
||||
|
||||
public MovementFrame withVector(Vector vector) {
|
||||
Vector vector2 = vector.clone();
|
||||
return new MovementFrame(this.input, this.velocity, this.position, this.rotationDelta, new MovementDelta(vector2, vector2, this.movementDelta.isHorizontalCollision(), this.movementDelta.isVerticalCollision(), this.movementDelta.isOnGround()), this.tick);
|
||||
}
|
||||
|
||||
public MovementFrame withVectorWithInput(Vector vector) {
|
||||
return new MovementFrame(this.input, this.velocity, this.position, this.rotationDelta, new MovementDelta(this.movementDelta.getExpectedMovement(), vector, this.movementDelta.isHorizontalCollision(), this.movementDelta.isVerticalCollision(), this.movementDelta.isOnGround()), this.tick);
|
||||
}
|
||||
|
||||
public MovementFrame withValue(double value) {
|
||||
Vector actualMovement = this.movementDelta.getActualMovement();
|
||||
actualMovement.setY(value);
|
||||
return this.withVectorWithInput(actualMovement);
|
||||
}
|
||||
|
||||
public MovementFrame copyWithInput() {
|
||||
return new MovementFrame(this.input, this.velocity, this.position, this.rotationDelta, new MovementDelta(this.movementDelta.getExpectedMovement(), this.movementDelta.getActualMovement(), true, this.movementDelta.isVerticalCollision(), this.movementDelta.isOnGround()), this.tick);
|
||||
}
|
||||
|
||||
public MovementFrame copy() {
|
||||
return new MovementFrame(this.input, this.velocity, this.position, this.rotationDelta, new MovementDelta(this.movementDelta.getExpectedMovement(), this.movementDelta.getActualMovement(), this.movementDelta.isHorizontalCollision(), true, this.movementDelta.isOnGround()), this.tick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object result) {
|
||||
if (this == result) {
|
||||
return true;
|
||||
} else if (result != null && this.getClass() == result.getClass()) {
|
||||
MovementFrame frame = (MovementFrame) result;
|
||||
return Objects.equals(this.rotationDelta, frame.rotationDelta) && Objects.equals(this.movementDelta, frame.movementDelta) && Objects.equals(this.velocity, frame.velocity);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.rotationDelta, this.movementDelta, this.velocity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import me.konsolas.aac.compat.NativeAccessProvider;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.WorldBorder;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Donkey;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Horse;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Llama;
|
||||
import org.bukkit.entity.Mule;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.SkeletonHorse;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
public class MovementState {
|
||||
|
||||
private final AtomicBoolean updated = new AtomicBoolean();
|
||||
|
||||
private EntityType entityType;
|
||||
|
||||
private EntityType rootVehicleType;
|
||||
|
||||
private EntityType vehicleType;
|
||||
|
||||
private EntityDimensions entityDimensions = EntityDimensions.DEFAULT;
|
||||
|
||||
private boolean unsupportedEntity;
|
||||
|
||||
private LivingEntity livingEntity;
|
||||
|
||||
private float walkSpeed;
|
||||
|
||||
private float flySpeed;
|
||||
|
||||
private double jumpStrength;
|
||||
|
||||
private double stepHeight;
|
||||
|
||||
private int levitationAmplifier;
|
||||
|
||||
private int jumpBoostAmplifier;
|
||||
|
||||
private boolean slowFalling;
|
||||
|
||||
private boolean dolphinsGrace;
|
||||
|
||||
private boolean wearingElytra;
|
||||
|
||||
private int depthStriderLevel;
|
||||
|
||||
private int soulSpeedLevel;
|
||||
|
||||
private double eyeHeight;
|
||||
|
||||
private double entityPushMagnitude;
|
||||
|
||||
private boolean nearFirework;
|
||||
|
||||
private boolean nearBoat;
|
||||
|
||||
private boolean nearShulker;
|
||||
|
||||
private double borderMinX;
|
||||
|
||||
private double borderMinZ;
|
||||
|
||||
private double borderMaxX;
|
||||
|
||||
private double borderMaxZ;
|
||||
|
||||
private boolean sneaking;
|
||||
|
||||
private boolean usingItem;
|
||||
|
||||
private boolean gliding;
|
||||
|
||||
private boolean flying;
|
||||
|
||||
private boolean canSprint;
|
||||
|
||||
private boolean sprinting;
|
||||
|
||||
private float maximumRecentWalkSpeed;
|
||||
|
||||
private final AtomicInteger sneakTransitionTicks = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger itemUseTransitionTicks = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger glideTransitionTicks = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger flightTransitionTicks = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger speedTransitionTicks = new AtomicInteger();
|
||||
|
||||
private volatile boolean riptideReady;
|
||||
|
||||
public void pollUpdateFlag() {
|
||||
this.updated.get();
|
||||
}
|
||||
|
||||
public void markUpdated() {
|
||||
this.updated.set(true);
|
||||
}
|
||||
|
||||
public void updateAttributes(Player player) {
|
||||
LivingEntity livingEntity2 = player;
|
||||
while (livingEntity2.getVehicle() instanceof LivingEntity) {
|
||||
livingEntity2 = (LivingEntity) livingEntity2.getVehicle();
|
||||
}
|
||||
if (livingEntity2 != player) {
|
||||
this.vehicleType = livingEntity2.getType();
|
||||
}
|
||||
this.livingEntity = (LivingEntity) livingEntity2;
|
||||
if (livingEntity2.getVehicle() != null) {
|
||||
Entity vehicle = livingEntity2.getVehicle();
|
||||
while (vehicle.getVehicle() != null) {
|
||||
vehicle = vehicle.getVehicle();
|
||||
}
|
||||
this.rootVehicleType = vehicle.getType();
|
||||
} else {
|
||||
this.rootVehicleType = null;
|
||||
}
|
||||
this.entityType = livingEntity2.getType();
|
||||
this.entityDimensions = EntityDimensions.create((Entity) livingEntity2);
|
||||
int value = -1;
|
||||
int value2 = -1;
|
||||
int value3 = -1;
|
||||
int value4 = -1;
|
||||
boolean matches = false;
|
||||
boolean matches2 = false;
|
||||
for (PotionEffect potionEffect : livingEntity2.getActivePotionEffects()) {
|
||||
if (potionEffect.getType().equals(PotionEffectType.SPEED)) {
|
||||
value = potionEffect.getAmplifier();
|
||||
} else if (potionEffect.getType().equals(PotionEffectType.SLOW)) {
|
||||
value2 = potionEffect.getAmplifier();
|
||||
} else if (potionEffect.getType().equals(PotionEffectType.JUMP)) {
|
||||
value3 = potionEffect.getAmplifier();
|
||||
} else {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_9;
|
||||
if (serverVersion.isAtLeast(version) && potionEffect.getType().equals(PotionEffectType.LEVITATION)) {
|
||||
value4 = potionEffect.getAmplifier();
|
||||
} else {
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version2) && potionEffect.getType().equals(PotionEffectType.SLOW_FALLING)) {
|
||||
matches = true;
|
||||
} else {
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version3 = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version3) && potionEffect.getType().equals(PotionEffectType.DOLPHINS_GRACE)) {
|
||||
matches2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.levitationAmplifier = value4;
|
||||
this.jumpBoostAmplifier = value3;
|
||||
this.slowFalling = matches;
|
||||
this.dolphinsGrace = matches2;
|
||||
ProtocolVersion serverVersion2 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version4 = ProtocolVersion.V1_9;
|
||||
if (serverVersion2.isAtLeast(version4)) {
|
||||
float value5 = (float) Objects.requireNonNull(livingEntity2.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED)).getValue();
|
||||
if (livingEntity2 instanceof Player && ((Player) livingEntity2).isSprinting()) {
|
||||
value5 = (float) (value5 / 1.300000011920929);
|
||||
}
|
||||
if (Math.abs(this.walkSpeed - value5) > 0.001) {
|
||||
this.speedTransitionTicks.set(5);
|
||||
this.maximumRecentWalkSpeed = Math.max(this.walkSpeed, value5);
|
||||
} else {
|
||||
this.speedTransitionTicks.updateAndGet(result -> Math.max(result - 1, 0));
|
||||
}
|
||||
this.walkSpeed = value5;
|
||||
this.flySpeed = player.getFlySpeed() / 2.0F;
|
||||
} else {
|
||||
float value6;
|
||||
switch(this.entityType) {
|
||||
case PLAYER:
|
||||
value6 = ((Player) livingEntity2).getWalkSpeed() / 2.0F;
|
||||
break;
|
||||
case PIG:
|
||||
value6 = 0.25F;
|
||||
break;
|
||||
case HORSE:
|
||||
case SKELETON_HORSE:
|
||||
case DONKEY:
|
||||
case MULE:
|
||||
case LLAMA:
|
||||
value6 = 0.3375F;
|
||||
break;
|
||||
default:
|
||||
value6 = 0.0F;
|
||||
}
|
||||
if (value >= 0) {
|
||||
value6 = (float) (value6 * (1.0 + 0.2 * (value + 1)));
|
||||
}
|
||||
if (value2 >= 0) {
|
||||
value6 = (float) (value6 * (1.0 - 0.15 * (value2 + 1)));
|
||||
}
|
||||
this.walkSpeed = value6;
|
||||
this.flySpeed = player.getFlySpeed() / 2.0F;
|
||||
}
|
||||
switch(this.entityType) {
|
||||
case PLAYER:
|
||||
case PIG:
|
||||
case HORSE:
|
||||
case SKELETON_HORSE:
|
||||
case DONKEY:
|
||||
case MULE:
|
||||
case LLAMA:
|
||||
case STRIDER:
|
||||
this.unsupportedEntity = false;
|
||||
break;
|
||||
default:
|
||||
this.unsupportedEntity = true;
|
||||
}
|
||||
switch(this.entityType) {
|
||||
case PLAYER:
|
||||
case PIG:
|
||||
this.jumpStrength = 0.42F;
|
||||
break;
|
||||
case HORSE:
|
||||
this.jumpStrength = ((Horse) livingEntity2).getJumpStrength();
|
||||
break;
|
||||
case SKELETON_HORSE:
|
||||
this.jumpStrength = ((SkeletonHorse) livingEntity2).getJumpStrength();
|
||||
break;
|
||||
case DONKEY:
|
||||
this.jumpStrength = ((Donkey) livingEntity2).getJumpStrength();
|
||||
break;
|
||||
case MULE:
|
||||
this.jumpStrength = ((Mule) livingEntity2).getJumpStrength();
|
||||
break;
|
||||
case LLAMA:
|
||||
this.jumpStrength = ((Llama) livingEntity2).getJumpStrength();
|
||||
break;
|
||||
case STRIDER:
|
||||
this.jumpStrength = 0.42F;
|
||||
break;
|
||||
case ARMOR_STAND:
|
||||
this.jumpStrength = 0.0;
|
||||
break;
|
||||
default:
|
||||
this.jumpStrength = 0.0;
|
||||
}
|
||||
switch(this.entityType) {
|
||||
case PLAYER:
|
||||
this.stepHeight = 0.6;
|
||||
break;
|
||||
case PIG:
|
||||
case HORSE:
|
||||
case SKELETON_HORSE:
|
||||
case DONKEY:
|
||||
case MULE:
|
||||
case LLAMA:
|
||||
this.stepHeight = 1.0;
|
||||
break;
|
||||
case STRIDER:
|
||||
this.stepHeight = 1.0;
|
||||
default:
|
||||
this.stepHeight = 0.0;
|
||||
break;
|
||||
case ARMOR_STAND:
|
||||
this.stepHeight = 0.0;
|
||||
}
|
||||
if (livingEntity2 instanceof Player) {
|
||||
ItemStack boots = ((Player) livingEntity2).getInventory().getBoots();
|
||||
ItemStack chestplate = ((Player) livingEntity2).getInventory().getChestplate();
|
||||
this.depthStriderLevel = boots != null ? boots.getEnchantmentLevel(Enchantment.DEPTH_STRIDER) : 0;
|
||||
ProtocolVersion serverVersion3 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version5 = ProtocolVersion.V1_16;
|
||||
this.soulSpeedLevel = serverVersion3.isAtLeast(version5) && boots != null ? boots.getEnchantmentLevel(Enchantment.SOUL_SPEED) : 0;
|
||||
serverVersion3 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version6 = ProtocolVersion.V1_9;
|
||||
this.wearingElytra = serverVersion3.isAtLeast(version6) && chestplate != null && chestplate.getType() == Material.ELYTRA;
|
||||
}
|
||||
this.eyeHeight = player.getEyeHeight();
|
||||
}
|
||||
|
||||
public void updateInputFlags(Player player) {
|
||||
boolean matches = false;
|
||||
boolean matches2 = false;
|
||||
boolean matches3 = false;
|
||||
for (Entity entity : player.getNearbyEntities(3.0, 3.0, 3.0)) {
|
||||
if (entity.getType() == EntityType.FIREWORK) {
|
||||
matches = true;
|
||||
}
|
||||
if (entity.getType() == EntityType.BOAT) {
|
||||
matches2 = true;
|
||||
}
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_9;
|
||||
if (serverVersion.isAtLeast(version) && entity.getType() == EntityType.SHULKER) {
|
||||
matches3 = true;
|
||||
}
|
||||
}
|
||||
this.entityPushMagnitude = EntityPushCalculator.calculatePush(player).length();
|
||||
this.nearFirework = matches;
|
||||
this.nearBoat = matches2;
|
||||
this.nearShulker = matches3;
|
||||
WorldBorder worldBorder = player.getWorld().getWorldBorder();
|
||||
Location center = worldBorder.getCenter();
|
||||
double value = worldBorder.getSize() / 2.0;
|
||||
this.borderMinX = center.getX() - value;
|
||||
this.borderMinZ = center.getZ() - value;
|
||||
this.borderMaxX = center.getX() + value;
|
||||
this.borderMaxZ = center.getZ() + value;
|
||||
}
|
||||
|
||||
public void updateNearbyEntities(Player player) {
|
||||
ItemStack activeItem = NativeAccessProvider.getInstance().getActiveItem(player);
|
||||
boolean sneaking2 = player.isSneaking();
|
||||
boolean matches = activeItem != null;
|
||||
boolean gliding2 = NativeAccessProvider.getInstance().isGliding(player);
|
||||
boolean flying2 = player.isFlying();
|
||||
player.getAllowFlight();
|
||||
if (sneaking2 != this.sneaking) {
|
||||
this.sneaking = sneaking2;
|
||||
this.sneakTransitionTicks.updateAndGet(result -> Math.max(result, 2));
|
||||
} else {
|
||||
this.sneakTransitionTicks.updateAndGet(result -> Math.max(result - 1, 0));
|
||||
}
|
||||
if (matches != this.usingItem) {
|
||||
this.usingItem = matches;
|
||||
this.itemUseTransitionTicks.updateAndGet(result -> Math.max(result, 2));
|
||||
} else {
|
||||
this.itemUseTransitionTicks.updateAndGet(result -> Math.max(result - 1, 0));
|
||||
}
|
||||
if (this.gliding != gliding2) {
|
||||
this.gliding = gliding2;
|
||||
this.glideTransitionTicks.updateAndGet(result -> Math.max(result, 1));
|
||||
} else {
|
||||
this.glideTransitionTicks.updateAndGet(result -> Math.max(result - 1, 0));
|
||||
}
|
||||
if (this.flying != flying2 && this.flightTransitionTicks.get() <= 0) {
|
||||
this.flying = flying2;
|
||||
this.flightTransitionTicks.updateAndGet(result -> Math.max(result, 10));
|
||||
} else {
|
||||
this.flightTransitionTicks.updateAndGet(result -> Math.max(result - 1, 0));
|
||||
}
|
||||
this.sprinting = player.isSprinting();
|
||||
this.canSprint = player.getFoodLevel() > 6 && !player.hasPotionEffect(PotionEffectType.BLINDNESS);
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_13;
|
||||
if (serverVersion.isAtLeast(version) && matches) {
|
||||
this.riptideReady = activeItem.getType() == Material.TRIDENT && activeItem.getEnchantmentLevel(Enchantment.RIPTIDE) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public EntityType getEntityType() {
|
||||
return this.entityType;
|
||||
}
|
||||
|
||||
public EntityType getVehicleType() {
|
||||
return this.vehicleType;
|
||||
}
|
||||
|
||||
public EntityType getRootVehicleType() {
|
||||
return this.rootVehicleType;
|
||||
}
|
||||
|
||||
public LivingEntity getLivingEntity() {
|
||||
return this.livingEntity;
|
||||
}
|
||||
|
||||
public EntityDimensions getEntityDimensions() {
|
||||
return this.entityDimensions;
|
||||
}
|
||||
|
||||
public boolean isUnsupportedEntity() {
|
||||
return this.unsupportedEntity;
|
||||
}
|
||||
|
||||
public float getWalkSpeed() {
|
||||
return this.walkSpeed;
|
||||
}
|
||||
|
||||
public float getFlySpeed() {
|
||||
return this.flySpeed;
|
||||
}
|
||||
|
||||
public double getJumpStrength() {
|
||||
return this.jumpStrength;
|
||||
}
|
||||
|
||||
public double getStepHeight() {
|
||||
return this.stepHeight;
|
||||
}
|
||||
|
||||
public int getLevitationAmplifier() {
|
||||
return this.levitationAmplifier;
|
||||
}
|
||||
|
||||
public int getJumpBoostAmplifier() {
|
||||
return this.jumpBoostAmplifier;
|
||||
}
|
||||
|
||||
public boolean isSlowFalling() {
|
||||
return this.slowFalling;
|
||||
}
|
||||
|
||||
public boolean isDolphinsGrace() {
|
||||
return this.dolphinsGrace;
|
||||
}
|
||||
|
||||
public int getDepthStriderLevel() {
|
||||
return this.depthStriderLevel;
|
||||
}
|
||||
|
||||
public int getSoulSpeedLevel() {
|
||||
return this.soulSpeedLevel;
|
||||
}
|
||||
|
||||
public boolean isWearingElytra() {
|
||||
return this.wearingElytra;
|
||||
}
|
||||
|
||||
public double getEyeHeight() {
|
||||
return this.eyeHeight;
|
||||
}
|
||||
|
||||
public double getEntityPushMagnitude() {
|
||||
return this.entityPushMagnitude;
|
||||
}
|
||||
|
||||
public boolean isNearFirework() {
|
||||
return this.nearFirework;
|
||||
}
|
||||
|
||||
public boolean isNearBoat() {
|
||||
return this.nearBoat;
|
||||
}
|
||||
|
||||
public boolean isNearShulker() {
|
||||
return this.nearShulker;
|
||||
}
|
||||
|
||||
public Boolean getStableSneaking() {
|
||||
return this.sneakTransitionTicks.get() > 0 ? null : this.sneaking;
|
||||
}
|
||||
|
||||
public Boolean getStableUsingItem() {
|
||||
return this.itemUseTransitionTicks.get() > 0 ? null : this.usingItem;
|
||||
}
|
||||
|
||||
public Boolean getStableGliding() {
|
||||
return this.glideTransitionTicks.get() > 0 ? null : this.gliding;
|
||||
}
|
||||
|
||||
public Boolean getFlyingState() {
|
||||
return this.flying;
|
||||
}
|
||||
|
||||
public void deferSneakState(int value) {
|
||||
this.sneakTransitionTicks.updateAndGet(result -> Math.max(result, value));
|
||||
}
|
||||
|
||||
public void deferItemUseState(int value) {
|
||||
this.itemUseTransitionTicks.updateAndGet(result -> Math.max(result, value));
|
||||
}
|
||||
|
||||
public void deferGlideState(int value) {
|
||||
this.glideTransitionTicks.updateAndGet(result -> Math.max(result, value));
|
||||
}
|
||||
|
||||
public void deferFlightState(int value, boolean matches) {
|
||||
this.flightTransitionTicks.updateAndGet(result -> Math.max(result, value));
|
||||
this.flying = matches;
|
||||
}
|
||||
|
||||
public void resetGlideTransition() {
|
||||
this.glideTransitionTicks.set(0);
|
||||
}
|
||||
|
||||
public boolean isSprinting() {
|
||||
return this.sprinting;
|
||||
}
|
||||
|
||||
public boolean canSprint() {
|
||||
return this.canSprint;
|
||||
}
|
||||
|
||||
public boolean isRiptideReady() {
|
||||
return this.riptideReady;
|
||||
}
|
||||
|
||||
public boolean crossesWorldBorder(double value, double value2, double value3) {
|
||||
return value + value3 > this.borderMaxX || value - value3 < this.borderMinX || value2 + value3 > this.borderMaxZ || value2 - value3 < this.borderMinZ;
|
||||
}
|
||||
|
||||
public float getMaximumRecentWalkSpeed() {
|
||||
return this.maximumRecentWalkSpeed;
|
||||
}
|
||||
|
||||
public boolean hasRecentSpeedChange() {
|
||||
return this.speedTransitionTicks.get() > 0;
|
||||
}
|
||||
|
||||
public void resetSpeedTransition() {
|
||||
this.speedTransitionTicks.set(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
class PlayerDimensions {
|
||||
|
||||
public static final EntityDimensions STANDING = new EntityDimensions(0.6F, 1.8F);
|
||||
|
||||
public static final EntityDimensions SNEAKING_LEGACY = new EntityDimensions(0.6F, 1.65F);
|
||||
|
||||
public static final EntityDimensions SNEAKING = new EntityDimensions(0.6F, 1.5);
|
||||
|
||||
public static final EntityDimensions SWIMMING = new EntityDimensions(0.6F, 0.6F);
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class PlayerInputState {
|
||||
|
||||
public static final PlayerInputState EMPTY = new PlayerInputState(0.0F, 0.0F, false, false, false, false, false, false, 0.0F, 0.0F, 0);
|
||||
|
||||
private final float forward;
|
||||
|
||||
private final float strafe;
|
||||
|
||||
private final boolean sprinting;
|
||||
|
||||
private final boolean jumping;
|
||||
|
||||
private final boolean sneaking;
|
||||
|
||||
private final boolean usingItem;
|
||||
|
||||
private final boolean elytra;
|
||||
|
||||
private final boolean flying;
|
||||
|
||||
private final float yaw;
|
||||
|
||||
private final float pitch;
|
||||
|
||||
private final int attackedEntityId;
|
||||
|
||||
public static String UNKNOWN_DESCRIPTION = "(?) (?, ?) ?";
|
||||
|
||||
public PlayerInputState(float forward, float strafe, boolean sprinting, boolean jumping, boolean sneaking, boolean usingItem, boolean elytra, boolean flying, float yaw, float pitch, int attackedEntityId) {
|
||||
this.forward = forward;
|
||||
this.strafe = strafe;
|
||||
this.sprinting = sprinting;
|
||||
this.jumping = jumping;
|
||||
this.sneaking = sneaking;
|
||||
this.usingItem = usingItem;
|
||||
this.elytra = elytra;
|
||||
this.flying = flying;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.attackedEntityId = attackedEntityId;
|
||||
}
|
||||
|
||||
public float getForward() {
|
||||
return this.forward;
|
||||
}
|
||||
|
||||
public float getStrafe() {
|
||||
return this.strafe;
|
||||
}
|
||||
|
||||
public boolean isSprinting() {
|
||||
return this.sprinting;
|
||||
}
|
||||
|
||||
public boolean isJumping() {
|
||||
return this.jumping;
|
||||
}
|
||||
|
||||
public boolean isSneaking() {
|
||||
return this.sneaking;
|
||||
}
|
||||
|
||||
public boolean isUsingItem() {
|
||||
return this.usingItem;
|
||||
}
|
||||
|
||||
public boolean isElytra() {
|
||||
return this.elytra;
|
||||
}
|
||||
|
||||
public boolean isFlying() {
|
||||
return this.flying;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return this.yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return this.pitch;
|
||||
}
|
||||
|
||||
public int getAttackedEntityId() {
|
||||
return this.attackedEntityId;
|
||||
}
|
||||
|
||||
public PlayerInputState copy() {
|
||||
return this.isJumping() ? this : new PlayerInputState(this.forward, this.strafe, this.sprinting, true, this.sneaking, this.usingItem, this.elytra, this.flying, this.yaw, this.pitch, this.attackedEntityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object result) {
|
||||
if (this == result) {
|
||||
return true;
|
||||
} else if (result != null && this.getClass() == result.getClass()) {
|
||||
PlayerInputState playerInputState = (PlayerInputState) result;
|
||||
if (Float.compare(playerInputState.forward, this.forward) != 0) {
|
||||
return false;
|
||||
} else if (Float.compare(playerInputState.strafe, this.strafe) != 0) {
|
||||
return false;
|
||||
} else if (this.sprinting != playerInputState.sprinting) {
|
||||
return false;
|
||||
} else if (this.jumping != playerInputState.jumping) {
|
||||
return false;
|
||||
} else if (this.sneaking != playerInputState.sneaking) {
|
||||
return false;
|
||||
} else if (this.usingItem != playerInputState.usingItem) {
|
||||
return false;
|
||||
} else if (this.elytra != playerInputState.elytra) {
|
||||
return false;
|
||||
} else if (this.flying != playerInputState.flying) {
|
||||
return false;
|
||||
} else if (Float.compare(playerInputState.yaw, this.yaw) != 0) {
|
||||
return false;
|
||||
} else {
|
||||
return Float.compare(playerInputState.pitch, this.pitch) != 0 ? false : this.attackedEntityId == playerInputState.attackedEntityId;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int value = this.forward != 0.0F ? Float.floatToIntBits(this.forward) : 0;
|
||||
value = 31 * value + (this.strafe != 0.0F ? Float.floatToIntBits(this.strafe) : 0);
|
||||
value = 31 * value + (this.sprinting ? 1 : 0);
|
||||
value = 31 * value + (this.jumping ? 1 : 0);
|
||||
value = 31 * value + (this.sneaking ? 1 : 0);
|
||||
value = 31 * value + (this.usingItem ? 1 : 0);
|
||||
value = 31 * value + (this.elytra ? 1 : 0);
|
||||
value = 31 * value + (this.flying ? 1 : 0);
|
||||
value = 31 * value + (this.yaw != 0.0F ? Float.floatToIntBits(this.yaw) : 0);
|
||||
value = 31 * value + (this.pitch != 0.0F ? Float.floatToIntBits(this.pitch) : 0);
|
||||
return 31 * value + this.attackedEntityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append('(');
|
||||
if (this.forward > 0.0F) {
|
||||
stringBuilder.append('W');
|
||||
} else if (this.forward < 0.0F) {
|
||||
stringBuilder.append('S');
|
||||
}
|
||||
if (this.strafe > 0.0F) {
|
||||
stringBuilder.append('A');
|
||||
} else if (this.strafe < 0.0F) {
|
||||
stringBuilder.append('D');
|
||||
}
|
||||
stringBuilder.append(')');
|
||||
stringBuilder.append(String.format(Locale.UK, " (%.2f, %.2f)", this.yaw, this.pitch));
|
||||
if (this.sprinting) {
|
||||
stringBuilder.append(" sprint");
|
||||
}
|
||||
if (this.jumping) {
|
||||
stringBuilder.append(" jump");
|
||||
}
|
||||
if (this.sneaking) {
|
||||
stringBuilder.append(" sneak");
|
||||
}
|
||||
if (this.usingItem) {
|
||||
stringBuilder.append(" use");
|
||||
}
|
||||
if (this.elytra) {
|
||||
stringBuilder.append(" elytra");
|
||||
}
|
||||
if (this.flying) {
|
||||
stringBuilder.append(" fly");
|
||||
}
|
||||
if (this.attackedEntityId > 0) {
|
||||
stringBuilder.append(" attack:").append(this.attackedEntityId);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public enum PlayerPose {
|
||||
|
||||
STANDING(result -> PlayerDimensions.STANDING, result -> 1.62F), SNEAKING(result -> {
|
||||
ProtocolVersion version = ProtocolVersion.V1_14;
|
||||
if (result.isAtLeast(version)) {
|
||||
return PlayerDimensions.SNEAKING;
|
||||
} else {
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_9;
|
||||
return result.isAtLeast(version2) ? PlayerDimensions.SNEAKING_LEGACY : PlayerDimensions.STANDING;
|
||||
}
|
||||
}, result -> {
|
||||
ProtocolVersion version = ProtocolVersion.V1_14;
|
||||
return result.isAtLeast(version) ? 1.27F : 1.54F;
|
||||
}), FALL_FLYING(result -> PlayerDimensions.SWIMMING, result -> 0.4F), SWIMMING(result -> PlayerDimensions.SWIMMING, result -> 0.4F);
|
||||
|
||||
public static List<PlayerPose> POSES = Collections.unmodifiableList(Arrays.asList(values()));
|
||||
|
||||
private final Function<ProtocolVersion, EntityDimensions> dimensionsFunction;
|
||||
|
||||
private final Function<ProtocolVersion, Float> eyeHeightFunction;
|
||||
|
||||
private PlayerPose(Function<ProtocolVersion, EntityDimensions> dimensionsFunction, Function<ProtocolVersion, Float> eyeHeightFunction) {
|
||||
this.dimensionsFunction = dimensionsFunction;
|
||||
this.eyeHeightFunction = eyeHeightFunction;
|
||||
}
|
||||
|
||||
public EntityDimensions findEntityDimensions(ProtocolVersion version) {
|
||||
return (EntityDimensions) this.dimensionsFunction.apply(version);
|
||||
}
|
||||
|
||||
public float calculateValue(ProtocolVersion version) {
|
||||
return (Float) this.eyeHeightFunction.apply(version);
|
||||
}
|
||||
|
||||
public boolean checkPlayerPose(Vector vector, MovementEnvironment movementEnvironment) {
|
||||
AxisAlignedBoundingBox box = new AxisAlignedBoundingBox(vector, this.findEntityDimensions(movementEnvironment.getProtocolVersion()));
|
||||
return movementEnvironment.collides(box);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
public class RotationDelta {
|
||||
|
||||
private final float yaw;
|
||||
|
||||
private final float pitch;
|
||||
|
||||
public RotationDelta(float yaw, float pitch) {
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return this.yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return this.pitch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package me.konsolas.aac.movement;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class TimedVelocity {
|
||||
|
||||
private final Vector velocity;
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
public TimedVelocity(Vector velocity, long timestamp) {
|
||||
this.velocity = velocity;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Vector getVector() {
|
||||
return this.velocity.clone();
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package me.konsolas.aac.player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerLoginInfo {
|
||||
|
||||
private final UUID playerId;
|
||||
|
||||
private final long firstSeen;
|
||||
|
||||
private final String address;
|
||||
|
||||
private final long lastSeen;
|
||||
|
||||
private final boolean exempt;
|
||||
|
||||
public PlayerLoginInfo(UUID playerId, long firstSeen, String address, long lastSeen, boolean exempt) {
|
||||
this.playerId = playerId;
|
||||
this.firstSeen = firstSeen;
|
||||
this.address = address;
|
||||
this.lastSeen = lastSeen;
|
||||
this.exempt = exempt;
|
||||
}
|
||||
|
||||
public UUID getPlayerId() {
|
||||
return this.playerId;
|
||||
}
|
||||
|
||||
public long getFirstSeen() {
|
||||
return this.firstSeen;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public long getLastSeen() {
|
||||
return this.lastSeen;
|
||||
}
|
||||
|
||||
public boolean isExempt() {
|
||||
return this.exempt;
|
||||
}
|
||||
|
||||
public PlayerLoginInfo copy() {
|
||||
return new PlayerLoginInfo(this.playerId, this.firstSeen, this.address, this.lastSeen, !this.exempt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.konsolas.aac.protocol;
|
||||
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public enum BlockDirection {
|
||||
|
||||
DOWN,
|
||||
BLOCK_DIRECTION,
|
||||
NORTH,
|
||||
SOUTH,
|
||||
WEST,
|
||||
EAST;
|
||||
|
||||
private static final List DIRECTIONS = Collections.unmodifiableList(Arrays.asList(values()));
|
||||
|
||||
static BlockDirection create(Direction direction) {
|
||||
return (BlockDirection) DIRECTIONS.get(direction.ordinal());
|
||||
}
|
||||
|
||||
static BlockDirection create(int value) {
|
||||
return (BlockDirection) DIRECTIONS.get(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.konsolas.aac.protocol;
|
||||
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
|
||||
public class BlockInteraction {
|
||||
|
||||
private final BlockPosition position;
|
||||
|
||||
private final BlockPosition adjacentPosition;
|
||||
|
||||
private final InteractionHand hand;
|
||||
|
||||
public BlockInteraction(BlockPosition position, BlockDirection blockDirection, InteractionHand hand) {
|
||||
this.position = position;
|
||||
this.adjacentPosition = PacketReader.findBlockPosition(position, blockDirection);
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
public BlockPosition getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
public BlockPosition getAdjacentPosition() {
|
||||
return this.adjacentPosition;
|
||||
}
|
||||
|
||||
public InteractionHand getHand() {
|
||||
return this.hand;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package me.konsolas.aac.protocol;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Play.Client;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class EntityVelocityPacket {
|
||||
|
||||
private final Vector velocity;
|
||||
|
||||
private EntityVelocityPacket(Vector velocity) {
|
||||
this.velocity = velocity;
|
||||
}
|
||||
|
||||
public static EntityVelocityPacket create(PacketContainer packet) {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_14;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
if (packet.getType() == PacketReader.USE_ITEM) {
|
||||
MovingObjectPositionBlock movingObjectPositionBlock = (MovingObjectPositionBlock) packet.getMovingBlockPositions().read(0);
|
||||
return new EntityVelocityPacket(movingObjectPositionBlock.getPosVector());
|
||||
}
|
||||
} else {
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_9;
|
||||
if (serverVersion.isAtLeast(version2) && packet.getType() == PacketReader.USE_ITEM || ProtocolVersion.getServerVersion() == ProtocolVersion.V1_8 && packet.getType() == Client.BLOCK_PLACE) {
|
||||
BlockPosition blockPosition = (BlockPosition) packet.getBlockPositionModifier().read(0);
|
||||
double value = blockPosition.getX() + (Float) packet.getFloat().read(0);
|
||||
double value2 = blockPosition.getY() + (Float) packet.getFloat().read(1);
|
||||
double value3 = blockPosition.getZ() + (Float) packet.getFloat().read(2);
|
||||
return new EntityVelocityPacket(new Vector(value, value2, value3));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Vector getVelocity() {
|
||||
return this.velocity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.konsolas.aac.protocol;
|
||||
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public enum InteractionHand {
|
||||
|
||||
MAIN_HAND, OFF_HAND;
|
||||
|
||||
private static final List HANDS = Collections.unmodifiableList(Arrays.asList(values()));
|
||||
|
||||
static InteractionHand create(Hand hand) {
|
||||
return (InteractionHand) HANDS.get(hand.ordinal());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package me.konsolas.aac.protocol;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Play.Client;
|
||||
import com.comphenix.protocol.PacketType.Play.Server;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.api.AACExemption;
|
||||
import me.konsolas.aac.checks.PlayerChecks;
|
||||
import me.konsolas.aac.compat.integration.OptionalIntegrations;
|
||||
import me.konsolas.aac.debug.DebugManager;
|
||||
import me.konsolas.aac.timings.AACListener;
|
||||
import me.konsolas.aac.util.math.MinecraftMath;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class PacketDispatcher {
|
||||
|
||||
private static final AACExemption CREATIVE_MODE = new AACExemption("The player is in creative mode");
|
||||
|
||||
private static final AACExemption SPECTATOR_MODE = new AACExemption("The player is in spectator mode");
|
||||
|
||||
private static final AACExemption SLEEPING = new AACExemption("The player is sleeping");
|
||||
|
||||
private static final AACExemption DEAD_PLAYER = new AACExemption("The player is dead");
|
||||
|
||||
private static final AACExemption BYPASS_PERMISSION = new AACExemption("The player had the aac.bypass permission when they joined");
|
||||
|
||||
private static final AACExemption BEDROCK_CLIENT = new AACExemption("The player is connected from Bedrock via GeyserMC");
|
||||
|
||||
public static final AACExemption COMMAND_EXEMPTION = new AACExemption("The player has been exempted by the command /aac exempt");
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
private final Map playerChecks = new ConcurrentHashMap();
|
||||
|
||||
private final Map exemptions = new ConcurrentHashMap();
|
||||
|
||||
private final Cache pistonPushes;
|
||||
|
||||
private final Cache slimePushes;
|
||||
|
||||
private final boolean floodgateAvailable;
|
||||
|
||||
private final Map pendingTeleportIds;
|
||||
|
||||
private final Map teleportOrigins = new ConcurrentHashMap();
|
||||
|
||||
private long tick = 0L;
|
||||
|
||||
public PacketDispatcher(AAC plugin) {
|
||||
this.plugin = plugin;
|
||||
AACListener aACListener = new AACListener(this);
|
||||
plugin.getServer().getPluginManager().registerEvents(aACListener, plugin);
|
||||
ProtocolLibrary.getProtocolManager().addPacketListener(aACListener);
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_9;
|
||||
this.pendingTeleportIds = serverVersion.isAtLeast(version) ? new ConcurrentHashMap() : null;
|
||||
long value = (long) (plugin.getConfig().getDouble("checks.move.piston_wait_time") * 1000.0);
|
||||
this.pistonPushes = CacheBuilder.newBuilder().expireAfterWrite(value, TimeUnit.MILLISECONDS).build();
|
||||
this.slimePushes = CacheBuilder.newBuilder().expireAfterWrite(value, TimeUnit.MILLISECONDS).build();
|
||||
Plugin plugin2 = Bukkit.getPluginManager().getPlugin("floodgate");
|
||||
if (this.floodgateAvailable = plugin2 != null) {
|
||||
Bukkit.getLogger().info("[AAC5] Detected floodgate " + plugin2.getDescription().getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
public AAC getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
public void addExemption(Player player, AACExemption aACExemption) {
|
||||
((Set) this.exemptions.get(player.getUniqueId())).add(aACExemption);
|
||||
}
|
||||
|
||||
public void removeExemption(Player player, AACExemption aACExemption) {
|
||||
((Set) this.exemptions.get(player.getUniqueId())).remove(aACExemption);
|
||||
}
|
||||
|
||||
public boolean shouldCheck(Player player) {
|
||||
Set values = (Set) this.exemptions.get(player.getUniqueId());
|
||||
if (values == null) {
|
||||
this.initializePlayer(player);
|
||||
values = (Set) this.exemptions.get(player.getUniqueId());
|
||||
}
|
||||
if (player.getGameMode() == GameMode.CREATIVE) {
|
||||
values.add(CREATIVE_MODE);
|
||||
} else {
|
||||
values.remove(CREATIVE_MODE);
|
||||
}
|
||||
if (player.getGameMode() == GameMode.SPECTATOR) {
|
||||
values.add(SPECTATOR_MODE);
|
||||
} else {
|
||||
values.remove(SPECTATOR_MODE);
|
||||
}
|
||||
if (player.isSleeping()) {
|
||||
values.add(SLEEPING);
|
||||
} else {
|
||||
values.remove(SLEEPING);
|
||||
}
|
||||
if (player.isDead()) {
|
||||
values.add(DEAD_PLAYER);
|
||||
} else {
|
||||
values.remove(DEAD_PLAYER);
|
||||
}
|
||||
if (!values.isEmpty()) {
|
||||
if (this.playerChecks.remove(player.getUniqueId()) != null && this.getPlugin().getDebugManager().isDebugging(player)) {
|
||||
this.getPlugin().getDebugManager().log(player, "dispatch: now exempt");
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (!this.playerChecks.containsKey(player.getUniqueId())) {
|
||||
if (this.getPlugin().getDebugManager().isDebugging(player)) {
|
||||
this.getPlugin().getDebugManager().log(player, "dispatch: now checking");
|
||||
}
|
||||
Map entries = this.playerChecks;
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
AAC aacPlugin = this.plugin;
|
||||
entries.put(uniqueId, new PlayerChecks(aacPlugin, player));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Set<AACExemption> getExemptions(Player player) {
|
||||
return Collections.unmodifiableSet((Set) this.exemptions.get(player.getUniqueId()));
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
this.tick++;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (this.shouldCheck(player)) {
|
||||
PlayerChecks playerChecks2 = (PlayerChecks) this.playerChecks.get(player.getUniqueId());
|
||||
long value = this.tick;
|
||||
playerChecks2.updateTime(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleEvent(Player player, Event event) {
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
if (this.playerChecks.containsKey(uniqueId) && ((Set) this.exemptions.get(uniqueId)).isEmpty()) {
|
||||
((PlayerChecks) this.playerChecks.get(player.getUniqueId())).onEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePacket(PacketEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
UUID uniqueId = player.getUniqueId();
|
||||
PacketContainer packet = event.getPacket();
|
||||
PacketType type = packet.getType();
|
||||
if (this.pendingTeleportIds != null && type == PacketReader.TELEPORT_ACCEPT) {
|
||||
int value = (Integer) packet.getIntegers().read(0);
|
||||
boolean matches = this.pendingTeleportIds.remove(uniqueId, value);
|
||||
DebugManager debugManager = this.getPlugin().getDebugManager();
|
||||
if (debugManager.isDebugging(player)) {
|
||||
this.getPlugin().getDebugManager().log(player, "dispatch: confirmed teleport " + value + ": " + matches);
|
||||
}
|
||||
} else if (PacketReader.checkPacketReader(type) || type == PacketReader.VEHICLE_MOVE) {
|
||||
Location location = (Location) this.teleportOrigins.get(uniqueId);
|
||||
if (location != null) {
|
||||
Location location2 = null;
|
||||
if (type == Client.POSITION_LOOK) {
|
||||
World world = location.getWorld();
|
||||
double value2 = (Double) packet.getDoubles().read(0);
|
||||
double value3 = (Double) packet.getDoubles().read(1);
|
||||
double value4 = (Double) packet.getDoubles().read(2);
|
||||
float value5 = (Float) packet.getFloat().read(0) % 360.0F;
|
||||
float value6 = (Float) packet.getFloat().read(1);
|
||||
float value7 = -90.0F;
|
||||
float value8 = value6;
|
||||
location2 = new Location(world, value2, value3, value4, value5, MinecraftMath.clamp(value8, value7, (float) 90.0F));
|
||||
if (this.pendingTeleportIds != null && !this.pendingTeleportIds.containsKey(uniqueId) || this.pendingTeleportIds == null && location2.distanceSquared(location) == 0.0) {
|
||||
if (this.getPlugin().getDebugManager().isDebugging(player)) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(player, "dispatch: accepted teleport: %.4f %.4f %.4f (pos: %.4f %.4f %.4f)", new Object[] { location2.getX(), location2.getY(), location2.getZ(), location.getX(), location.getY(), location.getZ() });
|
||||
}
|
||||
this.teleportOrigins.remove(uniqueId);
|
||||
PlayerChecks playerChecks2 = (PlayerChecks) this.playerChecks.get(uniqueId);
|
||||
if (playerChecks2 != null) {
|
||||
playerChecks2.updateLocationWithMovementCheck(location);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.teleportOrigins.containsKey(uniqueId)) {
|
||||
if (this.getPlugin().getDebugManager().isDebugging(player)) {
|
||||
String text = this.pendingTeleportIds != null ? "unconfirmed" : "unresolved";
|
||||
if (location2 != null) {
|
||||
this.getPlugin().getDebugManager().updatePlayer(event.getPlayer(), "dispatch: " + text + " teleport: %.4f,%.4f,%.4f (pos: %.4f %.4f %.4f)", new Object[] { location2.getX(), location2.getY(), location2.getZ(), location.getX(), location.getY(), location.getZ() });
|
||||
}
|
||||
}
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (type == Server.POSITION) {
|
||||
Location location3 = player.getLocation();
|
||||
this.teleportOrigins.put(uniqueId, location3);
|
||||
PlayerChecks playerChecks3 = (PlayerChecks) this.playerChecks.get(uniqueId);
|
||||
if (playerChecks3 != null) {
|
||||
playerChecks3.updateLocation(location3);
|
||||
}
|
||||
Object result;
|
||||
if (this.pendingTeleportIds != null) {
|
||||
result = (Integer) packet.getIntegers().read(0);
|
||||
this.pendingTeleportIds.put(event.getPlayer().getUniqueId(), result);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
DebugManager debugManager2 = this.getPlugin().getDebugManager();
|
||||
Player player2 = event.getPlayer();
|
||||
Player player3 = player2;
|
||||
if (debugManager2.isDebugging(player3)) {
|
||||
assert location3.getWorld() != null;
|
||||
this.getPlugin().getDebugManager().updatePlayer(event.getPlayer(), "dispatch: teleported to " + location3.getWorld().getName() + ": %.4f,%.4f,%.4f " + (result != null ? result : ""), new Object[] { location3.getX(), location3.getY(), location3.getZ() });
|
||||
}
|
||||
}
|
||||
if (this.playerChecks.containsKey(uniqueId) && ((Set) this.exemptions.get(uniqueId)).isEmpty()) {
|
||||
((PlayerChecks) this.playerChecks.get(event.getPlayer().getUniqueId())).handlePacket(event);
|
||||
}
|
||||
}
|
||||
|
||||
public Cache getPistonPushes() {
|
||||
return this.pistonPushes;
|
||||
}
|
||||
|
||||
public Cache getSlimePushes() {
|
||||
return this.slimePushes;
|
||||
}
|
||||
|
||||
public void initializePlayer(Player player) {
|
||||
this.exemptions.put(player.getUniqueId(), ConcurrentHashMap.newKeySet());
|
||||
if (player.hasPermission("aac.bypass")) {
|
||||
((Set) this.exemptions.get(player.getUniqueId())).add(BYPASS_PERMISSION);
|
||||
}
|
||||
if (this.floodgateAvailable && OptionalIntegrations.isFloodgatePlayer(player.getUniqueId())) {
|
||||
((Set) this.exemptions.get(player.getUniqueId())).add(BEDROCK_CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayer(Player player) {
|
||||
this.exemptions.remove(player.getUniqueId());
|
||||
this.playerChecks.remove(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package me.konsolas.aac.protocol;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Play.Client;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
|
||||
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
|
||||
import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction;
|
||||
import java.util.Objects;
|
||||
import me.konsolas.aac.util.math.MinecraftMath;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class PacketReader {
|
||||
|
||||
public static final PacketType VEHICLE_MOVE;
|
||||
|
||||
public static final PacketType USE_ITEM;
|
||||
|
||||
public static final PacketType TELEPORT_ACCEPT;
|
||||
|
||||
public static final PacketType GROUND;
|
||||
|
||||
public static boolean checkPacketReader(PacketContainer packet) {
|
||||
if (packet.getType() != Client.USE_ENTITY) {
|
||||
return false;
|
||||
} else {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_17;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
WrappedEnumEntityUseAction wrappedEnumEntityUseAction = (WrappedEnumEntityUseAction) packet.getEnumEntityUseActions().read(0);
|
||||
return wrappedEnumEntityUseAction.getAction() == EntityUseAction.ATTACK;
|
||||
} else {
|
||||
return packet.getEntityUseActions().read(0) == EntityUseAction.ATTACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkPacketReader(PacketType packetType) {
|
||||
return packetType == Client.POSITION || packetType == Client.POSITION_LOOK || packetType == Client.LOOK || packetType == GROUND;
|
||||
}
|
||||
|
||||
public static Location findLocation(PacketContainer packet, Location location) {
|
||||
Vector vector;
|
||||
if (packet.getType() != Client.POSITION && packet.getType() != Client.POSITION_LOOK && packet.getType() != VEHICLE_MOVE) {
|
||||
vector = location.toVector();
|
||||
} else {
|
||||
vector = new Vector((Double) packet.getDoubles().read(0), (Double) packet.getDoubles().read(1), (Double) packet.getDoubles().read(2));
|
||||
}
|
||||
float value;
|
||||
float value2;
|
||||
if (packet.getType() != Client.LOOK && packet.getType() != Client.POSITION_LOOK && packet.getType() != VEHICLE_MOVE) {
|
||||
value2 = location.getYaw();
|
||||
value = location.getPitch();
|
||||
} else {
|
||||
value2 = (Float) packet.getFloat().read(0) % 360.0F;
|
||||
float value3 = (Float) packet.getFloat().read(1);
|
||||
float value4 = value3;
|
||||
value = MinecraftMath.clamp(value4, -90.0F, 90.0F);
|
||||
}
|
||||
return vector.toLocation(Objects.requireNonNull(location.getWorld()), value2, value);
|
||||
}
|
||||
|
||||
public static Vector findVector(PacketContainer packet) {
|
||||
int value = (Integer) packet.getIntegers().read(1);
|
||||
int value2 = (Integer) packet.getIntegers().read(2);
|
||||
int value3 = (Integer) packet.getIntegers().read(3);
|
||||
return new Vector(value / 8000.0, value2 / 8000.0, value3 / 8000.0);
|
||||
}
|
||||
|
||||
public static BlockInteraction findBlockInteraction(PacketContainer packet) {
|
||||
if (packet.getBlockPositionModifier().size() > 0) {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_9;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
Object result = packet.getBlockPositionModifier().read(0);
|
||||
result = (BlockPosition) result;
|
||||
BlockDirection blockDirection = BlockDirection.create((Direction) packet.getDirections().read(0));
|
||||
InteractionHand interactionHand = InteractionHand.create((Hand) packet.getHands().read(0));
|
||||
BlockDirection blockDirection2 = blockDirection;
|
||||
BlockPosition blockPosition = (BlockPosition) result;
|
||||
return new BlockInteraction(blockPosition, blockDirection2, interactionHand);
|
||||
}
|
||||
if ((Integer) packet.getIntegers().read(0) < 6) {
|
||||
Object result2 = packet.getBlockPositionModifier().read(0);
|
||||
result2 = (BlockPosition) result2;
|
||||
BlockDirection blockDirection3 = BlockDirection.create((Integer) packet.getIntegers().read(0));
|
||||
InteractionHand interactionHand2 = InteractionHand.MAIN_HAND;
|
||||
BlockDirection blockDirection4 = blockDirection3;
|
||||
BlockPosition blockPosition2 = (BlockPosition) result2;
|
||||
return new BlockInteraction(blockPosition2, blockDirection4, interactionHand2);
|
||||
}
|
||||
} else {
|
||||
ProtocolVersion serverVersion2 = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_14;
|
||||
if (serverVersion2.isAtLeast(version2) && packet.getType() == USE_ITEM) {
|
||||
MovingObjectPositionBlock movingObjectPositionBlock = (MovingObjectPositionBlock) packet.getMovingBlockPositions().read(0);
|
||||
BlockPosition blockPosition3 = movingObjectPositionBlock.getBlockPosition();
|
||||
BlockDirection blockDirection5 = BlockDirection.create(movingObjectPositionBlock.getDirection());
|
||||
InteractionHand interactionHand3 = InteractionHand.create((Hand) packet.getHands().read(0));
|
||||
BlockDirection blockDirection6 = blockDirection5;
|
||||
BlockPosition blockPosition4 = blockPosition3;
|
||||
return new BlockInteraction(blockPosition4, blockDirection6, interactionHand3);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BlockPosition findBlockPositionWithVALUESS(BlockPosition blockPosition, BlockDirection blockDirection) {
|
||||
switch(blockDirection) {
|
||||
case DOWN:
|
||||
return new BlockPosition(blockPosition.getX(), blockPosition.getY() - 1, blockPosition.getZ());
|
||||
case BLOCK_DIRECTION:
|
||||
return new BlockPosition(blockPosition.getX(), blockPosition.getY() + 1, blockPosition.getZ());
|
||||
case NORTH:
|
||||
return new BlockPosition(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ() - 1);
|
||||
case SOUTH:
|
||||
return new BlockPosition(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ() + 1);
|
||||
case WEST:
|
||||
return new BlockPosition(blockPosition.getX() - 1, blockPosition.getY(), blockPosition.getZ());
|
||||
case EAST:
|
||||
return new BlockPosition(blockPosition.getX() + 1, blockPosition.getY(), blockPosition.getZ());
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
static BlockPosition findBlockPosition(BlockPosition blockPosition, BlockDirection blockDirection) {
|
||||
return findBlockPositionWithVALUESS(blockPosition, blockDirection);
|
||||
}
|
||||
|
||||
static {
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_9;
|
||||
VEHICLE_MOVE = serverVersion.isAtLeast(version) ? Client.VEHICLE_MOVE : null;
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version2 = ProtocolVersion.V1_9;
|
||||
USE_ITEM = serverVersion.isAtLeast(version2) ? Client.USE_ITEM : null;
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version3 = ProtocolVersion.V1_9;
|
||||
TELEPORT_ACCEPT = serverVersion.isAtLeast(version3) ? Client.TELEPORT_ACCEPT : null;
|
||||
serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version4 = ProtocolVersion.V1_16;
|
||||
GROUND = serverVersion.isAtLeast(version4) ? Client.GROUND : Client.FLYING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package me.konsolas.aac.protocol;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import me.konsolas.aac.compat.integration.OptionalIntegrations;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public enum ProtocolVersion {
|
||||
|
||||
V1_8(1, 8),
|
||||
V1_9(1, 9),
|
||||
V1_10(1, 10),
|
||||
V1_11(1, 11),
|
||||
V1_12(1, 12),
|
||||
V1_13(1, 13),
|
||||
V1_14(1, 14),
|
||||
V1_15(1, 15),
|
||||
V1_16(1, 16),
|
||||
V1_17(1, 17);
|
||||
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*");
|
||||
|
||||
private static final boolean VIA_VERSION_AVAILABLE = detectIntegration("ViaVersion");
|
||||
|
||||
private static final boolean PROTOCOL_SUPPORT_AVAILABLE = detectIntegration("ProtocolSupport");
|
||||
|
||||
private static final ProtocolVersion SERVER_VERSION = detectServerVersion();
|
||||
|
||||
private final int major;
|
||||
|
||||
private final int minor;
|
||||
|
||||
ProtocolVersion(int major, int minor) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
}
|
||||
|
||||
public int getMajor() {
|
||||
return major;
|
||||
}
|
||||
|
||||
public int getMinor() {
|
||||
return minor;
|
||||
}
|
||||
|
||||
public static ProtocolVersion getServerVersion() {
|
||||
return SERVER_VERSION;
|
||||
}
|
||||
|
||||
public boolean isAtLeast(ProtocolVersion version) {
|
||||
return version != null && compareTo(version) >= 0;
|
||||
}
|
||||
|
||||
public static ProtocolVersion detectPlayerVersion(Player player) {
|
||||
if (VIA_VERSION_AVAILABLE) {
|
||||
ProtocolVersion version = fromProtocolId(OptionalIntegrations.viaVersion(player));
|
||||
if (version != SERVER_VERSION)
|
||||
return version;
|
||||
}
|
||||
if (PROTOCOL_SUPPORT_AVAILABLE) {
|
||||
ProtocolVersion version = fromProtocolId(OptionalIntegrations.protocolSupportVersion(player));
|
||||
if (version != SERVER_VERSION)
|
||||
return version;
|
||||
}
|
||||
return SERVER_VERSION;
|
||||
}
|
||||
|
||||
private static boolean detectIntegration(String name) {
|
||||
Plugin plugin = Bukkit.getPluginManager().getPlugin(name);
|
||||
if (plugin == null)
|
||||
return false;
|
||||
Bukkit.getLogger().info("[AAC5] Detected " + name + (name.equals("ViaVersion") ? " " + plugin.getDescription().getVersion() : ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ProtocolVersion detectServerVersion() {
|
||||
Matcher matcher = VERSION_PATTERN.matcher(Bukkit.getVersion());
|
||||
if (!matcher.matches() || matcher.group(1) == null)
|
||||
throw new IllegalStateException("Failed to determine server version");
|
||||
String value = matcher.group(1);
|
||||
String[] parts = value.split("\\.");
|
||||
if (parts.length < 2)
|
||||
throw new IllegalStateException("Corrupt server version " + value);
|
||||
int major = Integer.parseInt(parts[0]), minor = Integer.parseInt(parts[1]);
|
||||
for (ProtocolVersion version : values()) if (version.major == major && version.minor == minor)
|
||||
return version;
|
||||
throw new IllegalStateException("Unsupported minecraft version: " + major + "." + minor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return major + "." + minor + ".x";
|
||||
}
|
||||
|
||||
public static ProtocolVersion fromProtocolId(int value) {
|
||||
if (755 <= value && value <= 756) {
|
||||
return V1_17;
|
||||
} else if (701 <= value && value <= 754) {
|
||||
return V1_16;
|
||||
} else if (550 <= value && value <= 578) {
|
||||
return V1_15;
|
||||
} else if (441 <= value && value <= 498) {
|
||||
return V1_14;
|
||||
} else if (341 <= value && value <= 404) {
|
||||
return V1_13;
|
||||
} else if (317 <= value && value <= 340) {
|
||||
return V1_12;
|
||||
} else if (301 <= value && value <= 316) {
|
||||
return V1_11;
|
||||
} else if (201 <= value && value <= 210) {
|
||||
return V1_10;
|
||||
} else if (48 <= value && value <= 110) {
|
||||
return V1_9;
|
||||
} else if (6 <= value && value <= 47) {
|
||||
return V1_8;
|
||||
} else if (value <= 0) {
|
||||
return null;
|
||||
} else if (value <= 5) {
|
||||
Bukkit.getLogger().info("[AAC5] Unsupported protocol " + value + " (assuming " + V1_8 + ")");
|
||||
return V1_8;
|
||||
} else {
|
||||
Bukkit.getLogger().info("[AAC5] Unsupported protocol " + value + " (assuming " + V1_17 + ")");
|
||||
return V1_17;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package me.konsolas.aac.timings;
|
||||
|
||||
import com.comphenix.protocol.PacketType.Play.Client;
|
||||
import com.comphenix.protocol.PacketType.Play.Server;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import me.konsolas.aac.compat.nms.MinecraftReflection;
|
||||
import me.konsolas.aac.protocol.PacketDispatcher;
|
||||
import me.konsolas.aac.protocol.PacketReader;
|
||||
import me.konsolas.aac.protocol.ProtocolVersion;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockDamageEvent;
|
||||
import org.bukkit.event.block.BlockPistonExtendEvent;
|
||||
import org.bukkit.event.block.BlockPistonRetractEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||
import org.bukkit.event.entity.EntityShootBowEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class AACListener extends PacketAdapter implements Listener {
|
||||
|
||||
private final Class placeholderApiClass = MinecraftReflection.getCraftPlayerClass();
|
||||
|
||||
private final PacketDispatcher dispatcher;
|
||||
|
||||
public AACListener(PacketDispatcher dispatcher) {
|
||||
super(dispatcher.getPlugin(), ListenerPriority.LOW, getList(), new ListenerOptions[] { ListenerOptions.ASYNC });
|
||||
this.dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
private static List getList() {
|
||||
ArrayList values = new ArrayList();
|
||||
values.add(PacketReader.GROUND);
|
||||
values.add(Client.POSITION);
|
||||
values.add(Client.LOOK);
|
||||
values.add(Client.POSITION_LOOK);
|
||||
values.add(Server.POSITION);
|
||||
values.add(Server.ENTITY_VELOCITY);
|
||||
values.add(Server.BLOCK_CHANGE);
|
||||
values.add(Server.MULTI_BLOCK_CHANGE);
|
||||
values.add(Server.MAP_CHUNK);
|
||||
values.add(Client.ENTITY_ACTION);
|
||||
values.add(Client.BLOCK_DIG);
|
||||
values.add(Client.BLOCK_PLACE);
|
||||
values.add(Client.USE_ENTITY);
|
||||
values.add(Client.ARM_ANIMATION);
|
||||
values.add(Client.ABILITIES);
|
||||
ProtocolVersion serverVersion = ProtocolVersion.getServerVersion();
|
||||
ProtocolVersion version = ProtocolVersion.V1_9;
|
||||
if (serverVersion.isAtLeast(version)) {
|
||||
values.add(Client.VEHICLE_MOVE);
|
||||
values.add(Client.USE_ITEM);
|
||||
values.add(Client.TELEPORT_ACCEPT);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
if (this.placeholderApiClass.isInstance(event.getPlayer())) {
|
||||
this.dispatcher.handlePacket(event);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
if (this.placeholderApiClass.isInstance(event.getPlayer())) {
|
||||
this.dispatcher.handlePacket(event);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockPlaceEvent(BlockPlaceEvent blockPlaceEvent) {
|
||||
if (blockPlaceEvent.getClass() == BlockPlaceEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = blockPlaceEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, blockPlaceEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockDamageEvent(BlockDamageEvent blockDamageEvent) {
|
||||
if (blockDamageEvent.getClass() == BlockDamageEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = blockDamageEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, blockDamageEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockBreakEvent(BlockBreakEvent blockBreakEvent) {
|
||||
if (blockBreakEvent.getClass() == BlockBreakEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = blockBreakEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, blockBreakEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onPlayerInteractEvent(PlayerInteractEvent playerInteractEvent) {
|
||||
if (playerInteractEvent.getClass() == PlayerInteractEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = playerInteractEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, playerInteractEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerItemConsumeEvent(PlayerItemConsumeEvent playerItemConsumeEvent) {
|
||||
if (playerItemConsumeEvent.getClass() == PlayerItemConsumeEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = playerItemConsumeEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, playerItemConsumeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onEntityShootBowEvent(EntityShootBowEvent entityShootBowEvent) {
|
||||
if (entityShootBowEvent.getEntity() instanceof Player && entityShootBowEvent.getClass() == EntityShootBowEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
LivingEntity entity = entityShootBowEvent.getEntity();
|
||||
Player player = (Player) entity;
|
||||
packetDispatcher.handleEvent(player, entityShootBowEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerMoveEvent(PlayerMoveEvent playerMoveEvent) {
|
||||
if (playerMoveEvent.getClass() == PlayerMoveEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = playerMoveEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, playerMoveEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerTeleportEvent(PlayerTeleportEvent playerTeleportEvent) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = playerTeleportEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, playerTeleportEvent);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerRespawnEvent(PlayerRespawnEvent playerRespawnEvent) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Player player = playerRespawnEvent.getPlayer();
|
||||
Player player2 = player;
|
||||
packetDispatcher.handleEvent(player2, playerRespawnEvent);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onEntityRegainHealthEvent(EntityRegainHealthEvent entityRegainHealthEvent) {
|
||||
if (entityRegainHealthEvent.getEntity() instanceof Player && entityRegainHealthEvent.getClass() == EntityRegainHealthEvent.class) {
|
||||
PacketDispatcher packetDispatcher = this.dispatcher;
|
||||
Entity entity = entityRegainHealthEvent.getEntity();
|
||||
Player player = (Player) entity;
|
||||
packetDispatcher.handleEvent(player, entityRegainHealthEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockPistonExtendEvent(BlockPistonExtendEvent blockPistonExtendEvent) {
|
||||
BlockFace direction = blockPistonExtendEvent.getDirection();
|
||||
List blocks = blockPistonExtendEvent.getBlocks();
|
||||
Block block = blockPistonExtendEvent.getBlock();
|
||||
Block block2 = block;
|
||||
List values = blocks;
|
||||
this.updateBlockFace(direction, values, block2);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockPistonRetractEvent(BlockPistonRetractEvent blockPistonRetractEvent) {
|
||||
BlockFace oppositeFace = blockPistonRetractEvent.getDirection().getOppositeFace();
|
||||
List blocks = blockPistonRetractEvent.getBlocks();
|
||||
Block block = blockPistonRetractEvent.getBlock();
|
||||
Block block2 = block;
|
||||
List values = blocks;
|
||||
this.updateBlockFace(oppositeFace, values, block2);
|
||||
}
|
||||
|
||||
private void updateBlockFace(BlockFace blockFace, List<Block> values, Block block) {
|
||||
Vector vector = new Vector(blockFace.getModX(), blockFace.getModY(), blockFace.getModZ());
|
||||
for (Block block2 : values) {
|
||||
AxisAlignedBoundingBox box = AxisAlignedBoundingBox.forBlock(block2.getX() + blockFace.getModX(), block2.getY() + blockFace.getModY(), block2.getZ() + blockFace.getModZ());
|
||||
if (block2.getType() == Material.SLIME_BLOCK) {
|
||||
this.dispatcher.getSlimePushes().put(box, vector);
|
||||
} else {
|
||||
this.dispatcher.getPistonPushes().put(box, vector);
|
||||
}
|
||||
}
|
||||
this.dispatcher.getPistonPushes().put(AxisAlignedBoundingBox.forBlock(block.getX() + blockFace.getModX(), block.getY() + blockFace.getModY(), block.getZ() + blockFace.getModZ()), vector);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.konsolas.aac.timings;
|
||||
|
||||
import me.konsolas.aac.AAC;
|
||||
|
||||
public class CheckTask implements Runnable {
|
||||
|
||||
private final AAC plugin;
|
||||
|
||||
public CheckTask(AAC plugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 1L, 1L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.plugin.getPacketDispatcher().tick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package me.konsolas.aac.timings;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import me.konsolas.aac.AAC;
|
||||
import me.konsolas.aac.movement.EntityDimensions;
|
||||
import me.konsolas.aac.util.collection.FixedSizeList;
|
||||
import me.konsolas.aac.util.math.AxisAlignedBoundingBox;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class EntityTracker implements Runnable {
|
||||
|
||||
private final LoadingCache<LivingEntity, Boolean> trackedEntities = CacheBuilder.newBuilder().expireAfterWrite(5000L, TimeUnit.MILLISECONDS).build(CacheLoader.from(entity -> Boolean.TRUE));
|
||||
|
||||
private final IntOpenHashSet activeEntityIds = new IntOpenHashSet();
|
||||
|
||||
private final Int2ObjectMap positionHistory = new Int2ObjectOpenHashMap();
|
||||
|
||||
private final int historyLength;
|
||||
|
||||
public EntityTracker(AAC aacPlugin) {
|
||||
aacPlugin.getServer().getScheduler().scheduleSyncRepeatingTask(aacPlugin, this, 1L, 1L);
|
||||
this.historyLength = aacPlugin.getConfig().getInt("checks.hitbox.lag_compensation_ticks");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.activeEntityIds.clear();
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
this.updateLivingEntityWithPositionHistory(player);
|
||||
}
|
||||
for (LivingEntity livingEntity : (Iterable<LivingEntity>) (Iterable<?>) (this.trackedEntities.asMap().keySet())) {
|
||||
this.updateLivingEntityWithPositionHistory(livingEntity);
|
||||
}
|
||||
this.positionHistory.keySet().retainAll(this.activeEntityIds);
|
||||
}
|
||||
|
||||
private void updateLivingEntityWithPositionHistory(LivingEntity livingEntity) {
|
||||
FixedSizeList fixedSizeList = (FixedSizeList) this.positionHistory.computeIfAbsent(livingEntity.getEntityId(), result -> new FixedSizeList(this.historyLength));
|
||||
fixedSizeList.add(livingEntity.getLocation().toVector());
|
||||
this.activeEntityIds.add(livingEntity.getEntityId());
|
||||
}
|
||||
|
||||
public List findList(LivingEntity livingEntity) {
|
||||
FixedSizeList fixedSizeList = (FixedSizeList) this.positionHistory.get(livingEntity.getEntityId());
|
||||
if (fixedSizeList != null) {
|
||||
EntityDimensions entityDimensions = EntityDimensions.create(livingEntity);
|
||||
ArrayList values = new ArrayList();
|
||||
for (Vector vector : (Iterable<Vector>) (Iterable<?>) (fixedSizeList)) {
|
||||
if (vector != null) {
|
||||
AxisAlignedBoundingBox box = new AxisAlignedBoundingBox(vector, entityDimensions);
|
||||
values.add(box);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLivingEntity(LivingEntity livingEntity) {
|
||||
if (!(livingEntity instanceof Player)) {
|
||||
this.trackedEntities.put(livingEntity, Boolean.TRUE);
|
||||
this.trackedEntities.refresh(livingEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user