mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-17 16:37:43 +01:00
PaperMC/Paper@4d6073f Partially fix #6880 (#6888) PaperMC/Paper@f8992ee Extend Entity by Sound.Emitter (#6890) PaperMC/Paper@db0bff9 Call onRemove logic for breakNaturally (#6854) PaperMC/Paper@72b731b Updated Upstream (CraftBukkit) (#6885) PaperMC/Paper@555f99d cleanup: Add hand to bucket events patch (#6881) PaperMC/Paper@51b7b60 Add API for resetting a single score (#6873) PaperMC/Paper@f3830bc Update paperweight to 1.2.0 (#6901) PaperMC/Paper@e22a1a9 Fix more namespacedkey parsing (#6903) PaperMC/Paper@284b3b8 Fix AnvilPrepareEvent not working with zero xp (#6895) PaperMC/Paper@22aaf91 Add config option to enable hopper occluding blocks (#6896)
4333 lines
205 KiB
Diff
4333 lines
205 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Initial Source <auto@mated.null>
|
|
Date: Fri, 16 Jul 2021 17:11:36 -0500
|
|
Subject: [PATCH] Airplane Server Changes
|
|
|
|
Copyright (C) 2020 Technove LLC
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index e067c69fea1dec79ef8e97cad1c74020b6a3a235..fd3528be3d90733e826bcbeae967b95cedf92dfe 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -22,8 +22,8 @@ repositories {
|
|
}
|
|
|
|
dependencies {
|
|
- implementation(project(":Paper-API"))
|
|
- implementation(project(":Paper-MojangAPI"))
|
|
+ implementation(project(":Airplane-API")) // Airplane // Paper
|
|
+ implementation("io.papermc.paper:paper-mojangapi:1.17.1-R0.1-SNAPSHOT") // Airplane
|
|
// Paper start
|
|
implementation("org.jline:jline-terminal-jansi:3.21.0")
|
|
implementation("net.minecrell:terminalconsoleappender:1.3.0")
|
|
@@ -57,6 +57,13 @@ dependencies {
|
|
implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation
|
|
implementation("com.velocitypowered:velocity-native:1.1.0-SNAPSHOT") // Paper
|
|
|
|
+ implementation("com.github.technove:AIR:fe3dbb4420") // Airplane - config
|
|
+ implementation("org.yaml:snakeyaml:1.28")
|
|
+ implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.7.2") { // Airplane - more config
|
|
+ exclude(group="org.yaml", module="snakeyaml") // exclude snakeyaml dependency because its old (1.26)
|
|
+ } // Airplane - more config
|
|
+ implementation("com.github.technove:Flare:2c4a2114a0") // Airplane - flare
|
|
+
|
|
testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test
|
|
testImplementation("junit:junit:4.13.1")
|
|
testImplementation("org.hamcrest:hamcrest-library:1.3")
|
|
@@ -75,6 +82,7 @@ tasks.jar {
|
|
"Main-Class" to "org.bukkit.craftbukkit.Main",
|
|
"Implementation-Title" to "CraftBukkit",
|
|
"Implementation-Version" to "git-Paper-$implementationVersion",
|
|
+ "Implementation-Version" to "git-Airplane-$implementationVersion", // Airplane
|
|
"Implementation-Vendor" to date, // Paper
|
|
"Specification-Title" to "Bukkit",
|
|
"Specification-Version" to project.version,
|
|
@@ -133,6 +141,22 @@ relocation {
|
|
}
|
|
}
|
|
|
|
+val generateReobfMappings = rootProject.tasks.named<io.papermc.paperweight.tasks.GenerateReobfMappings>("generateReobfMappings")
|
|
+
|
|
+val patchReobfMappings by tasks.registering<io.papermc.paperweight.tasks.PatchMappings> {
|
|
+ inputMappings.set(generateReobfMappings.flatMap { it.reobfMappings })
|
|
+ patch.set(rootProject.layout.cache.resolve("paperweight/upstreams/paper/build-data/reobf-mappings-patch.tiny"))
|
|
+
|
|
+ fromNamespace.set(io.papermc.paperweight.util.constants.DEOBF_NAMESPACE)
|
|
+ toNamespace.set(io.papermc.paperweight.util.constants.SPIGOT_NAMESPACE)
|
|
+
|
|
+ outputMappings.set(layout.cache.resolve("paperweight/mappings/reobf-patched.tiny"))
|
|
+}
|
|
+
|
|
+tasks.reobfJar {
|
|
+ mappingsFile.set(patchReobfMappings.flatMap { it.outputMappings })
|
|
+}
|
|
+
|
|
val generatePom = tasks.named<GenerateMavenPom>("generatePomFileForMavenPublication")
|
|
|
|
tasks.shadowJar {
|
|
diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java
|
|
index e29b0a90019b12bd6586ad0f7b5314f307e527ba..6e8001c7ff6497c9e5c274a5fe85cc204f3ba4c5 100644
|
|
--- a/src/main/java/co/aikar/timings/TimingsExport.java
|
|
+++ b/src/main/java/co/aikar/timings/TimingsExport.java
|
|
@@ -228,7 +228,8 @@ public class TimingsExport extends Thread {
|
|
parent.put("config", createObject(
|
|
pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)),
|
|
pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)),
|
|
- pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null))
|
|
+ pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), // Airplane
|
|
+ pair("airplane", mapAsJSON(gg.airplane.AirplaneConfig.getConfigCopy(), null)) // Airplane
|
|
));
|
|
|
|
new TimingsExport(listeners, parent, history).start();
|
|
diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
index 218f5bafeed8551b55b91c7fccaf6935c8b631ca..f5d01bce4d5547b4aeca96b7962b2090f47ea541 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
@@ -593,7 +593,7 @@ public class Metrics {
|
|
boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
|
|
// Only start Metrics, if it's enabled in the config
|
|
if (config.getBoolean("enabled", true)) {
|
|
- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger());
|
|
+ Metrics metrics = new Metrics("Airplane", serverUUID, logFailedRequests, Bukkit.getLogger()); // Tuinity - we have our own bstats page // Airplane
|
|
|
|
metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
|
|
String minecraftVersion = Bukkit.getVersion();
|
|
@@ -603,7 +603,7 @@ public class Metrics {
|
|
|
|
metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
|
|
metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline"));
|
|
- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"));
|
|
+ metrics.addCustomChart(new Metrics.SimplePie("airplane_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); // Tuinity - we have our own bstats page // Airplane
|
|
|
|
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
|
|
Map<String, Map<String, Integer>> map = new HashMap<>();
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
index 2509a170b8ddd812ad5be49e5345ec5a3c0cf2b8..6cda37d340bfc6f76cbb3bdaa3ffb6d3cf90da9f 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
@@ -205,16 +205,26 @@ public class PaperConfig {
|
|
public static String timingsServerName;
|
|
private static void timings() {
|
|
boolean timings = getBoolean("timings.enabled", true);
|
|
+ // Airplane start
|
|
+ boolean reallyEnableTimings = getBoolean("timings.really-enabled", false);
|
|
+ if (timings && !reallyEnableTimings) {
|
|
+ Bukkit.getLogger().log(Level.WARNING, "[Airplane] To improve performance, timings have been disabled by default");
|
|
+ Bukkit.getLogger().log(Level.WARNING, "[Airplane] You can still use timings by using /timings on, but they will not start on server startup unless you set timings.really-enabled to true in paper.yml");
|
|
+ Bukkit.getLogger().log(Level.WARNING, "[Airplane] If you would like to disable this message, either set timings.really-enabled to true or timings.enabled to false.");
|
|
+ }
|
|
+ timings = reallyEnableTimings;
|
|
+ // Airplane end
|
|
boolean verboseTimings = getBoolean("timings.verbose", true);
|
|
TimingsManager.url = getString("timings.url", "https://timings.aikar.co/");
|
|
if (!TimingsManager.url.endsWith("/")) {
|
|
TimingsManager.url += "/";
|
|
}
|
|
TimingsManager.privacy = getBoolean("timings.server-name-privacy", false);
|
|
- TimingsManager.hiddenConfigs = getList("timings.hidden-config-entries", Lists.newArrayList("database", "settings.bungeecord-addresses", "settings.velocity-support.secret"));
|
|
+ TimingsManager.hiddenConfigs = getList("timings.hidden-config-entries", Lists.newArrayList("database", "settings.bungeecord-addresses", "settings.velocity-support.secret", "web-services.token")); // Airplane
|
|
if (!TimingsManager.hiddenConfigs.contains("settings.velocity-support.secret")) {
|
|
TimingsManager.hiddenConfigs.add("settings.velocity-support.secret");
|
|
}
|
|
+ if (!TimingsManager.hiddenConfigs.contains("web-services.token")) TimingsManager.hiddenConfigs.add("web-services.token"); // Airplane
|
|
int timingHistoryInterval = getInt("timings.history-interval", 300);
|
|
int timingHistoryLength = getInt("timings.history-length", 3600);
|
|
timingsServerName = getString("timings.server-name", "Unknown Server");
|
|
diff --git a/src/main/java/gg/airplane/AirplaneCommand.java b/src/main/java/gg/airplane/AirplaneCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..89c89e633f14b5820147e734b1b7ad8cadfdce80
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/AirplaneCommand.java
|
|
@@ -0,0 +1,65 @@
|
|
+package gg.airplane;
|
|
+
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.md_5.bungee.api.ChatColor;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.util.Collections;
|
|
+import java.util.List;
|
|
+import java.util.stream.Collectors;
|
|
+import java.util.stream.Stream;
|
|
+
|
|
+public class AirplaneCommand extends Command {
|
|
+
|
|
+ public AirplaneCommand() {
|
|
+ super("airplane");
|
|
+ this.description = "Airplane related commands";
|
|
+ this.usageMessage = "/airplane [reload | version]";
|
|
+ this.setPermission("bukkit.command.airplane");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
|
|
+ if (args.length == 1) {
|
|
+ return Stream.of("reload", "version")
|
|
+ .filter(arg -> arg.startsWith(args[0].toLowerCase()))
|
|
+ .collect(Collectors.toList());
|
|
+ }
|
|
+ return Collections.emptyList();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
|
+ if (!testPermission(sender)) return true;
|
|
+ String prefix = ChatColor.of("#6a7eda") + "" + ChatColor.BOLD + "Airplane ✈ " + ChatColor.of("#e8ebf9");
|
|
+
|
|
+ if (args.length != 1) {
|
|
+ sender.sendMessage(prefix + "Usage: " + usageMessage);
|
|
+ args = new String[]{"version"};
|
|
+ }
|
|
+
|
|
+ if (args[0].equalsIgnoreCase("reload")) {
|
|
+ MinecraftServer console = MinecraftServer.getServer();
|
|
+ try {
|
|
+ AirplaneConfig.load();
|
|
+ } catch (IOException e) {
|
|
+ sender.sendMessage(Component.text("Failed to reload.", NamedTextColor.RED));
|
|
+ e.printStackTrace();
|
|
+ return true;
|
|
+ }
|
|
+ console.server.reloadCount++;
|
|
+
|
|
+ Command.broadcastCommandMessage(sender, prefix + "Airplane configuration has been reloaded.");
|
|
+ } else if (args[0].equalsIgnoreCase("version")) {
|
|
+ Command.broadcastCommandMessage(sender, prefix + "This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")");
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/AirplaneConfig.java b/src/main/java/gg/airplane/AirplaneConfig.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2b654e6adfe331a5fa68c1aea7d6d6cea1f3567d
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/AirplaneConfig.java
|
|
@@ -0,0 +1,257 @@
|
|
+package gg.airplane;
|
|
+
|
|
+import co.technove.air.AIR;
|
|
+import co.technove.air.ValueType;
|
|
+import net.minecraft.core.Registry;
|
|
+import gg.airplane.flare.FlareCommand;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import org.apache.logging.log4j.Level;
|
|
+import org.bukkit.configuration.Configuration;
|
|
+import org.bukkit.configuration.ConfigurationSection;
|
|
+import org.bukkit.configuration.MemoryConfiguration;
|
|
+import org.bukkit.configuration.MemorySection;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+import org.simpleyaml.configuration.comments.CommentType;
|
|
+import org.simpleyaml.configuration.file.YamlFile;
|
|
+import org.simpleyaml.exceptions.InvalidConfigurationException;
|
|
+import org.bukkit.command.SimpleCommandMap;
|
|
+
|
|
+import java.io.File;
|
|
+import java.io.FileInputStream;
|
|
+import java.io.IOException;
|
|
+import java.lang.reflect.Method;
|
|
+import java.lang.reflect.Modifier;
|
|
+import java.util.List;
|
|
+import java.net.URI;
|
|
+import java.util.Collections;
|
|
+
|
|
+public class AirplaneConfig {
|
|
+
|
|
+ private static @Nullable AIR oldConfig;
|
|
+ private static final YamlFile config = new YamlFile();
|
|
+ private static int updates = 0;
|
|
+
|
|
+ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) {
|
|
+ ConfigurationSection newSection = new MemoryConfiguration();
|
|
+ for (String key : section.getKeys(false)) {
|
|
+ if (section.isConfigurationSection(key)) {
|
|
+ newSection.set(key, convertToBukkit(section.getConfigurationSection(key)));
|
|
+ } else {
|
|
+ newSection.set(key, section.get(key));
|
|
+ }
|
|
+ }
|
|
+ return newSection;
|
|
+ }
|
|
+
|
|
+ public static ConfigurationSection getConfigCopy() {
|
|
+ return convertToBukkit(config);
|
|
+ }
|
|
+
|
|
+ public static int getUpdates() {
|
|
+ return updates;
|
|
+ }
|
|
+
|
|
+ public static void load() throws IOException {
|
|
+ File oldConfigFile = new File("airplane.air");
|
|
+ File configFile = new File("airplane.yml");
|
|
+ if (oldConfigFile.exists() && !configFile.exists()) {
|
|
+ try (FileInputStream inputStream = new FileInputStream(oldConfigFile)) {
|
|
+ oldConfig = new AIR(inputStream);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (configFile.exists()) {
|
|
+ try {
|
|
+ config.load(configFile);
|
|
+ } catch (InvalidConfigurationException e) {
|
|
+ throw new IOException(e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ getString("info.version", "1.0");
|
|
+ setComment("info",
|
|
+ "Airplane Configuration",
|
|
+ "Read https://blog.airplane.gg/ to find out more about Airplane",
|
|
+ "Join our Discord to receive support & optimization help: https://discord.gg/3gtc45q");
|
|
+
|
|
+ for (Method method : AirplaneConfig.class.getDeclaredMethods()) {
|
|
+ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 &&
|
|
+ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) {
|
|
+ method.setAccessible(true);
|
|
+ try {
|
|
+ method.invoke(null);
|
|
+ } catch (Throwable t) {
|
|
+ MinecraftServer.LOGGER.log(Level.WARN, "Failed to load configuration option from " + method.getName(), t);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ updates++;
|
|
+
|
|
+ config.save(configFile);
|
|
+ oldConfig = null;
|
|
+
|
|
+ if (oldConfigFile.exists()) {
|
|
+ oldConfigFile.renameTo(new File("airplane.air.old"));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static void setComment(String key, String... comment) {
|
|
+ if (config.contains(key)) {
|
|
+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static void ensureDefault(String key, Object defaultValue, String... comment) {
|
|
+ if (!config.contains(key)) {
|
|
+ config.set(key, defaultValue);
|
|
+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static boolean getBoolean(String key, boolean defaultValue, String... comment) {
|
|
+ return getBoolean(key, null, defaultValue, comment);
|
|
+ }
|
|
+
|
|
+ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) {
|
|
+ if (oldConfig != null) {
|
|
+ defaultValue = oldConfig.getBoolean(oldKey == null ? key : oldKey, defaultValue);
|
|
+ }
|
|
+ ensureDefault(key, defaultValue, comment);
|
|
+ return config.getBoolean(key, defaultValue);
|
|
+ }
|
|
+
|
|
+ private static int getInt(String key, int defaultValue, String... comment) {
|
|
+ return getInt(key, null, defaultValue, comment);
|
|
+ }
|
|
+
|
|
+ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) {
|
|
+ if (oldConfig != null) {
|
|
+ defaultValue = oldConfig.getInt(oldKey == null ? key : oldKey, defaultValue);
|
|
+ }
|
|
+ ensureDefault(key, defaultValue, comment);
|
|
+ return config.getInt(key, defaultValue);
|
|
+ }
|
|
+
|
|
+ private static double getDouble(String key, double defaultValue, String... comment) {
|
|
+ return getDouble(key, null, defaultValue, comment);
|
|
+ }
|
|
+
|
|
+ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) {
|
|
+ if (oldConfig != null) {
|
|
+ defaultValue = oldConfig.getDouble(oldKey == null ? key : oldKey, defaultValue);
|
|
+ }
|
|
+ ensureDefault(key, defaultValue, comment);
|
|
+ return config.getDouble(key, defaultValue);
|
|
+ }
|
|
+
|
|
+ private static String getString(String key, String defaultValue, String... comment) {
|
|
+ return getOldString(key, null, defaultValue, comment);
|
|
+ }
|
|
+
|
|
+ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) {
|
|
+ if (oldConfig != null) {
|
|
+ defaultValue = oldConfig.getString(oldKey == null ? key : oldKey, defaultValue);
|
|
+ }
|
|
+ ensureDefault(key, defaultValue, comment);
|
|
+ return config.getString(key, defaultValue);
|
|
+ }
|
|
+
|
|
+ private static List<String> getStringList(String key, List<String> defaultValue, String... comment) {
|
|
+ return getStringList(key, null, defaultValue, comment);
|
|
+ }
|
|
+
|
|
+ private static List<String> getStringList(String key, @Nullable String oldKey, List<String> defaultValue, String... comment) {
|
|
+ if (oldConfig != null) {
|
|
+ try {
|
|
+ defaultValue = oldConfig.getList(oldKey != null ? oldKey : key, ValueType.STRING, defaultValue);
|
|
+ } catch (IOException e) {
|
|
+ }
|
|
+ }
|
|
+ ensureDefault(key, defaultValue, comment);
|
|
+ return config.getStringList(key);
|
|
+ }
|
|
+
|
|
+ public static int maxProjectileLoadsPerTick;
|
|
+ public static int maxProjectileLoadsPerProjectile;
|
|
+
|
|
+
|
|
+ private static void projectileLoading() {
|
|
+ maxProjectileLoadsPerTick = getInt("projectile.max-loads-per-tick", 10, "Controls how many chunks are allowed", "to be sync loaded by projectiles in a tick.");
|
|
+ maxProjectileLoadsPerProjectile = getInt("projectile.max-loads-per-projectile", 10, "Controls how many chunks a projectile", "can load in its lifetime before it gets", "automatically removed.");
|
|
+
|
|
+ setComment("projectile", "Optimizes projectile settings");
|
|
+ }
|
|
+
|
|
+
|
|
+ public static boolean dearEnabled;
|
|
+ public static int startDistance;
|
|
+ public static int startDistanceSquared;
|
|
+ public static int maximumActivationPrio;
|
|
+ public static int activationDistanceMod;
|
|
+
|
|
+ private static void dynamicActivationOfBrains() throws IOException {
|
|
+ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true);
|
|
+ startDistance = getInt("dab.start-distance", "activation-range.start-distance", 12,
|
|
+ "This value determines how far away an entity has to be",
|
|
+ "from the player to start being effected by DEAR.");
|
|
+ startDistanceSquared = startDistance * startDistance;
|
|
+ maximumActivationPrio = getInt("dab.max-tick-freq", "activation-range.max-tick-freq", 20,
|
|
+ "This value defines how often in ticks, the furthest entity",
|
|
+ "will get their pathfinders and behaviors ticked. 20 = 1s");
|
|
+ activationDistanceMod = getInt("dab.activation-dist-mod", "activation-range.activation-dist-mod", 8,
|
|
+ "This value defines how much distance modifies an entity's",
|
|
+ "tick frequency. freq = (distanceToPlayer^2) / (2^value)",
|
|
+ "If you want further away entities to tick less often, use 7.",
|
|
+ "If you want further away entities to tick more often, try 9.");
|
|
+
|
|
+ for (EntityType<?> entityType : Registry.ENTITY_TYPE) {
|
|
+ entityType.dabEnabled = true; // reset all, before setting the ones to true
|
|
+ }
|
|
+ getStringList("dab.blacklisted-entities", "activation-range.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation")
|
|
+ .forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> {
|
|
+ entityType.dabEnabled = false;
|
|
+ }, () -> MinecraftServer.LOGGER.log(Level.WARN, "Unknown entity \"" + name + "\"")));
|
|
+
|
|
+ setComment("dab", "Optimizes entity brains when", "they're far away from the player");
|
|
+ }
|
|
+
|
|
+
|
|
+ public static URI profileWebUrl;
|
|
+
|
|
+ private static void profilerOptions() {
|
|
+ profileWebUrl = URI.create(getString("flare.url", "https://flare.airplane.gg", "Sets the server to use for profiles."));
|
|
+
|
|
+ setComment("flare", "Configures Flare, the built-in profiler");
|
|
+ }
|
|
+
|
|
+
|
|
+ public static String accessToken;
|
|
+
|
|
+ private static void airplaneWebServices() {
|
|
+ accessToken = getString("web-services.token", "");
|
|
+ // todo lookup token (off-thread) and let users know if their token is valid
|
|
+ if (accessToken.length() > 0) {
|
|
+ gg.airplane.flare.FlareSetup.init(); // Airplane
|
|
+ SimpleCommandMap commandMap = MinecraftServer.getServer().server.getCommandMap();
|
|
+ if (commandMap.getCommand("flare") == null) {
|
|
+ commandMap.register("flare", "Airplane", new FlareCommand());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ setComment("web-services", "Options for connecting to Airplane's online utilities");
|
|
+
|
|
+ }
|
|
+
|
|
+
|
|
+ public static boolean disableMethodProfiler;
|
|
+
|
|
+ private static void miscSettings() {
|
|
+ disableMethodProfiler = config.getBoolean("misc.disable-method-profiler", true);
|
|
+
|
|
+ config.setComment("misc", "Settings for things that don't belong elsewhere");
|
|
+ }
|
|
+
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/AirplaneLogger.java b/src/main/java/gg/airplane/AirplaneLogger.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1a9d71739019d12772bec6076b195552ff6299f9
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/AirplaneLogger.java
|
|
@@ -0,0 +1,17 @@
|
|
+package gg.airplane;
|
|
+
|
|
+import org.bukkit.Bukkit;
|
|
+
|
|
+import java.util.logging.Level;
|
|
+import java.util.logging.Logger;
|
|
+
|
|
+public class AirplaneLogger extends Logger {
|
|
+ public static final AirplaneLogger LOGGER = new AirplaneLogger();
|
|
+
|
|
+ private AirplaneLogger() {
|
|
+ super("Airplane", null);
|
|
+
|
|
+ setParent(Bukkit.getLogger());
|
|
+ setLevel(Level.ALL);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/AirplaneVersionFetcher.java b/src/main/java/gg/airplane/AirplaneVersionFetcher.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e79ec7919e6cf160fec1cb981d85f0f33c316b41
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/AirplaneVersionFetcher.java
|
|
@@ -0,0 +1,137 @@
|
|
+package gg.airplane;
|
|
+
|
|
+import com.destroystokyo.paper.VersionHistoryManager;
|
|
+import com.destroystokyo.paper.util.VersionFetcher;
|
|
+import com.google.gson.Gson;
|
|
+import com.google.gson.JsonObject;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.JoinConfiguration;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.format.TextDecoration;
|
|
+import org.bukkit.craftbukkit.CraftServer;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.net.URI;
|
|
+import java.net.http.HttpClient;
|
|
+import java.net.http.HttpRequest;
|
|
+import java.net.http.HttpResponse;
|
|
+import java.nio.charset.StandardCharsets;
|
|
+import java.util.concurrent.TimeUnit;
|
|
+import java.util.logging.Level;
|
|
+import java.util.logging.Logger;
|
|
+
|
|
+import static net.kyori.adventure.text.Component.text;
|
|
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
|
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
|
+
|
|
+public class AirplaneVersionFetcher implements VersionFetcher {
|
|
+
|
|
+ private static final Logger LOGGER = Logger.getLogger("AirplaneVersionFetcher");
|
|
+ private static final HttpClient client = HttpClient.newHttpClient();
|
|
+
|
|
+ private static final URI JENKINS_URI = URI.create("https://ci.tivy.ca/job/Airplane-1.17/lastSuccessfulBuild/buildNumber");
|
|
+ private static final String GITHUB_FORMAT = "https://api.github.com/repos/TECHNOVE/Airplane/compare/ver/1.17...%s";
|
|
+
|
|
+ private static final HttpResponse.BodyHandler<JsonObject> JSON_OBJECT_BODY_HANDLER = responseInfo -> HttpResponse.BodySubscribers.mapping(
|
|
+ HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8),
|
|
+ string -> new Gson().fromJson(string, JsonObject.class)
|
|
+ );
|
|
+
|
|
+ @Override
|
|
+ public long getCacheTime() {
|
|
+ return TimeUnit.MINUTES.toMillis(30);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull Component getVersionMessage(final @NotNull String serverVersion) {
|
|
+ final String[] parts = CraftServer.class.getPackage().getImplementationVersion().split("-");
|
|
+ @NotNull Component component;
|
|
+
|
|
+ if (parts.length != 3) {
|
|
+ component = text("Unknown server version.", RED);
|
|
+ } else {
|
|
+ final String versionString = parts[2];
|
|
+
|
|
+ try {
|
|
+ component = this.fetchJenkinsVersion(Integer.parseInt(versionString));
|
|
+ } catch (NumberFormatException e) {
|
|
+ component = this.fetchGithubVersion(versionString.substring(1, versionString.length() - 1));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final @Nullable Component history = this.getHistory();
|
|
+ return history != null ? Component.join(JoinConfiguration.noSeparators(), component, Component.newline(), this.getHistory()) : component;
|
|
+ }
|
|
+
|
|
+ private @NotNull Component fetchJenkinsVersion(final int versionNumber) {
|
|
+ final HttpRequest request = HttpRequest.newBuilder(JENKINS_URI).build();
|
|
+ try {
|
|
+ final HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
|
+ if (response.statusCode() != 200) {
|
|
+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED);
|
|
+ }
|
|
+
|
|
+ int latestVersionNumber;
|
|
+ try {
|
|
+ latestVersionNumber = Integer.parseInt(response.body());
|
|
+ } catch (NumberFormatException e) {
|
|
+ LOGGER.log(Level.WARNING, "Received invalid response from Jenkins \"" + response.body() + "\".");
|
|
+ return text("Received invalid response from server.", RED);
|
|
+ }
|
|
+
|
|
+ final int versionDiff = latestVersionNumber - versionNumber;
|
|
+ return this.getResponseMessage(versionDiff);
|
|
+ } catch (IOException | InterruptedException e) {
|
|
+ LOGGER.log(Level.WARNING, "Failed to look up version from Jenkins", e);
|
|
+ return text("Failed to retrieve version from server.", RED);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Based off code contributed by Techcable <Techcable@outlook.com> in Paper/GH-65
|
|
+ private @NotNull Component fetchGithubVersion(final @NotNull String hash) {
|
|
+ final URI uri = URI.create(String.format(GITHUB_FORMAT, hash));
|
|
+ final HttpRequest request = HttpRequest.newBuilder(uri).build();
|
|
+ try {
|
|
+ final HttpResponse<JsonObject> response = client.send(request, JSON_OBJECT_BODY_HANDLER);
|
|
+ if (response.statusCode() != 200) {
|
|
+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED);
|
|
+ }
|
|
+
|
|
+ final JsonObject obj = response.body();
|
|
+ final int versionDiff = obj.get("behind_by").getAsInt();
|
|
+
|
|
+ return this.getResponseMessage(versionDiff);
|
|
+ } catch (IOException | InterruptedException e) {
|
|
+ LOGGER.log(Level.WARNING, "Failed to look up version from GitHub", e);
|
|
+ return text("Failed to retrieve version from server.", RED);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private @NotNull Component getResponseMessage(final int versionDiff) {
|
|
+ return switch (Math.max(-1, Math.min(1, versionDiff))) {
|
|
+ case -1 ->
|
|
+ text("You are running an unsupported version of Airplane.", RED);
|
|
+ case 0 ->
|
|
+ text("You are on the latest version!", GREEN);
|
|
+ default ->
|
|
+ text("You are running " + versionDiff + " version" + (versionDiff == 1 ? "" : "s") + " beyond. " +
|
|
+ "Please update your server when possible to maintain stability, security, and receive the latest optimizations.", RED);
|
|
+ };
|
|
+ }
|
|
+
|
|
+ private @Nullable Component getHistory() {
|
|
+ final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData();
|
|
+ if (data == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final String oldVersion = data.getOldVersion();
|
|
+ if (oldVersion == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/commands/AirplaneCommands.java b/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..807cf274619b8f7be839e249cb62b9817876ca04
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
@@ -0,0 +1,10 @@
|
|
+package gg.airplane.commands;
|
|
+
|
|
+import gg.airplane.AirplaneCommand;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+
|
|
+public class AirplaneCommands {
|
|
+ public static void init() {
|
|
+ MinecraftServer.getServer().server.getCommandMap().register("airplane", "Airplane", new AirplaneCommand());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/compat/ServerConfigurations.java b/src/main/java/gg/airplane/compat/ServerConfigurations.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f9a71ff3edd7e7b6cda680e5a156373b5aa813c2
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/compat/ServerConfigurations.java
|
|
@@ -0,0 +1,78 @@
|
|
+package gg.airplane.compat;
|
|
+
|
|
+import co.aikar.timings.TimingsManager;
|
|
+import com.google.common.io.Files;
|
|
+import org.bukkit.configuration.InvalidConfigurationException;
|
|
+import org.bukkit.configuration.file.YamlConfiguration;
|
|
+
|
|
+import java.io.ByteArrayOutputStream;
|
|
+import java.io.File;
|
|
+import java.io.FileInputStream;
|
|
+import java.io.IOException;
|
|
+import java.nio.charset.StandardCharsets;
|
|
+import java.util.Arrays;
|
|
+import java.util.HashMap;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import java.util.Properties;
|
|
+import java.util.stream.Collectors;
|
|
+
|
|
+public class ServerConfigurations {
|
|
+
|
|
+ public static final String[] configurationFiles = new String[]{
|
|
+ "server.properties",
|
|
+ "bukkit.yml",
|
|
+ "spigot.yml",
|
|
+ "paper.yml",
|
|
+ "airplane.yml"
|
|
+ };
|
|
+
|
|
+ public static Map<String, String> getCleanCopies() throws IOException {
|
|
+ Map<String, String> files = new HashMap<>(configurationFiles.length);
|
|
+ for (String file : configurationFiles) {
|
|
+ files.put(file, getCleanCopy(file));
|
|
+ }
|
|
+ return files;
|
|
+ }
|
|
+
|
|
+ public static String getCleanCopy(String configName) throws IOException {
|
|
+ File file = new File(configName);
|
|
+ List<String> hiddenConfigs = TimingsManager.hiddenConfigs;
|
|
+
|
|
+ switch (Files.getFileExtension(configName)) {
|
|
+ case "properties": {
|
|
+ Properties properties = new Properties();
|
|
+ try (FileInputStream inputStream = new FileInputStream(file)) {
|
|
+ properties.load(inputStream);
|
|
+ }
|
|
+ for (String hiddenConfig : hiddenConfigs) {
|
|
+ properties.remove(hiddenConfig);
|
|
+ }
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
+ properties.store(outputStream, "");
|
|
+ return Arrays.stream(outputStream.toString()
|
|
+ .split("\n"))
|
|
+ .filter(line -> !line.startsWith("#"))
|
|
+ .collect(Collectors.joining("\n"));
|
|
+ }
|
|
+ case "yml": {
|
|
+ YamlConfiguration configuration = new YamlConfiguration();
|
|
+ try {
|
|
+ configuration.load(file);
|
|
+ } catch (InvalidConfigurationException e) {
|
|
+ throw new IOException(e);
|
|
+ }
|
|
+ configuration.options().header(null);
|
|
+ for (String key : configuration.getKeys(true)) {
|
|
+ if (hiddenConfigs.contains(key)) {
|
|
+ configuration.set(key, null);
|
|
+ }
|
|
+ }
|
|
+ return configuration.saveToString();
|
|
+ }
|
|
+ default:
|
|
+ throw new IllegalArgumentException("Bad file type " + configName);
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/entity/CollisionCache.java b/src/main/java/gg/airplane/entity/CollisionCache.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f67e19ad737a7a6b29e58aad59b1367574609efe
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/entity/CollisionCache.java
|
|
@@ -0,0 +1,263 @@
|
|
+package gg.airplane.entity;
|
|
+
|
|
+import io.papermc.paper.util.CollisionUtil;
|
|
+import io.papermc.paper.util.WorldUtil;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.SectionPos;
|
|
+import net.minecraft.server.level.ServerChunkCache;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.level.CollisionGetter;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
|
+import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
+import net.minecraft.world.phys.AABB;
|
|
+import net.minecraft.world.phys.shapes.CollisionContext;
|
|
+import net.minecraft.world.phys.shapes.Shapes;
|
|
+import net.minecraft.world.phys.shapes.VoxelShape;
|
|
+import org.bukkit.craftbukkit.util.UnsafeList;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.simpleyaml.utils.Validate;
|
|
+
|
|
+import java.util.HashSet;
|
|
+import java.util.List;
|
|
+import java.util.Set;
|
|
+import java.util.function.BiPredicate;
|
|
+
|
|
+public class CollisionCache {
|
|
+
|
|
+ private static record BlockEntry(int x, int y, int z, BlockState state) {
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ private final Entity entity;
|
|
+ private final UnsafeList<BlockEntry> blocks = new UnsafeList<>();
|
|
+ private Set<CollisionCacheList> chunkToList = new HashSet<>();
|
|
+
|
|
+ private boolean dirty = true;
|
|
+
|
|
+ private int previousMinBlockX;
|
|
+ private int previousMaxBlockX;
|
|
+ private int previousMinBlockY;
|
|
+ private int previousMaxBlockY;
|
|
+ private int previousMinBlockZ;
|
|
+ private int previousMaxBlockZ;
|
|
+
|
|
+ public CollisionCache(@NotNull Entity entity) {
|
|
+ this.entity = entity;
|
|
+ }
|
|
+
|
|
+ public int getId() {
|
|
+ return this.entity.getId();
|
|
+ }
|
|
+
|
|
+ public void dirtySection(@NotNull SectionPos sectionPos) {
|
|
+ this.dirty = true;
|
|
+ }
|
|
+
|
|
+ public void onRemove() {
|
|
+ this.blocks.setSize(0);
|
|
+
|
|
+ for (CollisionCacheList collisionCaches : this.chunkToList) {
|
|
+ collisionCaches.remove(this);
|
|
+ }
|
|
+ this.chunkToList.clear();
|
|
+ this.dirty = false;
|
|
+ }
|
|
+
|
|
+ public boolean getCollisions(final CollisionGetter view, AABB aabb, List<AABB> into, boolean collidesWithUnloaded, boolean checkOnly, BiPredicate<BlockState, BlockPos> predicate) {
|
|
+ boolean ret = false;
|
|
+
|
|
+ int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
|
+ int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
|
+
|
|
+ int minBlockY = Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1;
|
|
+ int maxBlockY = Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1;
|
|
+
|
|
+ int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
|
+ int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
|
+
|
|
+ // if nothing changed and the location didn't move out of our area, use previous set
|
|
+ if (!this.dirty && minBlockX >= this.previousMinBlockX && maxBlockX <= this.previousMaxBlockX &&
|
|
+ minBlockY >= this.previousMinBlockY && maxBlockY <= this.previousMaxBlockY &&
|
|
+ minBlockZ >= this.previousMinBlockZ && maxBlockZ <= this.previousMaxBlockZ) {
|
|
+ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
|
+ CollisionContext collisionShape = CollisionContext.of(entity);
|
|
+ if (checkOnly) {
|
|
+ for (int i = 0, length = this.blocks.size(); i < length; i++) {
|
|
+ BlockEntry blockEntry = this.blocks.unsafeGet(i);
|
|
+ mutablePos.set(blockEntry.x, blockEntry.y, blockEntry.z);
|
|
+ VoxelShape voxelshape2 = blockEntry.state.getCollisionShape(this.entity.level, mutablePos, collisionShape);
|
|
+ if (voxelshape2 != Shapes.empty()) {
|
|
+ VoxelShape voxelshape3 = voxelshape2.move((double) blockEntry.x, (double) blockEntry.y, (double) blockEntry.z);
|
|
+
|
|
+ if (voxelshape3.intersects(aabb) && predicate.test(blockEntry.state, mutablePos)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ for (int i = 0, length = this.blocks.size(); i < length; i++) {
|
|
+ BlockEntry blockEntry = this.blocks.unsafeGet(i);
|
|
+ mutablePos.set(blockEntry.x, blockEntry.y, blockEntry.z);
|
|
+ VoxelShape voxelshape2 = blockEntry.state.getCollisionShape(this.entity.level, mutablePos, collisionShape);
|
|
+
|
|
+ if (voxelshape2 != Shapes.empty()) {
|
|
+ VoxelShape voxelshape3 = voxelshape2.move((double) blockEntry.x, (double) blockEntry.y, (double) blockEntry.z);
|
|
+
|
|
+ ret |= CollisionUtil.addBoxesToIfIntersects(voxelshape3, aabb, into);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+ } else if (checkOnly) {
|
|
+ // tl;dr this is only used by inWall right now, and we don't want to generate a cache for inWall because it'll always be smaller than a move cache anyways
|
|
+ return CollisionUtil.getCollisionsForBlocksOrWorldBorder(view, this.entity, aabb, into, false, collidesWithUnloaded, false, checkOnly, predicate);
|
|
+ }
|
|
+
|
|
+ Validate.isTrue(predicate == null, "predicate cannot be used without checkOnly");
|
|
+
|
|
+ this.previousMinBlockX = minBlockX;
|
|
+ this.previousMaxBlockX = maxBlockX;
|
|
+
|
|
+ this.previousMinBlockY = minBlockY;
|
|
+ this.previousMaxBlockY = maxBlockY;
|
|
+
|
|
+ this.previousMinBlockZ = minBlockZ;
|
|
+ this.previousMaxBlockZ = maxBlockZ;
|
|
+
|
|
+ // remove old shapes, since we missed cache
|
|
+ this.blocks.setSize(0);
|
|
+ this.dirty = false;
|
|
+
|
|
+ final int minSection = WorldUtil.getMinSection(this.entity.level);
|
|
+ final int maxSection = WorldUtil.getMaxSection(this.entity.level);
|
|
+ final int minBlock = minSection << 4;
|
|
+ final int maxBlock = (maxSection << 4) | 15;
|
|
+
|
|
+ BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
|
+ CollisionContext collisionShape = null;
|
|
+
|
|
+ // special cases:
|
|
+ if (minBlockY > maxBlock || maxBlockY < minBlock) {
|
|
+ // no point in checking
|
|
+ if (!this.chunkToList.isEmpty()) {
|
|
+ for (CollisionCacheList collisionCaches : this.chunkToList) {
|
|
+ collisionCaches.remove(this);
|
|
+ }
|
|
+ this.chunkToList.clear();
|
|
+ }
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ int minYIterate = Math.max(minBlock, minBlockY);
|
|
+ int maxYIterate = Math.min(maxBlock, maxBlockY);
|
|
+
|
|
+ int minChunkX = minBlockX >> 4;
|
|
+ int maxChunkX = maxBlockX >> 4;
|
|
+
|
|
+ int minChunkZ = minBlockZ >> 4;
|
|
+ int maxChunkZ = maxBlockZ >> 4;
|
|
+
|
|
+ ServerChunkCache chunkProvider = (ServerChunkCache) this.entity.level.getChunkSource();
|
|
+
|
|
+ Set<CollisionCacheList> cacheLists = new HashSet<>(this.blocks.size());
|
|
+
|
|
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
|
+ int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
|
|
+ int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
|
|
+
|
|
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
|
+ int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
|
|
+ int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
|
|
+
|
|
+ int chunkXGlobalPos = currChunkX << 4;
|
|
+ int chunkZGlobalPos = currChunkZ << 4;
|
|
+
|
|
+ LevelChunk chunk = chunkProvider.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ);
|
|
+
|
|
+ if (chunk == null) {
|
|
+ if (collidesWithUnloaded) {
|
|
+ into.add(CollisionUtil.getBoxForChunk(currChunkX, currChunkZ));
|
|
+ ret = true;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ LevelChunkSection[] sections = chunk.getSections();
|
|
+
|
|
+ // bound y
|
|
+
|
|
+ for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
|
+ int sectionIndex = SectionPos.blockToSectionCoord(currY) - minSection;
|
|
+
|
|
+ CollisionCacheList cacheList = chunk.collisionCaches[sectionIndex];
|
|
+ if (cacheLists.add(cacheList)) {
|
|
+ cacheList.add(this);
|
|
+ }
|
|
+
|
|
+ LevelChunkSection section = sections[sectionIndex];
|
|
+ if (section == null || section.isEmpty()) {
|
|
+ // empty
|
|
+ // skip to next section
|
|
+ currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ net.minecraft.world.level.chunk.PalettedContainer<BlockState> blocks = section.states;
|
|
+
|
|
+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
|
|
+ for (int currX = minX; currX <= maxX; ++currX) {
|
|
+ int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8);
|
|
+ int blockX = currX | chunkXGlobalPos;
|
|
+ int blockY = currY;
|
|
+ int blockZ = currZ | chunkZGlobalPos;
|
|
+
|
|
+ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
|
|
+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
|
|
+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0);
|
|
+ if (edgeCount == 3) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ BlockState blockData = blocks.get(localBlockIndex);
|
|
+ if (blockData.isAir()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
|
|
+ mutablePos.set(blockX, blockY, blockZ);
|
|
+ if (collisionShape == null) {
|
|
+ collisionShape = CollisionContext.of(entity);
|
|
+ }
|
|
+ VoxelShape voxelshape2 = blockData.getCollisionShape(this.entity.level, mutablePos, collisionShape);
|
|
+
|
|
+ if (voxelshape2 != Shapes.empty() || blockData.getBlock() == Blocks.MOVING_PISTON) {
|
|
+ this.blocks.add(new BlockEntry(blockX, blockY, blockZ, blockData));
|
|
+ }
|
|
+
|
|
+ if (voxelshape2 != Shapes.empty()) {
|
|
+ VoxelShape voxelshape3 = voxelshape2.move((double) blockX, (double) blockY, (double) blockZ);
|
|
+
|
|
+ ret |= CollisionUtil.addBoxesToIfIntersects(voxelshape3, aabb, into);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (CollisionCacheList cache : this.chunkToList) {
|
|
+ if (!cacheLists.contains(cache)) {
|
|
+ cache.remove(this);
|
|
+ }
|
|
+ }
|
|
+ this.chunkToList = cacheLists;
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/entity/CollisionCacheList.java b/src/main/java/gg/airplane/entity/CollisionCacheList.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..017da9e1461250a0fd8baacdcca203d6949244fc
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/entity/CollisionCacheList.java
|
|
@@ -0,0 +1,128 @@
|
|
+package gg.airplane.entity;
|
|
+
|
|
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
|
+
|
|
+import java.util.Arrays;
|
|
+import java.util.Iterator;
|
|
+import java.util.NoSuchElementException;
|
|
+
|
|
+/**
|
|
+ * @see com.destroystokyo.paper.util.maplist.EntityList
|
|
+ */
|
|
+public class CollisionCacheList implements Iterable<CollisionCache> {
|
|
+
|
|
+ protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
|
|
+
|
|
+ {
|
|
+ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
|
+ }
|
|
+
|
|
+ protected static final CollisionCache[] EMPTY_LIST = new CollisionCache[0];
|
|
+
|
|
+ protected CollisionCache[] entities = EMPTY_LIST;
|
|
+ protected int count;
|
|
+
|
|
+ public int size() {
|
|
+ return this.count;
|
|
+ }
|
|
+
|
|
+ public boolean contains(final CollisionCache entity) {
|
|
+ return this.entityToIndex.containsKey(entity.getId());
|
|
+ }
|
|
+
|
|
+ public boolean remove(final CollisionCache entity) {
|
|
+ final int index = this.entityToIndex.remove(entity.getId());
|
|
+ if (index == Integer.MIN_VALUE) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // move the entity at the end to this index
|
|
+ final int endIndex = --this.count;
|
|
+ final CollisionCache end = this.entities[endIndex];
|
|
+ if (index != endIndex) {
|
|
+ // not empty after this call
|
|
+ this.entityToIndex.put(end.getId(), index); // update index
|
|
+ }
|
|
+ this.entities[index] = end;
|
|
+ this.entities[endIndex] = null;
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public boolean add(final CollisionCache entity) {
|
|
+ final int count = this.count;
|
|
+ final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count);
|
|
+
|
|
+ if (currIndex != Integer.MIN_VALUE) {
|
|
+ return false; // already in this list
|
|
+ }
|
|
+
|
|
+ CollisionCache[] list = this.entities;
|
|
+
|
|
+ if (list.length == count) {
|
|
+ // resize required
|
|
+ list = this.entities = Arrays.copyOf(list, (int) Math.max(4L, count * 2L)); // overflow results in negative
|
|
+ }
|
|
+
|
|
+ list[count] = entity;
|
|
+ this.count = count + 1;
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public CollisionCache getChecked(final int index) {
|
|
+ if (index < 0 || index >= this.count) {
|
|
+ throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count);
|
|
+ }
|
|
+ return this.entities[index];
|
|
+ }
|
|
+
|
|
+ public CollisionCache getUnchecked(final int index) {
|
|
+ return this.entities[index];
|
|
+ }
|
|
+
|
|
+ public CollisionCache[] getRawData() {
|
|
+ return this.entities;
|
|
+ }
|
|
+
|
|
+ public void clear() {
|
|
+ this.entityToIndex.clear();
|
|
+ Arrays.fill(this.entities, 0, this.count, null);
|
|
+ this.count = 0;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Iterator<CollisionCache> iterator() {
|
|
+ return new Iterator<CollisionCache>() {
|
|
+
|
|
+ CollisionCache lastRet;
|
|
+ int current;
|
|
+
|
|
+ @Override
|
|
+ public boolean hasNext() {
|
|
+ return this.current < CollisionCacheList.this.count;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CollisionCache next() {
|
|
+ if (this.current >= CollisionCacheList.this.count) {
|
|
+ throw new NoSuchElementException();
|
|
+ }
|
|
+ return this.lastRet = CollisionCacheList.this.entities[this.current++];
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void remove() {
|
|
+ final CollisionCache lastRet = this.lastRet;
|
|
+
|
|
+ if (lastRet == null) {
|
|
+ throw new IllegalStateException();
|
|
+ }
|
|
+ this.lastRet = null;
|
|
+
|
|
+ CollisionCacheList.this.remove(lastRet);
|
|
+ --this.current;
|
|
+ }
|
|
+ };
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/CustomCategories.java b/src/main/java/gg/airplane/flare/CustomCategories.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..031700b291ce71eac7de2ff3423a9bbfd8de4ac6
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/CustomCategories.java
|
|
@@ -0,0 +1,8 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import co.technove.flare.live.category.GraphCategory;
|
|
+
|
|
+public class CustomCategories {
|
|
+ public static final GraphCategory MC_PERF = new GraphCategory("MC Performance");
|
|
+ public static final GraphCategory ENTITIES_AND_CHUNKS = new GraphCategory("Entities & Chunks");
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/FlareCommand.java b/src/main/java/gg/airplane/flare/FlareCommand.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..12acd136a3f98a79c28e4986be3e92c5cc42658e
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/FlareCommand.java
|
|
@@ -0,0 +1,136 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import co.technove.flare.exceptions.UserReportableException;
|
|
+import co.technove.flare.internal.profiling.ProfileType;
|
|
+import gg.airplane.AirplaneConfig;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.event.ClickEvent;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import net.kyori.adventure.text.format.TextColor;
|
|
+import net.kyori.adventure.text.format.TextDecoration;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.apache.logging.log4j.Level;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.command.ConsoleCommandSender;
|
|
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
|
|
+import org.bukkit.util.StringUtil;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
+import java.time.Duration;
|
|
+import java.util.ArrayList;
|
|
+import java.util.Collections;
|
|
+import java.util.List;
|
|
+
|
|
+public class FlareCommand extends Command {
|
|
+
|
|
+ private static final String BASE_URL = "https://blog.airplane.gg/flare-tutorial/#setting-the-access-token";
|
|
+ private static final TextColor HEX = TextColor.fromHexString("#e3eaea");
|
|
+ private static final Component PREFIX = Component.text()
|
|
+ .append(Component.text("Flare ✈")
|
|
+ .color(TextColor.fromHexString("#6a7eda"))
|
|
+ .decoration(TextDecoration.BOLD, true)
|
|
+ .append(Component.text(" ", HEX)
|
|
+ .decoration(TextDecoration.BOLD, false)))
|
|
+ .asComponent();
|
|
+
|
|
+ public FlareCommand() {
|
|
+ super("flare", "Profile your server with Flare", "/flare", Collections.singletonList("profile"));
|
|
+ this.setPermission("airplane.flare");
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, String @NotNull [] args) {
|
|
+ if (!testPermission(sender)) return true;
|
|
+ if (AirplaneConfig.accessToken.length() == 0) {
|
|
+ Component clickable = Component.text(BASE_URL, HEX, TextDecoration.UNDERLINED).clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, BASE_URL));
|
|
+
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Flare currently requires an access token to use. To learn more, visit ").color(HEX).append(clickable)));
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ if (!FlareSetup.isSupported()) {
|
|
+ sender.sendMessage(PREFIX.append(
|
|
+ Component.text("Profiling is not supported in this environment, check your startup logs for the error.", NamedTextColor.RED)));
|
|
+ return true;
|
|
+ }
|
|
+ if (ProfilingManager.isProfiling()) {
|
|
+ if (args.length == 1 && args[0].equalsIgnoreCase("status")) {
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Current profile has been ran for " + ProfilingManager.getTimeRan().toString(), HEX)));
|
|
+ return true;
|
|
+ }
|
|
+ if (ProfilingManager.stop()) {
|
|
+ if (!(sender instanceof ConsoleCommandSender)) {
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Profiling has been stopped.", HEX)));
|
|
+ }
|
|
+ } else {
|
|
+ sender.sendMessage(PREFIX.append(Component.text("Profiling has already been stopped.", HEX)));
|
|
+ }
|
|
+ } else {
|
|
+ ProfileType profileType = ProfileType.ITIMER;
|
|
+ if (args.length > 0) {
|
|
+ try {
|
|
+ profileType = ProfileType.valueOf(args[0].toUpperCase());
|
|
+ } catch (Exception e) {
|
|
+ sender.sendMessage(PREFIX.append(Component
|
|
+ .text("Invalid profile type ", HEX)
|
|
+ .append(Component.text(args[0], HEX, TextDecoration.BOLD)
|
|
+ .append(Component.text("!", HEX)))
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+ ProfileType finalProfileType = profileType;
|
|
+ Bukkit.getScheduler().runTaskAsynchronously(new MinecraftInternalPlugin(), () -> {
|
|
+ try {
|
|
+ if (ProfilingManager.start(finalProfileType)) {
|
|
+ if (!(sender instanceof ConsoleCommandSender)) {
|
|
+ sender.sendMessage(PREFIX.append(Component
|
|
+ .text("Flare has been started: " + ProfilingManager.getProfilingUri(), HEX)
|
|
+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri()))
|
|
+ ));
|
|
+ sender.sendMessage(PREFIX.append(Component.text(" Run /" + commandLabel + " to stop the Flare.", HEX)));
|
|
+ }
|
|
+ } else {
|
|
+ sender.sendMessage(PREFIX.append(Component
|
|
+ .text("Flare has already been started: " + ProfilingManager.getProfilingUri(), HEX)
|
|
+ .clickEvent(ClickEvent.openUrl(ProfilingManager.getProfilingUri()))
|
|
+ ));
|
|
+ }
|
|
+ } catch (UserReportableException e) {
|
|
+ sender.sendMessage(Component.text("Flare failed to start: " + e.getUserError(), NamedTextColor.RED));
|
|
+ if (e.getCause() != null) {
|
|
+ MinecraftServer.LOGGER.log(Level.WARN, "Flare failed to start", e);
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, String @NotNull [] args) throws IllegalArgumentException {
|
|
+ List<String> list = new ArrayList<>();
|
|
+ if (ProfilingManager.isProfiling()) {
|
|
+ if (args.length == 1) {
|
|
+ String lastWord = args[0];
|
|
+ if (StringUtil.startsWithIgnoreCase("status", lastWord)) {
|
|
+ list.add("status");
|
|
+ }
|
|
+ if (StringUtil.startsWithIgnoreCase("stop", lastWord)) {
|
|
+ list.add("stop");
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (args.length <= 1) {
|
|
+ String lastWord = args.length == 0 ? "" : args[0];
|
|
+ for (ProfileType value : ProfileType.values()) {
|
|
+ if (StringUtil.startsWithIgnoreCase(value.getInternalName(), lastWord)) {
|
|
+ list.add(value.name().toLowerCase());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return list;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/FlareSetup.java b/src/main/java/gg/airplane/flare/FlareSetup.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..932b976a234f2b1d2975e72a34aa53838e9e5170
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/FlareSetup.java
|
|
@@ -0,0 +1,33 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import co.technove.flare.FlareInitializer;
|
|
+import co.technove.flare.internal.profiling.InitializationException;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.apache.logging.log4j.Level;
|
|
+
|
|
+public class FlareSetup {
|
|
+
|
|
+ private static boolean initialized = false;
|
|
+ private static boolean supported = false;
|
|
+
|
|
+ public static void init() {
|
|
+ if (initialized) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ initialized = true;
|
|
+ try {
|
|
+ for (String warning : FlareInitializer.initialize()) {
|
|
+ MinecraftServer.LOGGER.log(Level.WARN, "Flare warning: " + warning);
|
|
+ }
|
|
+ supported = true;
|
|
+ } catch (InitializationException e) {
|
|
+ MinecraftServer.LOGGER.log(Level.WARN, "Failed to enable Flare:", e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static boolean isSupported() {
|
|
+ return supported;
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/PluginLookup.java b/src/main/java/gg/airplane/flare/PluginLookup.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..22368976212ac6638a31e81d22c322a53c94d276
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/PluginLookup.java
|
|
@@ -0,0 +1,44 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import com.google.common.cache.Cache;
|
|
+import com.google.common.cache.CacheBuilder;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.plugin.Plugin;
|
|
+import org.bukkit.plugin.java.PluginClassLoader;
|
|
+
|
|
+import java.util.Optional;
|
|
+import java.util.concurrent.TimeUnit;
|
|
+
|
|
+public class PluginLookup {
|
|
+ private static final Cache<String, String> pluginNameCache = CacheBuilder.newBuilder()
|
|
+ .expireAfterAccess(1, TimeUnit.MINUTES)
|
|
+ .maximumSize(1024)
|
|
+ .build();
|
|
+
|
|
+ public static Optional<String> getPluginForClass(String name) {
|
|
+ if (name.startsWith("net.minecraft") || name.startsWith("java.") || name.startsWith("com.mojang") ||
|
|
+ name.startsWith("com.google") || name.startsWith("it.unimi") || name.startsWith("sun")) {
|
|
+ return Optional.empty();
|
|
+ }
|
|
+
|
|
+ String existing = pluginNameCache.getIfPresent(name);
|
|
+ if (existing != null) {
|
|
+ return Optional.ofNullable(existing.isEmpty() ? null : existing);
|
|
+ }
|
|
+
|
|
+ String newValue = "";
|
|
+
|
|
+ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
|
|
+ ClassLoader classLoader = plugin.getClass().getClassLoader();
|
|
+ if (classLoader instanceof PluginClassLoader) {
|
|
+ if (((PluginClassLoader) classLoader)._airplane_hasClass(name)) {
|
|
+ newValue = plugin.getName();
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pluginNameCache.put(name, newValue);
|
|
+ return Optional.ofNullable(newValue.isEmpty() ? null : newValue);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/ProfilingManager.java b/src/main/java/gg/airplane/flare/ProfilingManager.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8a429348bb4129b7810d5aa048cbc74d126624d8
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/ProfilingManager.java
|
|
@@ -0,0 +1,151 @@
|
|
+package gg.airplane.flare;
|
|
+
|
|
+import co.technove.flare.Flare;
|
|
+import co.technove.flare.FlareAuth;
|
|
+import co.technove.flare.FlareBuilder;
|
|
+import co.technove.flare.exceptions.UserReportableException;
|
|
+import co.technove.flare.internal.profiling.ProfileType;
|
|
+import gg.airplane.AirplaneConfig;
|
|
+import gg.airplane.AirplaneLogger;
|
|
+import gg.airplane.compat.ServerConfigurations;
|
|
+import gg.airplane.flare.collectors.GCEventCollector;
|
|
+import gg.airplane.flare.collectors.StatCollector;
|
|
+import gg.airplane.flare.collectors.TPSCollector;
|
|
+import gg.airplane.flare.collectors.WorldCountCollector;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
|
|
+import org.bukkit.scheduler.BukkitTask;
|
|
+import oshi.SystemInfo;
|
|
+import oshi.hardware.CentralProcessor;
|
|
+import oshi.hardware.GlobalMemory;
|
|
+import oshi.hardware.HardwareAbstractionLayer;
|
|
+import oshi.hardware.VirtualMemory;
|
|
+import oshi.software.os.OperatingSystem;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.net.URI;
|
|
+import java.time.Duration;
|
|
+import java.util.Objects;
|
|
+import java.util.logging.Level;
|
|
+
|
|
+public class ProfilingManager {
|
|
+
|
|
+ private static Flare currentFlare;
|
|
+ private static BukkitTask currentTask = null;
|
|
+
|
|
+ public static synchronized boolean isProfiling() {
|
|
+ return currentFlare != null && currentFlare.isRunning();
|
|
+ }
|
|
+
|
|
+ public static synchronized String getProfilingUri() {
|
|
+ return Objects.requireNonNull(currentFlare).getURI().map(URI::toString).orElse("Flare is not running");
|
|
+ }
|
|
+
|
|
+ public static Duration getTimeRan() {
|
|
+ Flare flare = currentFlare; // copy reference so no need to sync
|
|
+ if (flare == null) {
|
|
+ return Duration.ofMillis(0);
|
|
+ }
|
|
+ return flare.getCurrentDuration();
|
|
+ }
|
|
+
|
|
+ public static synchronized boolean start(ProfileType profileType) throws UserReportableException {
|
|
+ if (currentFlare != null && !currentFlare.isRunning()) {
|
|
+ currentFlare = null; // errored out
|
|
+ }
|
|
+ if (isProfiling()) {
|
|
+ return false;
|
|
+ }
|
|
+ if (Bukkit.isPrimaryThread()) {
|
|
+ throw new UserReportableException("Profiles should be started off-thread");
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ OperatingSystem os = new SystemInfo().getOperatingSystem();
|
|
+
|
|
+ SystemInfo systemInfo = new SystemInfo();
|
|
+ HardwareAbstractionLayer hardware = systemInfo.getHardware();
|
|
+
|
|
+ CentralProcessor processor = hardware.getProcessor();
|
|
+ CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier();
|
|
+
|
|
+ GlobalMemory memory = hardware.getMemory();
|
|
+ VirtualMemory virtualMemory = memory.getVirtualMemory();
|
|
+
|
|
+ FlareBuilder builder = new FlareBuilder()
|
|
+ .withProfileType(profileType)
|
|
+ .withMemoryProfiling(true)
|
|
+ .withAuth(FlareAuth.fromTokenAndUrl(AirplaneConfig.accessToken, AirplaneConfig.profileWebUrl))
|
|
+
|
|
+ .withFiles(ServerConfigurations.getCleanCopies())
|
|
+ .withVersion("Primary Version", Bukkit.getVersion())
|
|
+ .withVersion("Bukkit Version", Bukkit.getBukkitVersion())
|
|
+ .withVersion("Minecraft Version", Bukkit.getMinecraftVersion())
|
|
+
|
|
+ .withGraphCategories(CustomCategories.ENTITIES_AND_CHUNKS, CustomCategories.MC_PERF)
|
|
+ .withCollectors(new TPSCollector(), new WorldCountCollector(), new GCEventCollector(), new StatCollector())
|
|
+ .withClassIdentifier(PluginLookup::getPluginForClass)
|
|
+
|
|
+ .withHardware(new FlareBuilder.HardwareBuilder()
|
|
+ .setCoreCount(processor.getPhysicalProcessorCount())
|
|
+ .setThreadCount(processor.getLogicalProcessorCount())
|
|
+ .setCpuModel(processorIdentifier.getName())
|
|
+ .setCpuFrequency(processor.getMaxFreq())
|
|
+
|
|
+ .setTotalMemory(memory.getTotal())
|
|
+ .setTotalSwap(virtualMemory.getSwapTotal())
|
|
+ .setTotalVirtual(virtualMemory.getVirtualMax())
|
|
+ )
|
|
+
|
|
+ .withOperatingSystem(new FlareBuilder.OperatingSystemBuilder()
|
|
+ .setManufacturer(os.getManufacturer())
|
|
+ .setFamily(os.getFamily())
|
|
+ .setVersion(os.getVersionInfo().toString())
|
|
+ .setBitness(os.getBitness())
|
|
+ );
|
|
+
|
|
+ currentFlare = builder.build();
|
|
+ } catch (IOException e) {
|
|
+ AirplaneLogger.LOGGER.log(Level.WARNING, "Failed to read configuration files:", e);
|
|
+ throw new UserReportableException("Failed to load configuration files, check logs for further details.");
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ currentFlare.start();
|
|
+ } catch (IllegalStateException e) {
|
|
+ AirplaneLogger.LOGGER.log(Level.WARNING, "Error starting Flare:", e);
|
|
+ throw new UserReportableException("Failed to start Flare, check logs for further details.");
|
|
+ }
|
|
+
|
|
+ currentTask = Bukkit.getScheduler().runTaskLater(new MinecraftInternalPlugin(), ProfilingManager::stop, 20 * 60 * 15);
|
|
+ AirplaneLogger.LOGGER.log(Level.INFO, "Flare has been started: " + getProfilingUri());
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public static synchronized boolean stop() {
|
|
+ if (!isProfiling()) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!currentFlare.isRunning()) {
|
|
+ currentFlare = null;
|
|
+ return true;
|
|
+ }
|
|
+ AirplaneLogger.LOGGER.log(Level.INFO, "Flare has been stopped: " + getProfilingUri());
|
|
+ try {
|
|
+ currentFlare.stop();
|
|
+ } catch (IllegalStateException e) {
|
|
+ AirplaneLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", e);
|
|
+ }
|
|
+ currentFlare = null;
|
|
+
|
|
+ try {
|
|
+ currentTask.cancel();
|
|
+ } catch (Throwable t) {
|
|
+ AirplaneLogger.LOGGER.log(Level.WARNING, "Error occurred stopping Flare", t);
|
|
+ }
|
|
+
|
|
+ currentTask = null;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/collectors/GCEventCollector.java b/src/main/java/gg/airplane/flare/collectors/GCEventCollector.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..d355b6cf3132b69ab24e351e49bf4c34e2ab3752
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/collectors/GCEventCollector.java
|
|
@@ -0,0 +1,66 @@
|
|
+package gg.airplane.flare.collectors;
|
|
+
|
|
+import co.technove.flare.Flare;
|
|
+import co.technove.flare.internal.FlareInternal;
|
|
+import co.technove.flare.live.CollectorData;
|
|
+import co.technove.flare.live.EventCollector;
|
|
+import co.technove.flare.live.LiveEvent;
|
|
+import co.technove.flare.live.category.GraphCategory;
|
|
+import co.technove.flare.live.formatter.DataFormatter;
|
|
+import com.google.common.collect.ImmutableMap;
|
|
+import com.sun.management.GarbageCollectionNotificationInfo;
|
|
+
|
|
+import javax.management.ListenerNotFoundException;
|
|
+import javax.management.Notification;
|
|
+import javax.management.NotificationEmitter;
|
|
+import javax.management.NotificationListener;
|
|
+import javax.management.openmbean.CompositeData;
|
|
+import java.lang.management.GarbageCollectorMXBean;
|
|
+import java.lang.management.ManagementFactory;
|
|
+
|
|
+public class GCEventCollector extends EventCollector implements NotificationListener {
|
|
+
|
|
+ private static final CollectorData MINOR_GC = new CollectorData("builtin:gc:minor", "Minor GC", "A small pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM);
|
|
+ private static final CollectorData MAJOR_GC = new CollectorData("builtin:gc:major", "Major GC", "A large pause in the program to allow Garbage Collection to run.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM);
|
|
+ private static final CollectorData UNKNOWN_GC = new CollectorData("builtin:gc:generic", "Major GC", "A run of the Garbage Collection.", DataFormatter.MILLISECONDS, GraphCategory.SYSTEM);
|
|
+
|
|
+ public GCEventCollector() {
|
|
+ super(MINOR_GC, MAJOR_GC, UNKNOWN_GC);
|
|
+ }
|
|
+
|
|
+ private static CollectorData fromString(String string) {
|
|
+ if (string.endsWith("minor GC")) {
|
|
+ return MINOR_GC;
|
|
+ } else if (string.endsWith("major GC")) {
|
|
+ return MAJOR_GC;
|
|
+ }
|
|
+ return UNKNOWN_GC;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void start(Flare flare) {
|
|
+ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) {
|
|
+ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean;
|
|
+ notificationEmitter.addNotificationListener(this, null, null);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void stop(Flare flare) {
|
|
+ for (GarbageCollectorMXBean garbageCollectorBean : ManagementFactory.getGarbageCollectorMXBeans()) {
|
|
+ NotificationEmitter notificationEmitter = (NotificationEmitter) garbageCollectorBean;
|
|
+ try {
|
|
+ notificationEmitter.removeNotificationListener(this);
|
|
+ } catch (ListenerNotFoundException e) {
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void handleNotification(Notification notification, Object o) {
|
|
+ if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
|
|
+ GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
|
|
+ reportEvent(new LiveEvent(fromString(gcInfo.getGcAction()), System.currentTimeMillis(), (int) gcInfo.getGcInfo().getDuration(), ImmutableMap.of()));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/collectors/StatCollector.java b/src/main/java/gg/airplane/flare/collectors/StatCollector.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..5444a587a28664ebd8aaac98ee51616c078e54dd
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/collectors/StatCollector.java
|
|
@@ -0,0 +1,41 @@
|
|
+package gg.airplane.flare.collectors;
|
|
+
|
|
+import co.technove.flare.live.CollectorData;
|
|
+import co.technove.flare.live.LiveCollector;
|
|
+import co.technove.flare.live.category.GraphCategory;
|
|
+import co.technove.flare.live.formatter.DataFormatter;
|
|
+import com.sun.management.OperatingSystemMXBean;
|
|
+import oshi.SystemInfo;
|
|
+import oshi.hardware.CentralProcessor;
|
|
+
|
|
+import java.lang.management.ManagementFactory;
|
|
+import java.time.Duration;
|
|
+
|
|
+public class StatCollector extends LiveCollector {
|
|
+
|
|
+ private static final CollectorData CPU = new CollectorData("builtin:stat:cpu", "CPU Load", "The total amount of CPU usage across all cores.", DataFormatter.PERCENT, GraphCategory.SYSTEM);
|
|
+ private static final CollectorData CPU_PROCESS = new CollectorData("builtin:stat:cpu_process", "Process CPU", "The amount of CPU being used by this process.", DataFormatter.PERCENT, GraphCategory.SYSTEM);
|
|
+ private static final CollectorData MEMORY = new CollectorData("builtin:stat:memory_used", "Memory", "The amount of memory being used currently.", DataFormatter.BYTES, GraphCategory.SYSTEM);
|
|
+ private static final CollectorData MEMORY_TOTAL = new CollectorData("builtin:stat:memory_total", "Memory Total", "The total amount of memory allocated.", DataFormatter.BYTES, GraphCategory.SYSTEM);
|
|
+
|
|
+ private final OperatingSystemMXBean bean;
|
|
+ private final CentralProcessor processor;
|
|
+
|
|
+ public StatCollector() {
|
|
+ super(CPU, CPU_PROCESS, MEMORY, MEMORY_TOTAL);
|
|
+ this.interval = Duration.ofSeconds(5);
|
|
+
|
|
+ this.bean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
|
+ this.processor = new SystemInfo().getHardware().getProcessor();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ Runtime runtime = Runtime.getRuntime();
|
|
+
|
|
+ this.report(CPU, this.processor.getSystemLoadAverage(1)[0] / 100); // percentage
|
|
+ this.report(CPU_PROCESS, this.bean.getProcessCpuLoad());
|
|
+ this.report(MEMORY, runtime.totalMemory() - runtime.freeMemory());
|
|
+ this.report(MEMORY_TOTAL, runtime.totalMemory());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/collectors/TPSCollector.java b/src/main/java/gg/airplane/flare/collectors/TPSCollector.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..d7552c92552efc2193fc015b56089d39ce2f6cf0
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/collectors/TPSCollector.java
|
|
@@ -0,0 +1,31 @@
|
|
+package gg.airplane.flare.collectors;
|
|
+
|
|
+import co.technove.flare.live.CollectorData;
|
|
+import co.technove.flare.live.LiveCollector;
|
|
+import co.technove.flare.live.formatter.SuffixFormatter;
|
|
+import gg.airplane.flare.CustomCategories;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.bukkit.Bukkit;
|
|
+
|
|
+import java.time.Duration;
|
|
+import java.util.Arrays;
|
|
+
|
|
+public class TPSCollector extends LiveCollector {
|
|
+ private static final CollectorData TPS = new CollectorData("airplane:tps", "TPS", "Ticks per second, or how fast the server updates. For a smooth server this should be a constant 20TPS.", SuffixFormatter.of("TPS"), CustomCategories.MC_PERF);
|
|
+ private static final CollectorData MSPT = new CollectorData("airplane:mspt", "MSPT", "Milliseconds per tick, which can show how well your server is performing. This value should always be under 50mspt.", SuffixFormatter.of("mspt"), CustomCategories.MC_PERF);
|
|
+
|
|
+ public TPSCollector() {
|
|
+ super(TPS, MSPT);
|
|
+
|
|
+ this.interval = Duration.ofSeconds(5);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ long[] times = MinecraftServer.getServer().tickTimes5s.getTimes();
|
|
+ double mspt = ((double) Arrays.stream(times).sum() / (double) times.length) * 1.0E-6D;
|
|
+
|
|
+ this.report(TPS, Math.min(20D, Math.round(Bukkit.getServer().getTPS()[0] * 100d) / 100d));
|
|
+ this.report(MSPT, (double) Math.round(mspt * 100d) / 100d);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/flare/collectors/WorldCountCollector.java b/src/main/java/gg/airplane/flare/collectors/WorldCountCollector.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8b1ca40d99b04db9dca522aa42eb68c761c73037
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/flare/collectors/WorldCountCollector.java
|
|
@@ -0,0 +1,45 @@
|
|
+package gg.airplane.flare.collectors;
|
|
+
|
|
+import co.technove.flare.live.CollectorData;
|
|
+import co.technove.flare.live.LiveCollector;
|
|
+import co.technove.flare.live.formatter.SuffixFormatter;
|
|
+import gg.airplane.flare.CustomCategories;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.World;
|
|
+import org.bukkit.craftbukkit.CraftWorld;
|
|
+
|
|
+import java.time.Duration;
|
|
+
|
|
+public class WorldCountCollector extends LiveCollector {
|
|
+
|
|
+ private static final CollectorData PLAYER_COUNT = new CollectorData("airplane:world:playercount", "Player Count", "The number of players currently on the server.", new SuffixFormatter(" Player", " Players"), CustomCategories.ENTITIES_AND_CHUNKS);
|
|
+ private static final CollectorData ENTITY_COUNT = new CollectorData("airplane:world:entitycount", "Entity Count", "The number of entities in all worlds", new SuffixFormatter(" Entity", " Entities"), CustomCategories.ENTITIES_AND_CHUNKS);
|
|
+ private static final CollectorData CHUNK_COUNT = new CollectorData("airplane:world:chunkcount", "Chunk Count", "The number of chunks currently loaded.", new SuffixFormatter(" Chunk", " Chunks"), CustomCategories.ENTITIES_AND_CHUNKS);
|
|
+ private static final CollectorData TILE_ENTITY_COUNT = new CollectorData("airplane:world:blockentitycount", "Block Entity Count", "The number of block entities currently loaded.", new SuffixFormatter(" Block Entity", " Block Entities"), CustomCategories.ENTITIES_AND_CHUNKS);
|
|
+
|
|
+ public WorldCountCollector() {
|
|
+ super(PLAYER_COUNT, ENTITY_COUNT, CHUNK_COUNT, TILE_ENTITY_COUNT);
|
|
+
|
|
+ this.interval = Duration.ofSeconds(5);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ int entities = 0;
|
|
+ int chunkCount = 0;
|
|
+ int tileEntityCount = 0;
|
|
+
|
|
+ if (!Bukkit.isStopping()) {
|
|
+ for (World world : Bukkit.getWorlds()) {
|
|
+ entities += ((CraftWorld) world).getHandle().entityManager.getEntityGetter().getCount();
|
|
+ chunkCount += world.getChunkCount();
|
|
+ tileEntityCount += world.getTileEntityCount();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ this.report(PLAYER_COUNT, Bukkit.getOnlinePlayers().size());
|
|
+ this.report(ENTITY_COUNT, entities);
|
|
+ this.report(CHUNK_COUNT, chunkCount);
|
|
+ this.report(TILE_ENTITY_COUNT, tileEntityCount);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/structs/FluidDirectionCache.java b/src/main/java/gg/airplane/structs/FluidDirectionCache.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..aa8467b9dda1f7707e41f50ac7b3e9d7343723ec
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/structs/FluidDirectionCache.java
|
|
@@ -0,0 +1,136 @@
|
|
+package gg.airplane.structs;
|
|
+
|
|
+import it.unimi.dsi.fastutil.HashCommon;
|
|
+
|
|
+/**
|
|
+ * This is a replacement for the cache used in FluidTypeFlowing.
|
|
+ * The requirements for the previous cache were:
|
|
+ * - Store 200 entries
|
|
+ * - Look for the flag in the cache
|
|
+ * - If it exists, move to front of cache
|
|
+ * - If it doesn't exist, remove last entry in cache and insert in front
|
|
+ *
|
|
+ * This class accomplishes something similar, however has a few different
|
|
+ * requirements put into place to make this more optimize:
|
|
+ *
|
|
+ * - maxDistance is the most amount of entries to be checked, instead
|
|
+ * of having to check the entire list.
|
|
+ * - In combination with that, entries are all tracked by age and how
|
|
+ * frequently they're used. This enables us to remove old entries,
|
|
+ * without constantly shifting any around.
|
|
+ *
|
|
+ * Usage of the previous map would have to reset the head every single usage,
|
|
+ * shifting the entire map. Here, nothing happens except an increment when
|
|
+ * the cache is hit, and when it needs to replace an old element only a single
|
|
+ * element is modified.
|
|
+ */
|
|
+public class FluidDirectionCache<T> {
|
|
+
|
|
+ private static class FluidDirectionEntry<T> {
|
|
+ private final T data;
|
|
+ private final boolean flag;
|
|
+ private int uses = 0;
|
|
+ private int age = 0;
|
|
+
|
|
+ private FluidDirectionEntry(T data, boolean flag) {
|
|
+ this.data = data;
|
|
+ this.flag = flag;
|
|
+ }
|
|
+
|
|
+ public int getValue() {
|
|
+ return this.uses - (this.age >> 1); // age isn't as important as uses
|
|
+ }
|
|
+
|
|
+ public void incrementUses() {
|
|
+ this.uses = this.uses + 1 & Integer.MAX_VALUE;
|
|
+ }
|
|
+
|
|
+ public void incrementAge() {
|
|
+ this.age = this.age + 1 & Integer.MAX_VALUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private final FluidDirectionEntry[] entries;
|
|
+ private final int mask;
|
|
+ private final int maxDistance; // the most amount of entries to check for a value
|
|
+
|
|
+ public FluidDirectionCache(int size) {
|
|
+ int arraySize = HashCommon.nextPowerOfTwo(size);
|
|
+ this.entries = new FluidDirectionEntry[arraySize];
|
|
+ this.mask = arraySize - 1;
|
|
+ this.maxDistance = Math.min(arraySize, 4);
|
|
+ }
|
|
+
|
|
+ public Boolean getValue(T data) {
|
|
+ FluidDirectionEntry curr;
|
|
+ int pos;
|
|
+
|
|
+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) {
|
|
+ return null;
|
|
+ } else if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return curr.flag;
|
|
+ }
|
|
+
|
|
+ int checked = 1; // start at 1 because we already checked the first spot above
|
|
+
|
|
+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) {
|
|
+ if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return curr.flag;
|
|
+ } else if (++checked >= this.maxDistance) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public void putValue(T data, boolean flag) {
|
|
+ FluidDirectionEntry<T> curr;
|
|
+ int pos;
|
|
+
|
|
+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) {
|
|
+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add
|
|
+ return;
|
|
+ } else if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ int checked = 1; // start at 1 because we already checked the first spot above
|
|
+
|
|
+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) {
|
|
+ if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return;
|
|
+ } else if (++checked >= this.maxDistance) {
|
|
+ this.forceAdd(data, flag);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add
|
|
+ }
|
|
+
|
|
+ private void forceAdd(T data, boolean flag) {
|
|
+ int expectedPos = HashCommon.mix(data.hashCode()) & this.mask;
|
|
+
|
|
+ int toRemovePos = expectedPos;
|
|
+ FluidDirectionEntry entryToRemove = this.entries[toRemovePos];
|
|
+
|
|
+ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) {
|
|
+ int pos = i & this.mask;
|
|
+ FluidDirectionEntry entry = this.entries[pos];
|
|
+ if (entry.getValue() < entryToRemove.getValue()) {
|
|
+ toRemovePos = pos;
|
|
+ entryToRemove = entry;
|
|
+ }
|
|
+
|
|
+ entry.incrementAge(); // use this as a mechanism to age the other entries
|
|
+ }
|
|
+
|
|
+ // remove the least used/oldest entry
|
|
+ this.entries[toRemovePos] = new FluidDirectionEntry(data, flag);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/structs/ItemListWithBitset.java b/src/main/java/gg/airplane/structs/ItemListWithBitset.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1b7a4ee47f4445d7f2ac91d3a73ae113edbdddb2
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/structs/ItemListWithBitset.java
|
|
@@ -0,0 +1,114 @@
|
|
+package gg.airplane.structs;
|
|
+
|
|
+import net.minecraft.core.NonNullList;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import org.apache.commons.lang.Validate;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.util.AbstractList;
|
|
+import java.util.Arrays;
|
|
+import java.util.List;
|
|
+
|
|
+public class ItemListWithBitset extends AbstractList<ItemStack> {
|
|
+ public static ItemListWithBitset fromList(List<ItemStack> list) {
|
|
+ if (list instanceof ItemListWithBitset ours) {
|
|
+ return ours;
|
|
+ }
|
|
+ return new ItemListWithBitset(list);
|
|
+ }
|
|
+
|
|
+ private static ItemStack[] createArray(int size) {
|
|
+ ItemStack[] array = new ItemStack[size];
|
|
+ Arrays.fill(array, ItemStack.EMPTY);
|
|
+ return array;
|
|
+ }
|
|
+
|
|
+ private final ItemStack[] items;
|
|
+
|
|
+ private long bitSet = 0;
|
|
+ private final long allBits;
|
|
+
|
|
+ private static class OurNonNullList extends NonNullList<ItemStack> {
|
|
+ protected OurNonNullList(List<ItemStack> delegate) {
|
|
+ super(delegate, ItemStack.EMPTY);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public final NonNullList<ItemStack> nonNullList = new OurNonNullList(this);
|
|
+
|
|
+ private ItemListWithBitset(List<ItemStack> list) {
|
|
+ this(list.size());
|
|
+
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
+ this.set(i, list.get(i));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public ItemListWithBitset(int size) {
|
|
+ Validate.isTrue(size < Long.BYTES * 8, "size is too large");
|
|
+
|
|
+ this.items = createArray(size);
|
|
+ this.allBits = ((1L << size) - 1);
|
|
+ }
|
|
+
|
|
+ public boolean isCompletelyEmpty() {
|
|
+ return this.bitSet == 0;
|
|
+ }
|
|
+
|
|
+ public boolean hasFullStacks() {
|
|
+ return (this.bitSet & this.allBits) == allBits;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemStack set(int index, @NotNull ItemStack itemStack) {
|
|
+ ItemStack existing = this.items[index];
|
|
+
|
|
+ this.items[index] = itemStack;
|
|
+
|
|
+ if (itemStack == ItemStack.EMPTY) {
|
|
+ this.bitSet &= ~(1L << index);
|
|
+ } else {
|
|
+ this.bitSet |= 1L << index;
|
|
+ }
|
|
+
|
|
+ return existing;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public ItemStack get(int var0) {
|
|
+ return this.items[var0];
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int size() {
|
|
+ return this.items.length;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void clear() {
|
|
+ Arrays.fill(this.items, ItemStack.EMPTY);
|
|
+ }
|
|
+
|
|
+ // these are unsupported for block inventories which have a static size
|
|
+ @Override
|
|
+ public void add(int var0, ItemStack var1) {
|
|
+ throw new UnsupportedOperationException();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemStack remove(int var0) {
|
|
+ throw new UnsupportedOperationException();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String toString() {
|
|
+ return "ItemListWithBitset{" +
|
|
+ "items=" + Arrays.toString(items) +
|
|
+ ", bitSet=" + Long.toString(bitSet, 2) +
|
|
+ ", allBits=" + Long.toString(allBits, 2) +
|
|
+ ", size=" + this.items.length +
|
|
+ '}';
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..a7f297ebb569f7c1f205e967ca485be70013a714
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java
|
|
@@ -0,0 +1,119 @@
|
|
+package gg.airplane.structs;
|
|
+
|
|
+import it.unimi.dsi.fastutil.HashCommon;
|
|
+
|
|
+/**
|
|
+ * A replacement for the cache used in Biome.
|
|
+ */
|
|
+public class Long2FloatAgingCache {
|
|
+
|
|
+ private static class AgingEntry {
|
|
+ private long data;
|
|
+ private float value;
|
|
+ private int uses = 0;
|
|
+ private int age = 0;
|
|
+
|
|
+ private AgingEntry(long data, float value) {
|
|
+ this.data = data;
|
|
+ this.value = value;
|
|
+ }
|
|
+
|
|
+ public void replace(long data, float flag) {
|
|
+ this.data = data;
|
|
+ this.value = flag;
|
|
+ }
|
|
+
|
|
+ public int getValue() {
|
|
+ return this.uses - (this.age >> 1); // age isn't as important as uses
|
|
+ }
|
|
+
|
|
+ public void incrementUses() {
|
|
+ this.uses = this.uses + 1 & Integer.MAX_VALUE;
|
|
+ }
|
|
+
|
|
+ public void incrementAge() {
|
|
+ this.age = this.age + 1 & Integer.MAX_VALUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private final AgingEntry[] entries;
|
|
+ private final int mask;
|
|
+ private final int maxDistance; // the most amount of entries to check for a value
|
|
+
|
|
+ public Long2FloatAgingCache(int size) {
|
|
+ int arraySize = HashCommon.nextPowerOfTwo(size);
|
|
+ this.entries = new AgingEntry[arraySize];
|
|
+ this.mask = arraySize - 1;
|
|
+ this.maxDistance = Math.min(arraySize, 4);
|
|
+ }
|
|
+
|
|
+ public float getValue(long data) {
|
|
+ AgingEntry curr;
|
|
+ int pos;
|
|
+
|
|
+ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) {
|
|
+ return Float.NaN;
|
|
+ } else if (data == curr.data) {
|
|
+ curr.incrementUses();
|
|
+ return curr.value;
|
|
+ }
|
|
+
|
|
+ int checked = 1; // start at 1 because we already checked the first spot above
|
|
+
|
|
+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) {
|
|
+ if (data == curr.data) {
|
|
+ curr.incrementUses();
|
|
+ return curr.value;
|
|
+ } else if (++checked >= this.maxDistance) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return Float.NaN;
|
|
+ }
|
|
+
|
|
+ public void putValue(long data, float value) {
|
|
+ AgingEntry curr;
|
|
+ int pos;
|
|
+
|
|
+ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) {
|
|
+ this.entries[pos] = new AgingEntry(data, value); // add
|
|
+ return;
|
|
+ } else if (data == curr.data) {
|
|
+ curr.incrementUses();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ int checked = 1; // start at 1 because we already checked the first spot above
|
|
+
|
|
+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) {
|
|
+ if (data == curr.data) {
|
|
+ curr.incrementUses();
|
|
+ return;
|
|
+ } else if (++checked >= this.maxDistance) {
|
|
+ this.forceAdd(data, value);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ this.entries[pos] = new AgingEntry(data, value); // add
|
|
+ }
|
|
+
|
|
+ private void forceAdd(long data, float value) {
|
|
+ int expectedPos = HashCommon.mix(HashCommon.long2int(data)) & this.mask;
|
|
+ AgingEntry entryToRemove = this.entries[expectedPos];
|
|
+
|
|
+ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) {
|
|
+ int pos = i & this.mask;
|
|
+ AgingEntry entry = this.entries[pos];
|
|
+ if (entry.getValue() < entryToRemove.getValue()) {
|
|
+ entryToRemove = entry;
|
|
+ }
|
|
+
|
|
+ entry.incrementAge(); // use this as a mechanism to age the other entries
|
|
+ }
|
|
+
|
|
+ // remove the least used/oldest entry
|
|
+ entryToRemove.replace(data, value);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java
|
|
index 98ca1199a823cdf55b913396ce0a24554e85f116..b2e2d6ee25a88f9c8f6769c6774643220f9c148b 100644
|
|
--- a/src/main/java/io/papermc/paper/util/CollisionUtil.java
|
|
+++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java
|
|
@@ -519,7 +519,7 @@ public final class CollisionUtil {
|
|
if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
|
|
mutablePos.set(blockX, blockY, blockZ);
|
|
if (collisionShape == null) {
|
|
- collisionShape = new LazyEntityCollisionContext(entity);
|
|
+ collisionShape = CollisionContext.of(entity); // Airplane - use normal context, we already tore it apart
|
|
}
|
|
VoxelShape voxelshape2 = blockData.getCollisionShape(getter, mutablePos, collisionShape);
|
|
if (voxelshape2 != Shapes.empty()) {
|
|
@@ -547,6 +547,18 @@ public final class CollisionUtil {
|
|
return ret;
|
|
}
|
|
|
|
+ public static boolean getEntityCollisionsWithCache(final net.minecraft.world.level.Level getter, Entity entity, AABB aabb, List<AABB> into,
|
|
+ final boolean loadChunks, final boolean collidesWithUnloaded,
|
|
+ final boolean checkBorder, final boolean checkOnly, final BiPredicate<BlockState, BlockPos> predicate) {
|
|
+ if (checkOnly) {
|
|
+ return entity.collisionCache.getCollisions(getter, aabb, into, collidesWithUnloaded, checkOnly, predicate) ||
|
|
+ getEntityHardCollisions(getter, entity, aabb, into, checkOnly, null);
|
|
+ } else {
|
|
+ return entity.collisionCache.getCollisions(getter, aabb, into, collidesWithUnloaded, checkOnly, predicate) |
|
|
+ getEntityHardCollisions(getter, entity, aabb, into, checkOnly, null);
|
|
+ }
|
|
+ }
|
|
+
|
|
public static boolean getEntityHardCollisions(final CollisionGetter getter, final Entity entity, AABB aabb,
|
|
final List<AABB> into, final boolean checkOnly, final Predicate<Entity> predicate) {
|
|
if (isEmpty(aabb) || !(getter instanceof EntityGetter entityGetter)) {
|
|
@@ -599,6 +611,8 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
+ // Airplane start - unneeded
|
|
+ /*
|
|
public static final class LazyEntityCollisionContext extends EntityCollisionContext {
|
|
|
|
private CollisionContext delegate;
|
|
@@ -638,6 +652,8 @@ public final class CollisionUtil {
|
|
return this.getDelegate().canStandOnFluid(state, fluid);
|
|
}
|
|
}
|
|
+ */
|
|
+ // Airplane end
|
|
|
|
private CollisionUtil() {
|
|
throw new RuntimeException();
|
|
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
|
|
index 505546d32eea4682452dbac02311433157f6a30e..5c7b9ad379f3c272e15648dd16f4df9245d927da 100644
|
|
--- a/src/main/java/net/minecraft/Util.java
|
|
+++ b/src/main/java/net/minecraft/Util.java
|
|
@@ -344,6 +344,10 @@ public class Util {
|
|
}
|
|
|
|
public static <V> CompletableFuture<List<V>> sequence(List<? extends CompletableFuture<? extends V>> futures) {
|
|
+ // Airplane start - faster sequencing without all of.. _that_
|
|
+ return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
|
|
+ .thenApply(unused -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
|
|
+ /*
|
|
return futures.stream().reduce(CompletableFuture.completedFuture(Lists.newArrayList()), (completableFuture, completableFuture2) -> {
|
|
return completableFuture2.thenCombine(completableFuture, (object, list) -> {
|
|
List<V> list2 = Lists.newArrayListWithCapacity(list.size() + 1);
|
|
@@ -359,6 +363,8 @@ public class Util {
|
|
return list3;
|
|
});
|
|
});
|
|
+ */
|
|
+ // Airplane end
|
|
}
|
|
|
|
public static <V> CompletableFuture<List<V>> sequenceFailFast(List<? extends CompletableFuture<? extends V>> futures) {
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 5a4172faaf960d48939d6a485719041987df9242..7120a77ec93009faf1f18fd6c9bc020de66a5f73 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1729,7 +1729,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
@DontObfuscate
|
|
public String getServerModName() {
|
|
- return "Paper"; // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla!
|
|
+ return "Airplane"; // Airplane // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla!
|
|
}
|
|
|
|
public SystemReport fillSystemReport(SystemReport details) {
|
|
@@ -2305,6 +2305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
public ProfilerFiller getProfiler() {
|
|
+ if (gg.airplane.AirplaneConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE;
|
|
return this.profiler;
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
index b6ee0e709b0f0529b99567bc9b8fb6bfd99bcd8e..e2901132b78126c0a4eb04363dfe6a0dccd1313f 100644
|
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
@@ -234,6 +234,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // load mappings for stacktrace deobf and etc.
|
|
io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider
|
|
// Paper end
|
|
+ gg.airplane.AirplaneConfig.load(); // Airplane - config
|
|
+ gg.airplane.commands.AirplaneCommands.init(); // Airplane - command
|
|
|
|
this.setPvpAllowed(dedicatedserverproperties.pvp);
|
|
this.setFlightAllowed(dedicatedserverproperties.allowFlight);
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 7bb12a30d8b4a817f5a85969c6200306fd45a43d..aca05e6cbec746241c5ebe31374c593d75850415 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -2413,8 +2413,28 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
return ChunkMap.this.level.getServer().getScaledTrackingDistance(initialDistance);
|
|
}
|
|
|
|
+ private static int getHighestRange(Entity parent, int highest) {
|
|
+ List<Entity> passengers = parent.getPassengers();
|
|
+
|
|
+ for (int i = 0, size = passengers.size(); i < size; i++) {
|
|
+ Entity entity = passengers.get(i);
|
|
+ int range = entity.getType().clientTrackingRange() * 16;
|
|
+ range = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, range); // Paper
|
|
+
|
|
+ if (range > highest) { // Paper - we need the lowest range thanks to the fact that our tracker doesn't account for passenger logic // Tuinity - not anymore!
|
|
+ highest = range;
|
|
+ }
|
|
+
|
|
+ highest = getHighestRange(entity, highest);
|
|
+ }
|
|
+
|
|
+ return highest;
|
|
+ }
|
|
+
|
|
private int getEffectiveRange() {
|
|
int i = this.range;
|
|
+ // Airplane start - remove iterators and streams
|
|
+ /*
|
|
Iterator iterator = this.entity.getIndirectPassengers().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -2426,6 +2446,9 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
i = j;
|
|
}
|
|
}
|
|
+ */
|
|
+ i = getHighestRange(this.entity, i);
|
|
+ // Airplane end
|
|
|
|
return this.scaledRange(i);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index 4a3dbcfdacb809d162663c379c4e8151be522432..5116b1f90c0f27ce1d3cee6632563f32356a1eb6 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -984,6 +984,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
}
|
|
// Paper end - optimize isOutisdeRange
|
|
this.level.getProfiler().push("pollingChunks");
|
|
+ this.level.resetIceAndSnowTick(); // Airplane - reset ice & snow tick random
|
|
int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
|
|
boolean flag2 = level.ticksPerAnimalSpawns != 0L && worlddata.getGameTime() % level.ticksPerAnimalSpawns == 0L; // CraftBukkit
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
index 1c6bf5a3014beaf5f9c1c38aed4cf3225e50b8bb..a453aac607f9b38f0bf90038ff2d8071687c4bae 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
|
@@ -174,6 +174,7 @@ public class ServerEntity {
|
|
boolean flag4 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L;
|
|
|
|
if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround() && !(com.destroystokyo.paper.PaperConfig.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync
|
|
+ if (flag2 || flag3 || this.entity instanceof AbstractArrow) { // Airplane
|
|
if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) {
|
|
if (flag2) {
|
|
packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.isOnGround());
|
|
@@ -183,6 +184,7 @@ public class ServerEntity {
|
|
} else {
|
|
packet1 = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), (byte) i, (byte) j, this.entity.isOnGround());
|
|
}
|
|
+ } // Airplane
|
|
} else {
|
|
this.wasOnGround = this.entity.isOnGround();
|
|
this.teleportDelay = 0;
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index d40a367670ccea01978cabf7d45f3c1a690662fc..7c8eaf0e23b24a484a21b6c4a732b488b246b5b9 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -772,7 +772,20 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
|
|
gameprofilerfiller.push("tick");
|
|
- this.guardEntityTick(this::tickNonPassenger, entity);
|
|
+ // Airplane start - copied from this.guardEntityTick
|
|
+ try {
|
|
+ this.tickNonPassenger(entity); // Airplane - changed
|
|
+ MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick
|
|
+ } catch (Throwable throwable) {
|
|
+ if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
|
+ // Paper start - Prevent tile entity and entity crashes
|
|
+ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
|
|
+ MinecraftServer.LOGGER.error(msg, throwable);
|
|
+ getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable)));
|
|
+ entity.discard();
|
|
+ // Paper end
|
|
+ }
|
|
+ // Airplane end
|
|
gameprofilerfiller.pop();
|
|
}
|
|
}
|
|
@@ -830,9 +843,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
// Paper start - optimise random block ticking
|
|
private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos();
|
|
- private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom();
|
|
+ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); public java.util.Random getThreadUnsafeRandom() { return this.randomTickRandom; } // Airplane - getter
|
|
// Paper end
|
|
|
|
+ private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.randomTickRandom.nextInt(16); } // Airplane
|
|
+
|
|
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
|
|
ChunkPos chunkcoordintpair = chunk.getPos();
|
|
boolean flag = this.isRaining();
|
|
@@ -843,7 +858,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
gameprofilerfiller.push("thunder");
|
|
final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change
|
|
|
|
- if (!this.paperConfig.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - disable thunder
|
|
+ if (!this.paperConfig.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0 && chunk.shouldDoLightning(this.random)) { // Spigot // Paper - disable thunder // Airplane - replace random with shouldDoLightning
|
|
blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper
|
|
if (this.isRainingAt(blockposition)) {
|
|
DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition);
|
|
@@ -867,7 +882,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
|
|
gameprofilerfiller.popPush("iceandsnow");
|
|
- if (!this.paperConfig.disableIceAndSnow && this.randomTickRandom.nextInt(16) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking
|
|
+ if (!this.paperConfig.disableIceAndSnow && (this.currentIceAndSnowTick++ & 15) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking // Airplane - optimize further random ticking
|
|
// Paper start - optimise chunk ticking
|
|
this.getRandomBlockPosition(j, 0, k, 15, blockposition);
|
|
int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1;
|
|
diff --git a/src/main/java/net/minecraft/world/CompoundContainer.java b/src/main/java/net/minecraft/world/CompoundContainer.java
|
|
index 087ae3a6b49872a3580eb1a572bdbc493711a77a..5ef8657197beea06c1dcad6a32968c56a823b182 100644
|
|
--- a/src/main/java/net/minecraft/world/CompoundContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/CompoundContainer.java
|
|
@@ -1,5 +1,6 @@
|
|
package net.minecraft.world;
|
|
|
|
+import net.minecraft.core.Direction; // Airplane
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
|
|
@@ -72,6 +73,23 @@ public class CompoundContainer implements Container {
|
|
this.container2 = second;
|
|
}
|
|
|
|
+ // Airplane start
|
|
+ @Override
|
|
+ public boolean hasEmptySlot(Direction enumdirection) {
|
|
+ return this.container1.hasEmptySlot(null) || this.container2.hasEmptySlot(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isCompletelyFull(Direction enumdirection) {
|
|
+ return this.container1.isCompletelyFull(null) && this.container2.isCompletelyFull(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isCompletelyEmpty(Direction enumdirection) {
|
|
+ return this.container1.isCompletelyEmpty(null) && this.container2.isCompletelyEmpty(null);
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
@Override
|
|
public int getContainerSize() {
|
|
return this.container1.getContainerSize() + this.container2.getContainerSize();
|
|
diff --git a/src/main/java/net/minecraft/world/Container.java b/src/main/java/net/minecraft/world/Container.java
|
|
index 7437f01ca8f416e2c9150250e324af4725a4efb6..bdcd0e38a3ba904811112f41d8bfbfc0902ef190 100644
|
|
--- a/src/main/java/net/minecraft/world/Container.java
|
|
+++ b/src/main/java/net/minecraft/world/Container.java
|
|
@@ -1,6 +1,8 @@
|
|
package net.minecraft.world;
|
|
|
|
import java.util.Set;
|
|
+
|
|
+import net.minecraft.core.Direction; // Airplane
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.Item;
|
|
import net.minecraft.world.item.ItemStack;
|
|
@@ -9,6 +11,63 @@ import org.bukkit.craftbukkit.entity.CraftHumanEntity;
|
|
// CraftBukkit end
|
|
|
|
public interface Container extends Clearable {
|
|
+ // Airplane start - allow the inventory to override and optimize these frequent calls
|
|
+ default boolean hasEmptySlot(@org.jetbrains.annotations.Nullable Direction enumdirection) { // there is a slot with 0 items in it
|
|
+ if (this instanceof WorldlyContainer worldlyContainer) {
|
|
+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) {
|
|
+ if (this.getItem(i).isEmpty()) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ int size = this.getContainerSize();
|
|
+ for (int i = 0; i < size; i++) {
|
|
+ if (this.getItem(i).isEmpty()) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ default boolean isCompletelyFull(@org.jetbrains.annotations.Nullable Direction enumdirection) { // every stack is maxed
|
|
+ if (this instanceof WorldlyContainer worldlyContainer) {
|
|
+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) {
|
|
+ ItemStack itemStack = this.getItem(i);
|
|
+ if (itemStack.getCount() < itemStack.getMaxStackSize()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ int size = this.getContainerSize();
|
|
+ for (int i = 0; i < size; i++) {
|
|
+ ItemStack itemStack = this.getItem(i);
|
|
+ if (itemStack.getCount() < itemStack.getMaxStackSize()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ default boolean isCompletelyEmpty(@org.jetbrains.annotations.Nullable Direction enumdirection) {
|
|
+ if (this instanceof WorldlyContainer worldlyContainer) {
|
|
+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) {
|
|
+ if (!this.getItem(i).isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ int size = this.getContainerSize();
|
|
+ for (int i = 0; i < size; i++) {
|
|
+ if (!this.getItem(i).isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ // Airplane end
|
|
|
|
int LARGE_MAX_STACK_SIZE = 64;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index b7c4700fd5db14c77e7ee78311dd77a754d9d41c..2cad495e834300cdd3ee65cdf43d28ae602c9625 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -338,6 +338,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
protected int numCollisions = 0; // Paper
|
|
public void inactiveTick() { }
|
|
// Spigot end
|
|
+ // Airplane start
|
|
+ public int activatedPriority = gg.airplane.AirplaneConfig.maximumActivationPrio; // golf score
|
|
+ public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // used where needed
|
|
+ public final gg.airplane.entity.CollisionCache collisionCache = new gg.airplane.entity.CollisionCache(this);
|
|
+ // Airplane end
|
|
|
|
public float getBukkitYaw() {
|
|
return this.yRot;
|
|
@@ -362,17 +367,36 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
this.isLegacyTrackingEntity = isLegacyTrackingEntity;
|
|
}
|
|
|
|
+ private org.spigotmc.TrackingRange.TrackingRangeType getFurthestEntity(Entity entity, net.minecraft.server.level.ChunkMap chunkMap, org.spigotmc.TrackingRange.TrackingRangeType type, int range) {
|
|
+ List<Entity> passengers = entity.getPassengers();
|
|
+ for (int i = 0, size = passengers.size(); i < size; i++) {
|
|
+ Entity passenger = passengers.get(i);
|
|
+ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType;
|
|
+ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal());
|
|
+ if (passengerRange > range) {
|
|
+ type = passengerType;
|
|
+ range = passengerRange;
|
|
+ }
|
|
+
|
|
+ type = this.getFurthestEntity(passenger, chunkMap, type, range);
|
|
+ }
|
|
+
|
|
+ return type;
|
|
+ }
|
|
+
|
|
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> getPlayersInTrackRange() {
|
|
// determine highest range of passengers
|
|
if (this.passengers.isEmpty()) {
|
|
return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()]
|
|
.getObjectsInRange(MCUtil.getCoordinateKey(this));
|
|
}
|
|
- Iterable<Entity> passengers = this.getIndirectPassengers();
|
|
+ //Iterable<Entity> passengers = this.getIndirectPassengers(); // Airplane
|
|
net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap;
|
|
org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType;
|
|
int range = chunkMap.getEntityTrackerRange(type.ordinal());
|
|
|
|
+ // Airplane start - use getFurthestEntity to skip getIndirectPassengers
|
|
+ /*
|
|
for (Entity passenger : passengers) {
|
|
org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType;
|
|
int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal());
|
|
@@ -381,6 +405,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
range = passengerRange;
|
|
}
|
|
}
|
|
+ */
|
|
+ type = this.getFurthestEntity(this, chunkMap, type, range);
|
|
+ // Airplane end
|
|
|
|
return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this));
|
|
}
|
|
@@ -1264,8 +1291,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
}
|
|
}
|
|
|
|
- io.papermc.paper.util.CollisionUtil.getCollisions(world, this, collisionBox, potentialCollisions, false, true,
|
|
- false, false, null, null);
|
|
+ // Airplane start - use collision cache
|
|
+ io.papermc.paper.util.CollisionUtil.getEntityCollisionsWithCache(world, this, collisionBox, potentialCollisions, false, true,
|
|
+ false, false, null);
|
|
+ // Airplane end
|
|
|
|
if (io.papermc.paper.util.CollisionUtil.isCollidingWithBorderEdge(world.getWorldBorder(), collisionBox)) {
|
|
io.papermc.paper.util.CollisionUtil.addBoxesToIfIntersects(world.getWorldBorder().getCollisionShape(), collisionBox, potentialCollisions);
|
|
@@ -2449,10 +2478,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f);
|
|
|
|
// Paper start
|
|
- return io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this.level, this, axisalignedbb, null,
|
|
- false, false, false, true, (iblockdata, blockposition) -> {
|
|
- return iblockdata.isSuffocating(this.level, blockposition);
|
|
- });
|
|
+ return io.papermc.paper.util.CollisionUtil.getEntityCollisionsWithCache(this.level, this, axisalignedbb, null, // Airplane - use cache
|
|
+ false, false, false, true, this.level.isAlmostSuffocating); // Airplane - don't allocate lambda here
|
|
// Paper end
|
|
}
|
|
}
|
|
@@ -3839,16 +3866,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
}
|
|
|
|
public boolean updateFluidHeightAndDoFluidPushing(Tag<Fluid> tag, double d0) {
|
|
- if (this.touchingUnloadedChunk()) {
|
|
+ if (false && this.touchingUnloadedChunk()) { // Airplane - cost of a lookup here is the same cost as below, so skip
|
|
return false;
|
|
} else {
|
|
AABB axisalignedbb = this.getBoundingBox().deflate(0.001D);
|
|
- int i = Mth.floor(axisalignedbb.minX);
|
|
- int j = Mth.ceil(axisalignedbb.maxX);
|
|
- int k = Mth.floor(axisalignedbb.minY);
|
|
- int l = Mth.ceil(axisalignedbb.maxY);
|
|
- int i1 = Mth.floor(axisalignedbb.minZ);
|
|
- int j1 = Mth.ceil(axisalignedbb.maxZ);
|
|
+ // Airplane start - rename
|
|
+ int minBlockX = Mth.floor(axisalignedbb.minX);
|
|
+ int maxBlockX = Mth.ceil(axisalignedbb.maxX);
|
|
+ int minBlockY = Mth.floor(axisalignedbb.minY);
|
|
+ int maxBlockY = Mth.ceil(axisalignedbb.maxY);
|
|
+ int minBlockZ = Mth.floor(axisalignedbb.minZ);
|
|
+ int maxBlockZ = Mth.ceil(axisalignedbb.maxZ);
|
|
+ // Airplane end
|
|
double d1 = 0.0D;
|
|
boolean flag = this.isPushedByFluid();
|
|
boolean flag1 = false;
|
|
@@ -3856,14 +3885,62 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
int k1 = 0;
|
|
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
|
|
|
|
- for (int l1 = i; l1 < j; ++l1) {
|
|
- for (int i2 = k; i2 < l; ++i2) {
|
|
- for (int j2 = i1; j2 < j1; ++j2) {
|
|
- blockposition_mutableblockposition.set(l1, i2, j2);
|
|
- FluidState fluid = this.level.getFluidState(blockposition_mutableblockposition);
|
|
+ // Airplane start - based off CollisionUtil.getCollisionsForBlocksOrWorldBorder
|
|
+ final int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this.level);
|
|
+ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(this.level);
|
|
+ final int minBlock = minSection << 4;
|
|
+ final int maxBlock = (maxSection << 4) | 15;
|
|
+
|
|
+ // special cases:
|
|
+ if (minBlockY > maxBlock || maxBlockY < minBlock) {
|
|
+ // no point in checking
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ int minYIterate = Math.max(minBlock, minBlockY);
|
|
+ int maxYIterate = Math.min(maxBlock, maxBlockY);
|
|
+
|
|
+ int minChunkX = minBlockX >> 4;
|
|
+ int maxChunkX = maxBlockX >> 4;
|
|
+
|
|
+ int minChunkZ = minBlockZ >> 4;
|
|
+ int maxChunkZ = maxBlockZ >> 4;
|
|
+
|
|
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
|
+ int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
|
|
+ int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 16; // coordinate in chunk
|
|
+
|
|
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
|
+ int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
|
|
+ int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 16; // coordinate in chunk
|
|
+
|
|
+ net.minecraft.world.level.chunk.ChunkAccess chunk = this.level.getChunkIfLoadedImmediately(currChunkX, currChunkZ);
|
|
+ if (chunk == null) {
|
|
+ return false; // if we're touching an unloaded chunk then it's false
|
|
+ }
|
|
+
|
|
+ net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections();
|
|
+
|
|
+ for (int currY = minYIterate; currY < maxYIterate; ++currY) {
|
|
+ net.minecraft.world.level.chunk.LevelChunkSection section = sections[(currY >> 4) - minSection];
|
|
+
|
|
+ if (section == null || section.isEmpty() || section.fluidStateCount == 0) { // if no fluids, nothing in this section
|
|
+ // empty
|
|
+ // skip to next section
|
|
+ currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ net.minecraft.world.level.chunk.PalettedContainer<BlockState> blocks = section.states;
|
|
+
|
|
+ for (int currZ = minZ; currZ < maxZ; ++currZ) {
|
|
+ for (int currX = minX; currX < maxX; ++currX) {
|
|
+ int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8);
|
|
+ FluidState fluid = blocks.get(localBlockIndex).getFluidState();
|
|
|
|
if (fluid.is(tag)) {
|
|
- double d2 = (double) ((float) i2 + fluid.getHeight(this.level, blockposition_mutableblockposition));
|
|
+ blockposition_mutableblockposition.set((currChunkX << 4) + currX, currY, (currChunkZ << 4) + currZ);
|
|
+ double d2 = (double) ((float) currY + fluid.getHeight(this.level, blockposition_mutableblockposition));
|
|
|
|
if (d2 >= axisalignedbb.minY) {
|
|
flag1 = true;
|
|
@@ -3880,9 +3957,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
|
|
}
|
|
}
|
|
}
|
|
+ }
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
+ // Airplane end
|
|
|
|
if (vec3d.length() > 0.0D) {
|
|
if (k1 > 0) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
index 1c446dba5de89698397041ee38a2e1a00bec8a56..03371c99c34ba4b2ffde3f6da36f171b582e3c3f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
@@ -294,6 +294,7 @@ public class EntityType<T extends Entity> implements EntityTypeTest<Entity, T> {
|
|
return Registry.ENTITY_TYPE.getOptional(ResourceLocation.tryParse(id));
|
|
}
|
|
|
|
+ public boolean dabEnabled = false; // Airplane
|
|
// Paper start - add id
|
|
public final String id;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
index 465dab588e770bf4d1e645e1f451a066f388014a..df12ba04df24e1c70c43e00283ea2a78ae2ca422 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -142,7 +142,6 @@ import org.bukkit.event.entity.EntityTeleportEvent;
|
|
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
|
// CraftBukkit end
|
|
|
|
-import co.aikar.timings.MinecraftTimings; // Paper
|
|
|
|
public abstract class LivingEntity extends Entity {
|
|
|
|
@@ -1828,6 +1827,20 @@ public abstract class LivingEntity extends Entity {
|
|
return this.lastClimbablePos;
|
|
}
|
|
|
|
+
|
|
+ // Airplane start
|
|
+ private boolean cachedOnClimable = false;
|
|
+ private BlockPos lastClimbingPosition = null;
|
|
+
|
|
+ public boolean onClimableCached() {
|
|
+ if (!this.blockPosition().equals(this.lastClimbingPosition)) {
|
|
+ this.cachedOnClimable = this.onClimbable();
|
|
+ this.lastClimbingPosition = this.blockPosition();
|
|
+ }
|
|
+ return this.cachedOnClimable;
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
public boolean onClimbable() {
|
|
if (this.isSpectator()) {
|
|
return false;
|
|
@@ -3444,7 +3457,10 @@ public abstract class LivingEntity extends Entity {
|
|
Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ());
|
|
|
|
// Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists
|
|
- return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - use distanceToSqr
|
|
+ // Airplane start
|
|
+ //return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - use distanceToSqr
|
|
+ return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level.rayTraceDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
|
+ // Airplane end
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
index 55d07e70a67e08bab3a7a66076c980986736e5b8..b247d9dc2b2f5b4a6e87ca35e2a08f9be4e7a7b4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
|
@@ -207,10 +207,10 @@ public abstract class Mob extends LivingEntity {
|
|
@Override
|
|
public void inactiveTick() {
|
|
super.inactiveTick();
|
|
- if (this.goalSelector.inactiveTick()) {
|
|
+ if (this.goalSelector.inactiveTick(this.activatedPriority, true)) { // Airplane - pass activated priroity
|
|
this.goalSelector.tick();
|
|
}
|
|
- if (this.targetSelector.inactiveTick()) {
|
|
+ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Airplane - pass activated priority
|
|
this.targetSelector.tick();
|
|
}
|
|
}
|
|
@@ -838,9 +838,11 @@ public abstract class Mob extends LivingEntity {
|
|
this.sensing.tick();
|
|
this.level.getProfiler().pop();
|
|
this.level.getProfiler().push("targetSelector");
|
|
+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Airplane - use this to alternate ticking
|
|
this.targetSelector.tick();
|
|
this.level.getProfiler().pop();
|
|
this.level.getProfiler().push("goalSelector");
|
|
+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Airplane - use this to alternate ticking
|
|
this.goalSelector.tick();
|
|
this.level.getProfiler().pop();
|
|
this.level.getProfiler().push("navigation");
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/Brain.java b/src/main/java/net/minecraft/world/entity/ai/Brain.java
|
|
index 75022e5888de2730bb29d1e7535ddc63e2c5f713..a7c71fc4e49079d11b9a0537a4f6d20bbbba815b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/Brain.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/Brain.java
|
|
@@ -76,15 +76,15 @@ public class Brain<E extends LivingEntity> {
|
|
|
|
@Override
|
|
public <T> DataResult<Brain<E>> decode(DynamicOps<T> dynamicOps, MapLike<T> mapLike) {
|
|
- MutableObject<DataResult<Builder<Brain.MemoryValue<?>>>> mutableObject = new MutableObject<>(DataResult.success(ImmutableList.builder()));
|
|
+ MutableObject<DataResult<Builder<Brain.MemoryValue<?>>>> mutableObject2 = new MutableObject<>(DataResult.success(ImmutableList.builder())); // Airplane - compile error due to decompiler
|
|
mapLike.entries().forEach((pair) -> {
|
|
DataResult<MemoryModuleType<?>> dataResult = Registry.MEMORY_MODULE_TYPE.parse(dynamicOps, pair.getFirst());
|
|
DataResult<? extends Brain.MemoryValue<?>> dataResult2 = dataResult.flatMap((memoryModuleType) -> {
|
|
return this.captureRead(memoryModuleType, dynamicOps, (T)pair.getSecond());
|
|
});
|
|
- mutableObject.setValue(mutableObject.getValue().apply2(Builder::add, dataResult2));
|
|
+ mutableObject2.setValue(mutableObject2.getValue().apply2(Builder::add, dataResult2)); // Airplane - compile error due to decompiler
|
|
});
|
|
- ImmutableList<Brain.MemoryValue<?>> immutableList = mutableObject.getValue().resultOrPartial(Brain.LOGGER::error).map(Builder::build).orElseGet(ImmutableList::of);
|
|
+ ImmutableList<Brain.MemoryValue<?>> immutableList = mutableObject2.getValue().resultOrPartial(Brain.LOGGER::error).map(Builder::build).orElseGet(ImmutableList::of); // Airplane - compile error due to decompiler
|
|
return DataResult.success(new Brain<>(memoryModules, sensors, immutableList, mutableObject::getValue));
|
|
}
|
|
|
|
@@ -174,7 +174,7 @@ public class Brain<E extends LivingEntity> {
|
|
}
|
|
|
|
public <U> Optional<U> getMemory(MemoryModuleType<U> type) {
|
|
- return this.memories.get(type).map(ExpirableValue::getValue);
|
|
+ return (Optional<U>) this.memories.get(type).map(ExpirableValue::getValue); // Airplane - compile fix
|
|
}
|
|
|
|
public <U> long getTimeUntilExpiry(MemoryModuleType<U> type) {
|
|
@@ -465,7 +465,7 @@ public class Brain<E extends LivingEntity> {
|
|
private final Optional<? extends ExpirableValue<U>> value;
|
|
|
|
static <U> Brain.MemoryValue<U> createUnchecked(MemoryModuleType<U> type, Optional<? extends ExpirableValue<?>> data) {
|
|
- return new Brain.MemoryValue<>(type, data);
|
|
+ return new Brain.MemoryValue<>(type, (Optional) data); // Airplane - compile fix
|
|
}
|
|
|
|
MemoryValue(MemoryModuleType<U> type, Optional<? extends ExpirableValue<U>> data) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
index 9cbfda029782385d1a7987f5be46d450bd8a758e..d3e97858dacc850012e5585ac44a1aeafc82b8f4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
|
@@ -22,9 +22,11 @@ public class AttributeMap {
|
|
private final Map<Attribute, AttributeInstance> attributes = Maps.newHashMap();
|
|
private final Set<AttributeInstance> dirtyAttributes = Sets.newHashSet();
|
|
private final AttributeSupplier supplier;
|
|
+ private final java.util.function.Function<Attribute, AttributeInstance> createInstance; // Airplane
|
|
|
|
public AttributeMap(AttributeSupplier defaultAttributes) {
|
|
this.supplier = defaultAttributes;
|
|
+ this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Airplane
|
|
}
|
|
|
|
private void onAttributeModified(AttributeInstance instance) {
|
|
@@ -44,11 +46,10 @@ public class AttributeMap {
|
|
}).collect(Collectors.toList());
|
|
}
|
|
|
|
+
|
|
@Nullable
|
|
public AttributeInstance getInstance(Attribute attribute) {
|
|
- return this.attributes.computeIfAbsent(attribute, (attributex) -> {
|
|
- return this.supplier.createInstance(this::onAttributeModified, attributex);
|
|
- });
|
|
+ return this.attributes.computeIfAbsent(attribute, this.createInstance); // Airplane - cache lambda, as for some reason java allocates it anyways
|
|
}
|
|
|
|
public boolean hasAttribute(Attribute attribute) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
|
index afbb2acd27416c801af3d718850b82a170734cd3..0b206a3f964f5143e0720890d78d682b8b558c15 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
|
|
@@ -68,6 +68,7 @@ public class AcquirePoi extends Behavior<PathfinderMob> {
|
|
@Override
|
|
protected void start(ServerLevel world, PathfinderMob entity, long time) {
|
|
this.nextScheduledStart = time + 20L + (long)world.getRandom().nextInt(20);
|
|
+ if (entity.getNavigation().isStuck()) this.nextScheduledStart += 200L; // Airplane - wait an additional 10s to check again if they're stuck
|
|
PoiManager poiManager = world.getPoiManager();
|
|
this.batchCache.long2ObjectEntrySet().removeIf((entry) -> {
|
|
return !entry.getValue().isStillValid(time);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java
|
|
index 42d466f7f162943886078eba3db18f2dfc2d7bee..32da2d3f87758bee359522769ebbee73f4f3256a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java
|
|
@@ -37,7 +37,11 @@ public class VillagerPanicTrigger extends Behavior<Villager> {
|
|
|
|
@Override
|
|
protected void tick(ServerLevel serverLevel, Villager villager, long l) {
|
|
- if (l % 100L == 0L) {
|
|
+ // Airplane start
|
|
+ if (villager.nextGolemPanic < 0) villager.nextGolemPanic = l + 100;
|
|
+ if (--villager.nextGolemPanic < l) {
|
|
+ villager.nextGolemPanic = -1;
|
|
+ // Airplane end
|
|
villager.spawnGolemIfNeeded(serverLevel, l, 3);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
|
index a96831d5df2b88203aec8fe2a5909708764b38ee..441e2edd8357c4f11093b4dee2192780ac3f3579 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
|
@@ -49,9 +49,12 @@ public class GoalSelector {
|
|
}
|
|
|
|
// Paper start
|
|
- public boolean inactiveTick() {
|
|
+ public boolean inactiveTick(int tickRate, boolean inactive) { // Airplane start
|
|
+ if (inactive && !gg.airplane.AirplaneConfig.dearEnabled) tickRate = 4; // reset to Paper's
|
|
+ tickRate = Math.min(tickRate, this.newGoalRate);
|
|
this.curRate++;
|
|
- return this.curRate % this.newGoalRate == 0;
|
|
+ return this.curRate % tickRate == 0;
|
|
+ // Airplane end
|
|
}
|
|
public boolean hasTasks() {
|
|
for (WrappedGoal task : this.availableGoals) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
|
index c28ade67f6a59146064a57bf016a646197f47ac4..419e6275a400f587f57e81684520072a93654aae 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
|
@@ -114,6 +114,7 @@ public abstract class MoveToBlockGoal extends Goal {
|
|
for(int m = 0; m <= l; m = m > 0 ? -m : 1 - m) {
|
|
for(int n = m < l && m > -l ? l : 0; n <= l; n = n > 0 ? -n : 1 - n) {
|
|
mutableBlockPos.setWithOffset(blockPos, m, k - 1, n);
|
|
+ if (!this.mob.level.hasChunkAt(mutableBlockPos)) continue; // Airplane - if this block isn't loaded, continue
|
|
if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level, mutableBlockPos)) {
|
|
this.blockPos = mutableBlockPos;
|
|
setTargetPosition(mutableBlockPos.immutable()); // Paper
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
index 3ee691d4caccbc1b3e0f52decb41d436ac0d08ec..8a0aea6b28295e03aaac1768336b1bc36d9ad9e9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
@@ -74,9 +74,18 @@ public class TargetingConditions {
|
|
}
|
|
|
|
if (this.range > 0.0D) {
|
|
- double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D;
|
|
- double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper
|
|
+ // Airplane start - check range before getting visibility
|
|
+ // d = invisibility percent, e = follow range adjusted for invisibility, f = distance
|
|
double f = baseEntity.distanceToSqr(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ());
|
|
+ double followRangeRaw = this.useFollowRange ? this.getFollowRange(baseEntity) : this.range;
|
|
+
|
|
+ if (f > followRangeRaw * followRangeRaw) { // the actual follow range will always be this value or smaller, so if the distance is larger then it never will return true after getting invis
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D;
|
|
+ double e = Math.max((followRangeRaw) * d, 2.0D); // Paper
|
|
+ // Airplane end
|
|
if (f > e * e) {
|
|
return false;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
index 9ed2f8f2843c634fd14dda4459f85b7140dd447a..cda441f5cf64f061dc145fbeba175874c31cebe4 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
|
|
@@ -254,13 +254,22 @@ public class Bat extends AmbientCreature {
|
|
}
|
|
}
|
|
|
|
+ // Airplane start - only check for spooky season once an hour
|
|
+ private static boolean isSpookySeason = false;
|
|
+ private static final int ONE_HOUR = 20 * 60 * 60;
|
|
+ private static int lastSpookyCheck = -ONE_HOUR;
|
|
private static boolean isHalloween() {
|
|
+ if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) {
|
|
LocalDate localdate = LocalDate.now();
|
|
int i = localdate.get(ChronoField.DAY_OF_MONTH);
|
|
int j = localdate.get(ChronoField.MONTH_OF_YEAR);
|
|
|
|
- return j == 10 && i >= 20 || j == 11 && i <= 3;
|
|
+ isSpookySeason = j == 10 && i >= 20 || j == 11 && i <= 3;
|
|
+ lastSpookyCheck = net.minecraft.server.MinecraftServer.currentTick;
|
|
+ }
|
|
+ return isSpookySeason;
|
|
}
|
|
+ // Airplane end
|
|
|
|
@Override
|
|
protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
|
|
index 2b8725087fd3bfeca7162bda2783fdacd13a8390..16adf07774d568852b289ae8507779dd78110fc3 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
|
|
@@ -275,9 +275,11 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable {
|
|
return true;
|
|
}
|
|
|
|
+ private int behaviorTick = 0; // Airplane
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
this.level.getProfiler().push("axolotlBrain");
|
|
+ if (this.behaviorTick++ % this.activatedPriority == 0) // Airplane
|
|
this.getBrain().tick((ServerLevel) this.level, this); // CraftBukkit - decompile error
|
|
this.level.getProfiler().pop();
|
|
this.level.getProfiler().push("axolotlActivityUpdate");
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
|
|
index c7e24da48aaff9fbd1a8272483231744326e3a8e..5eceec2802a14b59ed60c600622d1f76ff6a77fa 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
|
|
@@ -144,9 +144,11 @@ public class Goat extends Animal {
|
|
return (Brain<Goat>) super.getBrain(); // CraftBukkit - decompile error
|
|
}
|
|
|
|
+ private int behaviorTick = 0; // Airplane
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
this.level.getProfiler().push("goatBrain");
|
|
+ if (this.behaviorTick++ % this.activatedPriority == 0) // Airplane
|
|
this.getBrain().tick((ServerLevel) this.level, this); // CraftBukkit - decompile error
|
|
this.level.getProfiler().pop();
|
|
this.level.getProfiler().push("goatActivityUpdate");
|
|
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
index 063f3e4c67e6716c9a03dbe4b72eafd32e4f0d53..dae6f7a05426ea31d13c82458b33e20abc2571b6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
|
@@ -243,10 +243,16 @@ public class ItemEntity extends Entity {
|
|
if (entityitem.isMergable()) {
|
|
// Paper Start - Fix items merging through walls
|
|
if (this.level.paperConfig.fixItemsMergingThroughWalls) {
|
|
+ // Airplane start - skip the allocations
|
|
+ /*
|
|
net.minecraft.world.level.ClipContext rayTrace = new net.minecraft.world.level.ClipContext(this.position(), entityitem.position(),
|
|
net.minecraft.world.level.ClipContext.Block.COLLIDER, net.minecraft.world.level.ClipContext.Fluid.NONE, this);
|
|
net.minecraft.world.phys.BlockHitResult rayTraceResult = level.clip(rayTrace);
|
|
if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
|
|
+ */
|
|
+ if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) ==
|
|
+ net.minecraft.world.phys.HitResult.Type.BLOCK) continue;
|
|
+ // Airplane end
|
|
}
|
|
// Paper End
|
|
this.tryToMerge(entityitem);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
index b27e2e1e2270a7a2f3e36b90a19237e39e4175b8..e5068464d66641b4ae96aecee8d1216ae2dc37c1 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
|
|
@@ -312,11 +312,17 @@ public class EnderMan extends Monster implements NeutralMob {
|
|
private boolean teleport(double x, double y, double z) {
|
|
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, y, z);
|
|
|
|
- while (blockposition_mutableblockposition.getY() > this.level.getMinBuildHeight() && !this.level.getBlockState(blockposition_mutableblockposition).getMaterial().blocksMotion()) {
|
|
+ // Airplane start - single chunk lookup
|
|
+ net.minecraft.world.level.chunk.LevelChunk chunk = this.level.getChunkIfLoaded(blockposition_mutableblockposition);
|
|
+ if (chunk == null) {
|
|
+ return false;
|
|
+ }
|
|
+ // Airplane end
|
|
+ while (blockposition_mutableblockposition.getY() > this.level.getMinBuildHeight() && !chunk.getBlockState(blockposition_mutableblockposition).getMaterial().blocksMotion()) { // Airplane
|
|
blockposition_mutableblockposition.move(Direction.DOWN);
|
|
}
|
|
|
|
- BlockState iblockdata = this.level.getBlockState(blockposition_mutableblockposition);
|
|
+ BlockState iblockdata = chunk.getBlockState(blockposition_mutableblockposition); // Airplane
|
|
boolean flag = iblockdata.getMaterial().blocksMotion();
|
|
boolean flag1 = iblockdata.getFluidState().is((Tag) FluidTags.WATER);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
|
index c5b9c0c650df5f4b7e3d2a431dc900e210104dea..4c111bc335ab8cafe6674b77dcd7a22cb1c58ae9 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
|
|
@@ -123,12 +123,14 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
|
|
|
|
@Override
|
|
public Brain<Hoglin> getBrain() {
|
|
- return super.getBrain();
|
|
+ return (Brain<Hoglin>) super.getBrain(); // Airplane - decompile fix
|
|
}
|
|
|
|
+ private int behaviorTick; // Airplane
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
this.level.getProfiler().push("hoglinBrain");
|
|
+ if (this.behaviorTick++ % this.activatedPriority == 0) // Airplane
|
|
this.getBrain().tick((ServerLevel)this.level, this);
|
|
this.level.getProfiler().pop();
|
|
HoglinAi.updateActivity(this);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
|
|
index 37712fe8585ede00569026bba5377ab61ad08ff5..758558c1e312edc730d1f5d7ffdbc5a41356a6e5 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
|
|
@@ -289,9 +289,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
|
|
return !this.cannotHunt;
|
|
}
|
|
|
|
+ private int behaviorTick; // Airplane
|
|
@Override
|
|
protected void customServerAiStep() {
|
|
this.level.getProfiler().push("piglinBrain");
|
|
+ if (this.behaviorTick++ % this.activatedPriority == 0) // Airplane
|
|
this.getBrain().tick((ServerLevel) this.level, (Piglin) this); // CraftBukkit - decompile error
|
|
this.level.getProfiler().pop();
|
|
PiglinAi.updateActivity(this);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
index b746453774054dd2db023b7eea265a395cfc7cc9..4904971710acbb2855ed9ed96fead4aed189c494 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
|
@@ -142,6 +142,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
return villageplacetype == PoiType.MEETING;
|
|
});
|
|
|
|
+ public long nextGolemPanic = -1; // Airplane
|
|
+
|
|
public Villager(EntityType<? extends Villager> entityType, Level world) {
|
|
this(entityType, world, VillagerType.PLAINS);
|
|
}
|
|
@@ -245,11 +247,17 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
|
}
|
|
// Spigot End
|
|
|
|
+ private int behaviorTick = 0; // Airplane
|
|
@Override
|
|
protected void customServerAiStep() { mobTick(false); }
|
|
protected void mobTick(boolean inactive) {
|
|
this.level.getProfiler().push("villagerBrain");
|
|
- if (!inactive) this.getBrain().tick((ServerLevel) this.level, this); // CraftBukkit - decompile error // Paper
|
|
+ // Airplane start
|
|
+ if (!inactive) {
|
|
+ if (this.behaviorTick++ % this.activatedPriority == 0) // Airplane
|
|
+ this.getBrain().tick((ServerLevel) this.level, this);
|
|
+ }
|
|
+ // Airplane end
|
|
this.level.getProfiler().pop();
|
|
if (this.assignProfessionWhenSpawned) {
|
|
this.assignProfessionWhenSpawned = false;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java
|
|
index c7e16e96633e17b951f0681599c5b3efc3ce1e6c..8d329bca0818033df41fbd781028919c73e052a6 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/player/Inventory.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java
|
|
@@ -688,6 +688,8 @@ public class Inventory implements Container, Nameable {
|
|
}
|
|
|
|
public boolean contains(ItemStack stack) {
|
|
+ // Airplane start - don't allocate iterators
|
|
+ /*
|
|
Iterator iterator = this.compartments.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -702,6 +704,18 @@ public class Inventory implements Container, Nameable {
|
|
}
|
|
}
|
|
}
|
|
+ */
|
|
+ for (int i = 0; i < this.compartments.size(); i++) {
|
|
+ List<ItemStack> list = this.compartments.get(i);
|
|
+ for (int j = 0; j < list.size(); j++) {
|
|
+ ItemStack itemstack1 = list.get(j);
|
|
+
|
|
+ if (!itemstack1.isEmpty() && itemstack1.sameItem(stack)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Airplane end
|
|
|
|
return false;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
index 6339203bda5e569d5df241dd589eb36e7233704b..461173191361fdb0c2c950eacf03ba43693e1908 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
|
|
@@ -42,6 +42,36 @@ public abstract class Projectile extends Entity {
|
|
super(type, world);
|
|
}
|
|
|
|
+ // Airplane start
|
|
+ private static int loadedThisTick = 0;
|
|
+ private static int loadedTick;
|
|
+
|
|
+ private int loadedLifetime = 0;
|
|
+ @Override
|
|
+ public void setPos(double x, double y, double z) {
|
|
+ int currentTick = net.minecraft.server.MinecraftServer.currentTick;
|
|
+ if (loadedTick != currentTick) {
|
|
+ loadedTick = currentTick;
|
|
+ loadedThisTick = 0;
|
|
+ }
|
|
+ int previousX = Mth.floor(this.getX()) >> 4, previousZ = Mth.floor(this.getZ()) >> 4;
|
|
+ int newX = Mth.floor(x) >> 4, newZ = Mth.floor(z) >> 4;
|
|
+ if (previousX != newX || previousZ != newZ) {
|
|
+ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level.getChunkSource()).getChunkAtIfLoadedMainThread(newX, newZ) != null;
|
|
+ if (!isLoaded) {
|
|
+ if (Projectile.loadedThisTick > gg.airplane.AirplaneConfig.maxProjectileLoadsPerTick) {
|
|
+ if (++this.loadedLifetime > gg.airplane.AirplaneConfig.maxProjectileLoadsPerProjectile) {
|
|
+ this.discard();
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ Projectile.loadedThisTick++;
|
|
+ }
|
|
+ }
|
|
+ super.setPos(x, y, z);
|
|
+ }
|
|
+ // Airplane start
|
|
+
|
|
public void setOwner(@Nullable Entity entity) {
|
|
if (entity != null) {
|
|
this.ownerUUID = entity.getUUID();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
|
|
index f57864ce919ef4721cfb5913c636fe8903ce4cc1..610d756b4a264deb58ea8144c951f652697805ee 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
|
|
@@ -40,7 +40,10 @@ import org.bukkit.inventory.InventoryHolder;
|
|
|
|
public abstract class AbstractMinecartContainer extends AbstractMinecart implements Container, MenuProvider {
|
|
|
|
+ // Airplane start
|
|
private NonNullList<ItemStack> itemStacks;
|
|
+ private gg.airplane.structs.ItemListWithBitset itemStacksOptimized;
|
|
+ // Airplane end
|
|
@Nullable
|
|
public ResourceLocation lootTable;
|
|
public long lootTableSeed;
|
|
@@ -89,12 +92,18 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
|
|
|
|
protected AbstractMinecartContainer(EntityType<?> type, Level world) {
|
|
super(type, world);
|
|
- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513
|
|
+ // Airplane start
|
|
+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513
|
|
+ this.itemStacks = this.itemStacksOptimized.nonNullList;
|
|
+ // Airplane end
|
|
}
|
|
|
|
protected AbstractMinecartContainer(EntityType<?> type, double x, double y, double z, Level world) {
|
|
super(type, world, x, y, z);
|
|
- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513
|
|
+ // Airplane start
|
|
+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513
|
|
+ this.itemStacks = this.itemStacksOptimized.nonNullList;
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
@@ -217,7 +226,10 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
|
|
protected void readAdditionalSaveData(CompoundTag nbt) {
|
|
super.readAdditionalSaveData(nbt);
|
|
this.lootableData.loadNbt(nbt); // Paper
|
|
- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
|
|
+ // Airplane start
|
|
+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize());
|
|
+ this.itemStacks = this.itemStacksOptimized.nonNullList;
|
|
+ // Airplane end
|
|
if (nbt.contains("LootTable", 8)) {
|
|
this.lootTable = new ResourceLocation(nbt.getString("LootTable"));
|
|
this.lootTableSeed = nbt.getLong("LootTableSeed");
|
|
diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
|
|
index 6b960f0a31175bcfd8d477ee5b3c4d783303cdd5..3a81d3a58b937c9800cd0be738439cff0ba28ce9 100644
|
|
--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
|
|
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
|
|
@@ -25,8 +25,13 @@ public class ShapelessRecipe implements CraftingRecipe {
|
|
final String group;
|
|
final ItemStack result;
|
|
final NonNullList<Ingredient> ingredients;
|
|
+ private final boolean isBukkit; // Airplane
|
|
|
|
+ // Airplane start
|
|
public ShapelessRecipe(ResourceLocation id, String group, ItemStack output, NonNullList<Ingredient> input) {
|
|
+ this(id, group, output, input, false);
|
|
+ }
|
|
+ public ShapelessRecipe(ResourceLocation id, String group, ItemStack output, NonNullList<Ingredient> input, boolean isBukkit) { this.isBukkit = isBukkit; // Airplane end
|
|
this.id = id;
|
|
this.group = group;
|
|
this.result = output;
|
|
@@ -73,6 +78,28 @@ public class ShapelessRecipe implements CraftingRecipe {
|
|
}
|
|
|
|
public boolean matches(CraftingContainer inventory, Level world) {
|
|
+ // Airplane start
|
|
+ if (!this.isBukkit) {
|
|
+ java.util.List<Ingredient> ingredients = com.google.common.collect.Lists.newArrayList(this.ingredients.toArray(new Ingredient[0]));
|
|
+
|
|
+ inventory: for (int index = 0; index < inventory.getContainerSize(); index++) {
|
|
+ ItemStack itemStack = inventory.getItem(index);
|
|
+
|
|
+ if (!itemStack.isEmpty()) {
|
|
+ for (int i = 0; i < ingredients.size(); i++) {
|
|
+ if (ingredients.get(i).test(itemStack)) {
|
|
+ ingredients.remove(i);
|
|
+ continue inventory;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ingredients.isEmpty();
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
StackedContents autorecipestackmanager = new StackedContents();
|
|
int i = 0;
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
|
index 6200a8ab4f7b2c40e7139cfb90a62f42c5828de2..f6a8e10347b9a374e2da9d28734b72443555459d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/BlockGetter.java
|
|
+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
|
@@ -73,6 +73,16 @@ public interface BlockGetter extends LevelHeightAccessor {
|
|
});
|
|
}
|
|
|
|
+ // Airplane start - broken down variant of below rayTraceBlock, used by World#rayTraceDirect
|
|
+ default net.minecraft.world.phys.BlockHitResult.Type rayTraceBlockDirect(Vec3 vec3d, Vec3 vec3d1, BlockPos blockposition, BlockState iblockdata, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) {
|
|
+ if (iblockdata.isAir()) return null; // Tuinity - optimise air cases
|
|
+ VoxelShape voxelshape = ClipContext.Block.COLLIDER.get(iblockdata, this, blockposition, voxelshapecoll);
|
|
+ net.minecraft.world.phys.BlockHitResult movingobjectpositionblock = this.clipWithInteractionOverride(vec3d, vec3d1, blockposition, voxelshape, iblockdata);
|
|
+
|
|
+ return movingobjectpositionblock == null ? null : movingobjectpositionblock.getType();
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
// CraftBukkit start - moved block handling into separate method for use by Block#rayTrace
|
|
default BlockHitResult rayTraceBlock(ClipContext raytrace1, BlockPos blockposition) {
|
|
// Paper start - Prevent raytrace from loading chunks
|
|
diff --git a/src/main/java/net/minecraft/world/level/GameRules.java b/src/main/java/net/minecraft/world/level/GameRules.java
|
|
index e7ca5d6fb8922e7e8065864f736b06056be080a0..833ad6fbedfc275b3fde640b0e873f23e61acc3b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/GameRules.java
|
|
+++ b/src/main/java/net/minecraft/world/level/GameRules.java
|
|
@@ -90,6 +90,7 @@ public class GameRules {
|
|
public static final GameRules.Key<GameRules.BooleanValue> RULE_UNIVERSAL_ANGER = GameRules.register("universalAnger", GameRules.Category.MOBS, GameRules.BooleanValue.create(false));
|
|
public static final GameRules.Key<GameRules.IntegerValue> RULE_PLAYERS_SLEEPING_PERCENTAGE = GameRules.register("playersSleepingPercentage", GameRules.Category.PLAYER, GameRules.IntegerValue.create(100));
|
|
private final Map<GameRules.Key<?>, GameRules.Value<?>> rules;
|
|
+ private final GameRules.Value<?>[] gameruleArray;
|
|
|
|
private static <T extends GameRules.Value<T>> GameRules.Key<T> register(String name, GameRules.Category category, GameRules.Type<T> type) {
|
|
GameRules.Key<T> gamerules_gamerulekey = new GameRules.Key<>(name, category);
|
|
@@ -108,17 +109,33 @@ public class GameRules {
|
|
}
|
|
|
|
public GameRules() {
|
|
- this.rules = (Map) GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> {
|
|
+ // Airplane start - use this to ensure gameruleArray is initialized
|
|
+ this((Map) GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> {
|
|
return ((GameRules.Type) entry.getValue()).createRule();
|
|
- }));
|
|
+ })));
|
|
+ // Airplane end
|
|
}
|
|
|
|
private GameRules(Map<GameRules.Key<?>, GameRules.Value<?>> rules) {
|
|
this.rules = rules;
|
|
+
|
|
+ // Airplane start
|
|
+ int arraySize = rules.keySet().stream().mapToInt(key -> key.gameRuleIndex).max().orElse(-1) + 1;
|
|
+ GameRules.Value<?>[] values = new GameRules.Value[arraySize];
|
|
+
|
|
+ for (Entry<GameRules.Key<?>, GameRules.Value<?>> entry : rules.entrySet()) {
|
|
+ values[entry.getKey().gameRuleIndex] = entry.getValue();
|
|
+ }
|
|
+
|
|
+ this.gameruleArray = values;
|
|
+ // Airplane end
|
|
}
|
|
|
|
public <T extends GameRules.Value<T>> T getRule(GameRules.Key<T> key) {
|
|
- return (T) this.rules.get(key); // CraftBukkit - decompile error
|
|
+ // Airplane start
|
|
+ return key == null ? null : (T) this.gameruleArray[key.gameRuleIndex];
|
|
+ //return (T) this.rules.get(key); // CraftBukkit - decompile error
|
|
+ // Airplane end
|
|
}
|
|
|
|
public CompoundTag createTag() {
|
|
@@ -177,6 +194,10 @@ public class GameRules {
|
|
}
|
|
|
|
public static final class Key<T extends GameRules.Value<T>> {
|
|
+ // Airplane start
|
|
+ private static int lastGameRuleIndex = 0;
|
|
+ public final int gameRuleIndex = lastGameRuleIndex++;
|
|
+ // Airplane end
|
|
|
|
final String id;
|
|
private final GameRules.Category category;
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index 9cafd000b3533ed9fd35df2ec880f55e262084fb..f66894df2902a1f1bba1dd69a3c3ee229fbe13f6 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -176,6 +176,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
public final Map<Explosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions
|
|
public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here
|
|
|
|
+ public final java.util.function.BiPredicate<BlockState, BlockPos> isAlmostSuffocating = (iblockdata, blockposition) -> iblockdata.isSuffocating(this, blockposition); // Airplane - move here, no allocs
|
|
+
|
|
// Paper start - fix and optimise world upgrading
|
|
// copied from below
|
|
public static ResourceKey<DimensionType> getDimensionKey(DimensionType manager) {
|
|
@@ -311,6 +313,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
// Paper end - optimise checkDespawn
|
|
|
|
+ // Airplane start - ensure these get inlined
|
|
+ private final int minBuildHeight, minSection, height, maxBuildHeight, maxSection;
|
|
+ @Override public final int getMaxBuildHeight() { return this.maxBuildHeight; }
|
|
+ @Override public final int getMinSection() { return this.minSection; }
|
|
+ @Override public final int getMaxSection() { return this.maxSection; }
|
|
+ @Override public final int getMinBuildHeight() { return this.minBuildHeight; }
|
|
+ @Override public final int getHeight() { return this.height; }
|
|
+ // Airplane end
|
|
+
|
|
protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, final DimensionType dimensionmanager, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper - Anti-Xray - Pass executor
|
|
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
|
|
this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), this.spigotConfig); // Paper
|
|
@@ -329,6 +340,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
this.profiler = supplier;
|
|
this.levelData = worlddatamutable;
|
|
this.dimensionType = dimensionmanager;
|
|
+ // Airplane start
|
|
+ this.minBuildHeight = dimensionmanager.minY();
|
|
+ this.minSection = SectionPos.blockToSectionCoord(this.minBuildHeight);
|
|
+ this.height = dimensionmanager.height();
|
|
+ this.maxBuildHeight = this.minBuildHeight + this.height;
|
|
+ this.maxSection = SectionPos.blockToSectionCoord(this.maxBuildHeight - 1) + 1;
|
|
+ // Airplane end
|
|
this.dimension = resourcekey;
|
|
this.isClientSide = flag;
|
|
if (dimensionmanager.coordinateScale() != 1.0D) {
|
|
@@ -441,6 +459,91 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
return null;
|
|
}
|
|
|
|
+ // Airplane start - broken down method of raytracing for EntityLiving#hasLineOfSight, replaces IBlockAccess#rayTrace(RayTrace)
|
|
+ public net.minecraft.world.phys.BlockHitResult.Type rayTraceDirect(net.minecraft.world.phys.Vec3 vec3d, net.minecraft.world.phys.Vec3 vec3d1, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) {
|
|
+ // most of this code comes from IBlockAccess#a(RayTrace, BiFunction, Function), but removes the needless functions
|
|
+ if (vec3d.equals(vec3d1)) {
|
|
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
|
+ }
|
|
+
|
|
+ double endX = Mth.lerp(-1.0E-7D, vec3d1.x, vec3d.x);
|
|
+ double endY = Mth.lerp(-1.0E-7D, vec3d1.y, vec3d.y);
|
|
+ double endZ = Mth.lerp(-1.0E-7D, vec3d1.z, vec3d.z);
|
|
+
|
|
+ double startX = Mth.lerp(-1.0E-7D, vec3d.x, vec3d1.x);
|
|
+ double startY = Mth.lerp(-1.0E-7D, vec3d.y, vec3d1.y);
|
|
+ double startZ = Mth.lerp(-1.0E-7D, vec3d.z, vec3d1.z);
|
|
+
|
|
+ int currentX = Mth.floor(startX);
|
|
+ int currentY = Mth.floor(startY);
|
|
+ int currentZ = Mth.floor(startZ);
|
|
+
|
|
+ BlockPos.MutableBlockPos currentBlock = new BlockPos.MutableBlockPos(currentX, currentY, currentZ);
|
|
+
|
|
+ LevelChunk chunk = this.getChunkIfLoaded(currentBlock);
|
|
+ if (chunk == null) {
|
|
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
|
+ }
|
|
+
|
|
+ net.minecraft.world.phys.BlockHitResult.Type initialCheck = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll);
|
|
+
|
|
+ if (initialCheck != null) {
|
|
+ return initialCheck;
|
|
+ }
|
|
+
|
|
+ double diffX = endX - startX;
|
|
+ double diffY = endY - startY;
|
|
+ double diffZ = endZ - startZ;
|
|
+
|
|
+ int xDirection = Mth.sign(diffX);
|
|
+ int yDirection = Mth.sign(diffY);
|
|
+ int zDirection = Mth.sign(diffZ);
|
|
+
|
|
+ double normalizedX = xDirection == 0 ? Double.MAX_VALUE : (double) xDirection / diffX;
|
|
+ double normalizedY = yDirection == 0 ? Double.MAX_VALUE : (double) yDirection / diffY;
|
|
+ double normalizedZ = zDirection == 0 ? Double.MAX_VALUE : (double) zDirection / diffZ;
|
|
+
|
|
+ double normalizedXDirection = normalizedX * (xDirection > 0 ? 1.0D - Mth.frac(startX) : Mth.frac(startX));
|
|
+ double normalizedYDirection = normalizedY * (yDirection > 0 ? 1.0D - Mth.frac(startY) : Mth.frac(startY));
|
|
+ double normalizedZDirection = normalizedZ * (zDirection > 0 ? 1.0D - Mth.frac(startZ) : Mth.frac(startZ));
|
|
+
|
|
+ net.minecraft.world.phys.BlockHitResult.Type result;
|
|
+
|
|
+ do {
|
|
+ if (normalizedXDirection > 1.0D && normalizedYDirection > 1.0D && normalizedZDirection > 1.0D) {
|
|
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
|
+ }
|
|
+
|
|
+ if (normalizedXDirection < normalizedYDirection) {
|
|
+ if (normalizedXDirection < normalizedZDirection) {
|
|
+ currentX += xDirection;
|
|
+ normalizedXDirection += normalizedX;
|
|
+ } else {
|
|
+ currentZ += zDirection;
|
|
+ normalizedZDirection += normalizedZ;
|
|
+ }
|
|
+ } else if (normalizedYDirection < normalizedZDirection) {
|
|
+ currentY += yDirection;
|
|
+ normalizedYDirection += normalizedY;
|
|
+ } else {
|
|
+ currentZ += zDirection;
|
|
+ normalizedZDirection += normalizedZ;
|
|
+ }
|
|
+
|
|
+ currentBlock.set(currentX, currentY, currentZ);
|
|
+ if (chunk.getPos().x != currentBlock.getX() >> 4 || chunk.getPos().z != currentBlock.getZ() >> 4) {
|
|
+ chunk = this.getChunkIfLoaded(currentBlock);
|
|
+ if (chunk == null) {
|
|
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
|
+ }
|
|
+ }
|
|
+ result = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll);
|
|
+ } while (result == null);
|
|
+
|
|
+ return result;
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
public boolean isInWorldBounds(BlockPos pos) {
|
|
return pos.isValidLocation(this); // Paper - use better/optimized check
|
|
}
|
|
@@ -976,13 +1079,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
try {
|
|
tickConsumer.accept(entity);
|
|
MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
|
- } catch (Throwable throwable) {
|
|
+ } catch (Throwable throwable) { // Airplane - diff on change ServerLevel.tick
|
|
if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
|
// Paper start - Prevent tile entity and entity crashes
|
|
final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
|
|
MinecraftServer.LOGGER.error(msg, throwable);
|
|
getCraftServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable)));
|
|
- entity.discard();
|
|
+ entity.discard(); // Airplane - diff on change ServerLevel.tick
|
|
// Paper end
|
|
}
|
|
}
|
|
@@ -1436,6 +1539,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
|
|
public ProfilerFiller getProfiler() {
|
|
+ if (gg.airplane.AirplaneConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Airplane
|
|
return (ProfilerFiller) this.profiler.get();
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
index d63d89960839b506e240349043ab77c4d81ca71a..a0db30970adebb244ff41913e8ec1df1eb472a4d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -418,12 +418,12 @@ public final class NaturalSpawner {
|
|
return spawnGroup == MobCategory.MONSTER && world.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS) && structureAccessor.getStructureAt(pos, false, StructureFeature.NETHER_BRIDGE).isValid() ? StructureFeature.NETHER_BRIDGE.getSpecialEnemies() : chunkGenerator.getMobsAt(biome != null ? biome : world.getBiome(pos), structureAccessor, spawnGroup, pos);
|
|
}
|
|
|
|
- private static BlockPos getRandomPosWithin(Level world, LevelChunk chunk) {
|
|
+ private static BlockPos getRandomPosWithin(ServerLevel world, LevelChunk chunk) { // Airplane - accept serverlevel
|
|
ChunkPos chunkcoordintpair = chunk.getPos();
|
|
- int i = chunkcoordintpair.getMinBlockX() + world.random.nextInt(16);
|
|
- int j = chunkcoordintpair.getMinBlockZ() + world.random.nextInt(16);
|
|
+ int i = chunkcoordintpair.getMinBlockX() + world.getThreadUnsafeRandom().nextInt(16); // Airplane - use thread unsafe random
|
|
+ int j = chunkcoordintpair.getMinBlockZ() + world.getThreadUnsafeRandom().nextInt(16); // Airplane
|
|
int k = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, j) + 1;
|
|
- int l = Mth.randomBetweenInclusive(world.random, world.getMinBuildHeight(), k);
|
|
+ int l = Mth.randomBetweenInclusive(world.getThreadUnsafeRandom(), world.getMinBuildHeight(), k); // Airplane
|
|
|
|
return new BlockPos(i, l, j);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/biome/Biome.java b/src/main/java/net/minecraft/world/level/biome/Biome.java
|
|
index 2b814006fa30dd233dcb345d1d20ce3bf6469053..b5c6b0bc307aef2835761cfa50413cebfd624795 100644
|
|
--- a/src/main/java/net/minecraft/world/level/biome/Biome.java
|
|
+++ b/src/main/java/net/minecraft/world/level/biome/Biome.java
|
|
@@ -104,8 +104,10 @@ public final class Biome {
|
|
private final float scale;
|
|
private final Biome.BiomeCategory biomeCategory;
|
|
private final BiomeSpecialEffects specialEffects;
|
|
- private final ThreadLocal<Long2FloatLinkedOpenHashMap> temperatureCache = ThreadLocal.withInitial(() -> {
|
|
+ // Airplane start - use our cache
|
|
+ private final ThreadLocal<gg.airplane.structs.Long2FloatAgingCache> temperatureCache = ThreadLocal.withInitial(() -> {
|
|
return Util.make(() -> {
|
|
+ /*
|
|
Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) {
|
|
@Override
|
|
protected void rehash(int i) {
|
|
@@ -113,6 +115,10 @@ public final class Biome {
|
|
};
|
|
long2FloatLinkedOpenHashMap.defaultReturnValue(Float.NaN);
|
|
return long2FloatLinkedOpenHashMap;
|
|
+
|
|
+ */
|
|
+ return new gg.airplane.structs.Long2FloatAgingCache(TEMPERATURE_CACHE_SIZE);
|
|
+ // Airplane end
|
|
});
|
|
});
|
|
|
|
@@ -154,17 +160,15 @@ public final class Biome {
|
|
|
|
public final float getTemperature(BlockPos blockPos) {
|
|
long l = blockPos.asLong();
|
|
- Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = this.temperatureCache.get();
|
|
- float f = long2FloatLinkedOpenHashMap.get(l);
|
|
+ // Airplane start
|
|
+ gg.airplane.structs.Long2FloatAgingCache cache = this.temperatureCache.get();
|
|
+ float f = cache.getValue(l);
|
|
if (!Float.isNaN(f)) {
|
|
return f;
|
|
} else {
|
|
float g = this.getHeightAdjustedTemperature(blockPos);
|
|
- if (long2FloatLinkedOpenHashMap.size() == 1024) {
|
|
- long2FloatLinkedOpenHashMap.removeFirstFloat();
|
|
- }
|
|
-
|
|
- long2FloatLinkedOpenHashMap.put(l, g);
|
|
+ cache.putValue(l, g);
|
|
+ // Airplane end
|
|
return g;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java
|
|
index 52de9852f87d346714a950b60a0004d386ac10f0..86bbd9fcee5982cf901ef0480052025ccf57ccb4 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java
|
|
@@ -32,7 +32,10 @@ import org.bukkit.entity.HumanEntity;
|
|
public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity {
|
|
|
|
private static final int EVENT_SET_OPEN_COUNT = 1;
|
|
+ // Airplane start
|
|
private NonNullList<ItemStack> items;
|
|
+ private gg.airplane.structs.ItemListWithBitset optimizedItems;
|
|
+ // Airplane end
|
|
public final ContainerOpenersCounter openersCounter;
|
|
private final ChestLidController chestLidController;
|
|
|
|
@@ -66,9 +69,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
|
|
}
|
|
// CraftBukkit end
|
|
|
|
+ private final boolean isNative = getClass().equals(ChestBlockEntity.class); // Airplane
|
|
protected ChestBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
|
super(type, pos, state);
|
|
- this.items = NonNullList.withSize(27, ItemStack.EMPTY);
|
|
+ // Airplane start
|
|
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(27);
|
|
+ this.items = this.optimizedItems.nonNullList;
|
|
+ // Airplane end
|
|
this.openersCounter = new ContainerOpenersCounter() {
|
|
@Override
|
|
protected void onOpen(Level world, BlockPos pos, BlockState state) {
|
|
@@ -99,6 +106,23 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
|
|
this.chestLidController = new ChestLidController();
|
|
}
|
|
|
|
+ // Airplane start
|
|
+ @Override
|
|
+ public boolean hasEmptySlot(Direction enumdirection) {
|
|
+ return isNative ? !this.optimizedItems.hasFullStacks() : super.hasEmptySlot(enumdirection);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isCompletelyFull(Direction enumdirection) {
|
|
+ return isNative ? this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection) : super.isCompletelyFull(enumdirection);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isCompletelyEmpty(Direction enumdirection) {
|
|
+ return isNative && this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection);
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
public ChestBlockEntity(BlockPos pos, BlockState state) {
|
|
this(BlockEntityType.CHEST, pos, state);
|
|
}
|
|
@@ -116,7 +140,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
|
|
@Override
|
|
public void load(CompoundTag nbt) {
|
|
super.load(nbt);
|
|
- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
|
|
+ // Airplane start
|
|
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize());
|
|
+ this.items = this.optimizedItems.nonNullList;
|
|
+ // Airplane end
|
|
if (!this.tryLoadLootTable(nbt)) {
|
|
ContainerHelper.loadAllItems(nbt, this.items);
|
|
}
|
|
@@ -189,7 +216,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
|
|
|
|
@Override
|
|
protected void setItems(NonNullList<ItemStack> list) {
|
|
- this.items = list;
|
|
+ // Airplane start
|
|
+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list);
|
|
+ this.items = this.optimizedItems.nonNullList;
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
|
index 06ffdf7301c8c9a02b4aa3693c69984064c8e085..29e3ef6f74f499d3f4fd0b0f686328602050b9b5 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
|
@@ -44,7 +44,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
|
|
public static final int MOVE_ITEM_SPEED = 8;
|
|
public static final int HOPPER_CONTAINER_SIZE = 5;
|
|
+ // Airplane start
|
|
private NonNullList<ItemStack> items;
|
|
+ private gg.airplane.structs.ItemListWithBitset optimizedItems; // Airplane
|
|
+ // Airplane end
|
|
private int cooldownTime;
|
|
private long tickedGameTime;
|
|
|
|
@@ -80,14 +83,37 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
|
|
public HopperBlockEntity(BlockPos pos, BlockState state) {
|
|
super(BlockEntityType.HOPPER, pos, state);
|
|
- this.items = NonNullList.withSize(5, ItemStack.EMPTY);
|
|
+ // Airplane start
|
|
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(5);
|
|
+ this.items = this.optimizedItems.nonNullList;
|
|
+ // Airplane end
|
|
this.cooldownTime = -1;
|
|
}
|
|
|
|
+ // Airplane start
|
|
+ @Override
|
|
+ public boolean hasEmptySlot(Direction enumdirection) {
|
|
+ return !this.optimizedItems.hasFullStacks();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isCompletelyFull(Direction enumdirection) {
|
|
+ return this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isCompletelyEmpty(Direction enumdirection) {
|
|
+ return this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection);
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
@Override
|
|
public void load(CompoundTag nbt) {
|
|
super.load(nbt);
|
|
- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
|
|
+ // Airplane start
|
|
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize());
|
|
+ this.items = this.optimizedItems.nonNullList;
|
|
+ // Airplane end
|
|
if (!this.tryLoadLootTable(nbt)) {
|
|
ContainerHelper.loadAllItems(nbt, this.items);
|
|
}
|
|
@@ -160,7 +186,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
flag = HopperBlockEntity.a(world, pos, state, (Container) blockEntity, blockEntity); // CraftBukkit
|
|
}
|
|
|
|
- if (!blockEntity.inventoryFull()) {
|
|
+ if (!blockEntity.optimizedItems.hasFullStacks() || !blockEntity.inventoryFull()) { // Airplane - use bitset first
|
|
flag |= booleansupplier.getAsBoolean();
|
|
}
|
|
|
|
@@ -199,7 +225,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
skipPushModeEventFire = skipHopperEvents;
|
|
boolean foundItem = false;
|
|
for (int i = 0; i < hopper.getContainerSize(); ++i) {
|
|
- ItemStack item = hopper.getItem(i);
|
|
+ ItemStack item = hopper.getItem(i); // Airplane
|
|
if (!item.isEmpty()) {
|
|
foundItem = true;
|
|
ItemStack origItemStack = item;
|
|
@@ -403,12 +429,18 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
}
|
|
|
|
private static boolean isFullContainer(Container inventory, Direction direction) {
|
|
- return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams
|
|
+ // Airplane start - use bitsets
|
|
+ //return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams
|
|
+ return inventory.isCompletelyFull(direction);
|
|
+ // Airplane end
|
|
}
|
|
|
|
private static boolean isEmptyContainer(Container inv, Direction facing) {
|
|
// Paper start
|
|
- return allMatch(inv, facing, IS_EMPTY_TEST);
|
|
+ // Airplane start - use bitsets
|
|
+ //return allMatch(inv, facing, IS_EMPTY_TEST);
|
|
+ return inv.isCompletelyEmpty(facing);
|
|
+ // Airplane end
|
|
}
|
|
private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
|
|
if (iinventory instanceof WorldlyContainer) {
|
|
@@ -585,7 +617,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
|
|
if (HopperBlockEntity.canPlaceItemInContainer(to, stack, slot, enumdirection)) {
|
|
boolean flag = false;
|
|
- boolean flag1 = to.isEmpty();
|
|
+ boolean flag1 = to.isCompletelyEmpty(enumdirection); // Airplane
|
|
|
|
if (itemstack1.isEmpty()) {
|
|
// Spigot start - SPIGOT-6693, InventorySubcontainer#setItem
|
|
@@ -733,7 +765,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
|
|
@Override
|
|
protected void setItems(NonNullList<ItemStack> list) {
|
|
- this.items = list;
|
|
+ // Airplane start
|
|
+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list);
|
|
+ this.items = this.optimizedItems.nonNullList;
|
|
+ // Airplane end
|
|
}
|
|
|
|
public static void entityInside(Level world, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
|
index ed3518fe7c841d9e1a9c97626acaa3d765a6d76f..ac564148956beb984650341c5c0994573f4f7225 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
|
@@ -96,13 +96,8 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
|
|
public boolean isEmpty() {
|
|
this.unpackLootTable((Player)null);
|
|
// Paper start
|
|
- for (ItemStack itemStack : this.getItems()) {
|
|
- if (!itemStack.isEmpty()) {
|
|
- return false;
|
|
- }
|
|
- }
|
|
+ return this.isCompletelyEmpty(null); // Airplane - use super
|
|
// Paper end
|
|
- return true;
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
index 86686c24b0b7de4b4bfadbc77419a8872a8e86ee..db7904b1bb402a36684b97c443336630762aeaf9 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
@@ -172,6 +172,20 @@ public class LevelChunk implements ChunkAccess {
|
|
}
|
|
// Paper end - rewrite light engine
|
|
|
|
+ // Airplane start - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively
|
|
+ private int lightningTick;
|
|
+ // shouldDoLightning compiles down to 29 bytes, which with the default of 35 byte inlining should guarantee an inline
|
|
+ public final boolean shouldDoLightning(java.util.Random random) {
|
|
+ if (this.lightningTick-- <= 0) {
|
|
+ this.lightningTick = random.nextInt(100000) << 1;
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
+ public final gg.airplane.entity.CollisionCacheList[] collisionCaches; // Airplane
|
|
+
|
|
public LevelChunk(Level world, ChunkPos pos, ChunkBiomeContainer biomes) {
|
|
this(world, pos, biomes, UpgradeData.EMPTY, EmptyTickList.empty(), EmptyTickList.empty(), 0L, (LevelChunkSection[]) null, (Consumer) null);
|
|
}
|
|
@@ -209,6 +223,12 @@ public class LevelChunk implements ChunkAccess {
|
|
this.inhabitedTime = inhabitedTime;
|
|
this.postLoad = loadToWorldConsumer;
|
|
this.sections = new LevelChunkSection[world.getSectionsCount()];
|
|
+ // Airplane start
|
|
+ this.collisionCaches = new gg.airplane.entity.CollisionCacheList[world.getSectionsCount()];
|
|
+ for (int i = 0; i < this.collisionCaches.length; i++) {
|
|
+ this.collisionCaches[i] = new gg.airplane.entity.CollisionCacheList();
|
|
+ }
|
|
+ // Airplane end
|
|
if (sections != null) {
|
|
if (this.sections.length == sections.length) {
|
|
System.arraycopy(sections, 0, this.sections, 0, this.sections.length);
|
|
@@ -220,6 +240,7 @@ public class LevelChunk implements ChunkAccess {
|
|
this.postProcessing = new ShortList[world.getSectionsCount()];
|
|
// CraftBukkit start
|
|
this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
|
|
+ this.lightningTick = this.level.random.nextInt(100000) << 1; // Airplane - initialize lightning tick
|
|
}
|
|
|
|
public org.bukkit.Chunk bukkitChunk;
|
|
@@ -655,6 +676,17 @@ public class LevelChunk implements ChunkAccess {
|
|
int i1 = blockposition.getZ() & 15;
|
|
BlockState iblockdata1 = chunksection.setBlockState(k, l, i1, iblockdata);
|
|
|
|
+ // Airplane start - notify dirty
|
|
+ SectionPos pos = SectionPos.of(this.chunkPos, j);
|
|
+ gg.airplane.entity.CollisionCache[] caches = this.collisionCaches[j].getRawData();
|
|
+ for (int index = 0; index < caches.length; index++) {
|
|
+ gg.airplane.entity.CollisionCache cache = caches[index];
|
|
+ if (cache != null) {
|
|
+ cache.dirtySection(pos);
|
|
+ }
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
if (iblockdata1 == iblockdata) {
|
|
return null;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
index 72e3264dc74822f746fb84fec0be400047d2d9f5..831e2dbe530daf63ac9e681a92af2740fa18ac8c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
@@ -18,6 +18,9 @@ public class LevelChunkSection {
|
|
short nonEmptyBlockCount; // Paper - package-private
|
|
private short tickingBlockCount;
|
|
private short tickingFluidCount;
|
|
+ // Airplane start
|
|
+ public short fluidStateCount;
|
|
+ // Airplane end
|
|
public final PalettedContainer<BlockState> states; // Paper - package-private // Paper - public
|
|
public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper
|
|
|
|
@@ -88,6 +91,7 @@ public class LevelChunkSection {
|
|
|
|
if (!fluidState.isEmpty()) {
|
|
--this.tickingFluidCount;
|
|
+ --this.fluidStateCount; // Airplane
|
|
}
|
|
|
|
if (!state.isAir()) {
|
|
@@ -102,6 +106,7 @@ public class LevelChunkSection {
|
|
|
|
if (!fluidState2.isEmpty()) {
|
|
++this.tickingFluidCount;
|
|
+ ++this.fluidStateCount; // Airplane
|
|
}
|
|
|
|
return blockState;
|
|
@@ -155,6 +160,7 @@ public class LevelChunkSection {
|
|
if (fluidState.isRandomlyTicking()) {
|
|
this.tickingFluidCount = (short)(this.tickingFluidCount + 1); // Paper
|
|
}
|
|
+ this.fluidStateCount++; // Airplane
|
|
}
|
|
|
|
});
|
|
diff --git a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java
|
|
index da1ad0b2679e392ed81b50c15f012c63cb5c939e..81e83022421e2c311c32f6e6007cfc0c82efb822 100644
|
|
--- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java
|
|
+++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java
|
|
@@ -6,6 +6,8 @@ import javax.annotation.Nullable;
|
|
import net.minecraft.world.phys.AABB;
|
|
|
|
public interface LevelEntityGetter<T extends EntityAccess> {
|
|
+ int getCount(); // Airplane
|
|
+
|
|
@Nullable
|
|
T get(int id);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
|
|
index 3b13f6ea36a3bfecabe09221eb5c48dddab119db..c02b9104c0cc1a7319cca29d5e32a5c2a33bff18 100644
|
|
--- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
|
|
+++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
|
|
@@ -14,6 +14,8 @@ public class LevelEntityGetterAdapter<T extends EntityAccess> implements LevelEn
|
|
this.sectionStorage = cache;
|
|
}
|
|
|
|
+ @Override public int getCount() { return this.visibleEntities.count(); } // Airplane
|
|
+
|
|
@Nullable
|
|
@Override
|
|
public T get(int id) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
|
index 8cb246863e06c5b95ba1442e2ec47095026c4f39..5a72ce41cc57578498c4a072a1eb04ac28958afe 100644
|
|
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
|
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
|
@@ -627,6 +627,12 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
|
PersistentEntitySectionManager.this.knownUuids.remove(this.entity.getUUID());
|
|
this.entity.setLevelCallback(PersistentEntitySectionManager.Callback.NULL);
|
|
PersistentEntitySectionManager.this.removeSectionIfEmpty(this.currentSectionKey, this.currentSection);
|
|
+
|
|
+ // Airplane start
|
|
+ if (this.entity instanceof Entity realEntity) {
|
|
+ realEntity.collisionCache.onRemove();
|
|
+ }
|
|
+ // Airplane end
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
index 7fda7da544b2d0bbd3803d88ee34c92350a8b8ef..adf91f3006a2d224c957f08520f93f761c3ba832 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
@@ -45,6 +45,8 @@ public abstract class FlowingFluid extends Fluid {
|
|
public static final BooleanProperty FALLING = BlockStateProperties.FALLING;
|
|
public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_FLOWING;
|
|
private static final int CACHE_SIZE = 200;
|
|
+ // Airplane start - use our own cache
|
|
+ /*
|
|
private static final ThreadLocal<Object2ByteLinkedOpenHashMap<Block.BlockStatePairKey>> OCCLUSION_CACHE = ThreadLocal.withInitial(() -> {
|
|
Object2ByteLinkedOpenHashMap<Block.BlockStatePairKey> object2bytelinkedopenhashmap = new Object2ByteLinkedOpenHashMap<Block.BlockStatePairKey>(200) {
|
|
protected void rehash(int i) {}
|
|
@@ -53,6 +55,14 @@ public abstract class FlowingFluid extends Fluid {
|
|
object2bytelinkedopenhashmap.defaultReturnValue((byte) 127);
|
|
return object2bytelinkedopenhashmap;
|
|
});
|
|
+ */
|
|
+
|
|
+ private static final ThreadLocal<gg.airplane.structs.FluidDirectionCache<Block.BlockStatePairKey>> localFluidDirectionCache = ThreadLocal.withInitial(() -> {
|
|
+ // Airplane todo - mess with this number for performance
|
|
+ // with 2048 it seems very infrequent on a small world that it has to remove old entries
|
|
+ return new gg.airplane.structs.FluidDirectionCache<>(2048);
|
|
+ });
|
|
+ // Airplane end
|
|
private final Map<FluidState, VoxelShape> shapes = Maps.newIdentityHashMap();
|
|
|
|
public FlowingFluid() {}
|
|
@@ -240,6 +250,8 @@ public abstract class FlowingFluid extends Fluid {
|
|
}
|
|
|
|
private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) {
|
|
+ // Airplane start - modify to use our cache
|
|
+ /*
|
|
Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap;
|
|
|
|
if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) {
|
|
@@ -247,9 +259,16 @@ public abstract class FlowingFluid extends Fluid {
|
|
} else {
|
|
object2bytelinkedopenhashmap = null;
|
|
}
|
|
+ */
|
|
+ gg.airplane.structs.FluidDirectionCache<Block.BlockStatePairKey> cache = null;
|
|
+
|
|
+ if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) {
|
|
+ cache = localFluidDirectionCache.get();
|
|
+ }
|
|
|
|
Block.BlockStatePairKey block_a;
|
|
|
|
+ /*
|
|
if (object2bytelinkedopenhashmap != null) {
|
|
block_a = new Block.BlockStatePairKey(state, fromState, face);
|
|
byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(block_a);
|
|
@@ -260,11 +279,22 @@ public abstract class FlowingFluid extends Fluid {
|
|
} else {
|
|
block_a = null;
|
|
}
|
|
+ */
|
|
+ if (cache != null) {
|
|
+ block_a = new Block.BlockStatePairKey(state, fromState, face);
|
|
+ Boolean flag = cache.getValue(block_a);
|
|
+ if (flag != null) {
|
|
+ return flag;
|
|
+ }
|
|
+ } else {
|
|
+ block_a = null;
|
|
+ }
|
|
|
|
VoxelShape voxelshape = state.getCollisionShape(world, pos);
|
|
VoxelShape voxelshape1 = fromState.getCollisionShape(world, fromPos);
|
|
boolean flag = !Shapes.mergedFaceOccludes(voxelshape, voxelshape1, face);
|
|
|
|
+ /*
|
|
if (object2bytelinkedopenhashmap != null) {
|
|
if (object2bytelinkedopenhashmap.size() == 200) {
|
|
object2bytelinkedopenhashmap.removeLastByte();
|
|
@@ -272,6 +302,11 @@ public abstract class FlowingFluid extends Fluid {
|
|
|
|
object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0));
|
|
}
|
|
+ */
|
|
+ if (cache != null) {
|
|
+ cache.putValue(block_a, flag);
|
|
+ }
|
|
+ // Airplane end
|
|
|
|
return flag;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java
|
|
index 05b64f2730bfe836bd1d72dcfccd9f536908a099..39e941a6a315e2a9fc0f47eb39ef9d2b58069f90 100644
|
|
--- a/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java
|
|
+++ b/src/main/java/net/minecraft/world/level/storage/loot/LootContext.java
|
|
@@ -41,8 +41,10 @@ public class LootContext {
|
|
this.level = world;
|
|
this.lootTables = tableGetter;
|
|
this.conditions = conditionGetter;
|
|
- this.params = ImmutableMap.copyOf(parameters);
|
|
- this.dynamicDrops = ImmutableMap.copyOf(drops);
|
|
+ // Airplane start - use unmodifiable maps instead of immutable ones to skip the copy
|
|
+ this.params = java.util.Collections.unmodifiableMap(parameters);
|
|
+ this.dynamicDrops = java.util.Collections.unmodifiableMap(drops);
|
|
+ // Airplane end
|
|
}
|
|
|
|
public boolean hasParam(LootContextParam<?> parameter) {
|
|
diff --git a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java
|
|
index fcb7bd9f3b6b6ada0f2e5692bce32ab76b8798a7..61c2096f2c034dbc3ad33b193b058c7d0d05e909 100644
|
|
--- a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java
|
|
+++ b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java
|
|
@@ -22,55 +22,82 @@ public class EntityCollisionContext implements CollisionContext {
|
|
return defaultValue;
|
|
}
|
|
};
|
|
- private final boolean descending;
|
|
- private final double entityBottom;
|
|
- private final ItemStack heldItem;
|
|
- private final ItemStack footItem;
|
|
- private final Predicate<Fluid> canStandOnFluid;
|
|
- private final Optional<Entity> entity;
|
|
+ // Airplane start - remove these and pray no plugin uses them
|
|
+ //private final boolean descending;
|
|
+ //private final double entityBottom;
|
|
+ //private final ItemStack heldItem;
|
|
+ //private final ItemStack footItem;
|
|
+ //private final Predicate<Fluid> canStandOnFluid;
|
|
+ // Airplane end
|
|
+ private final @org.jetbrains.annotations.Nullable Entity entity; // Airplane
|
|
|
|
protected EntityCollisionContext(boolean descending, double minY, ItemStack boots, ItemStack heldItem, Predicate<Fluid> walkOnFluidPredicate, Optional<Entity> entity) {
|
|
- this.descending = descending;
|
|
- this.entityBottom = minY;
|
|
- this.footItem = boots;
|
|
- this.heldItem = heldItem;
|
|
- this.canStandOnFluid = walkOnFluidPredicate;
|
|
- this.entity = entity;
|
|
+ // Airplane start
|
|
+ //this.descending = descending;
|
|
+ //this.entityBottom = minY;
|
|
+ //this.footItem = boots;
|
|
+ //this.heldItem = heldItem;
|
|
+ ///this.canStandOnFluid = walkOnFluidPredicate;
|
|
+ this.entity = entity.orElse(null);
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Deprecated
|
|
protected EntityCollisionContext(Entity entity) {
|
|
+ // Airplane start - remove unneeded things
|
|
+ /*
|
|
this(entity.isDescending(), entity.getY(), entity instanceof LivingEntity ? ((LivingEntity)entity).getItemBySlot(EquipmentSlot.FEET) : ItemStack.EMPTY, entity instanceof LivingEntity ? ((LivingEntity)entity).getMainHandItem() : ItemStack.EMPTY, entity instanceof LivingEntity ? ((LivingEntity)entity)::canStandOnFluid : (fluid) -> {
|
|
return false;
|
|
}, Optional.of(entity));
|
|
+ */
|
|
+ this.entity = entity;
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
public boolean hasItemOnFeet(Item item) {
|
|
- return this.footItem.is(item);
|
|
+ // Airplane start
|
|
+ Entity entity = this.entity;
|
|
+ if (entity instanceof LivingEntity livingEntity) {
|
|
+ return livingEntity.getItemBySlot(EquipmentSlot.FEET).is(item);
|
|
+ }
|
|
+ return ItemStack.EMPTY.is(item);
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
public boolean isHoldingItem(Item item) {
|
|
- return this.heldItem.is(item);
|
|
+ // Airplane start
|
|
+ Entity entity = this.entity;
|
|
+ if (entity instanceof LivingEntity livingEntity) {
|
|
+ return livingEntity.getMainHandItem().is(item);
|
|
+ }
|
|
+ return ItemStack.EMPTY.is(item);
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
public boolean canStandOnFluid(FluidState state, FlowingFluid fluid) {
|
|
- return this.canStandOnFluid.test(fluid) && !state.getType().isSame(fluid);
|
|
+ // Airplane start
|
|
+ Entity entity = this.entity;
|
|
+ if (entity instanceof LivingEntity livingEntity) {
|
|
+ return livingEntity.canStandOnFluid(fluid) && !state.getType().isSame(fluid);
|
|
+ }
|
|
+ return false;
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
public boolean isDescending() {
|
|
- return this.descending;
|
|
+ return this.entity != null && this.entity.isDescending(); // Airplane
|
|
}
|
|
|
|
@Override
|
|
public boolean isAbove(VoxelShape shape, BlockPos pos, boolean defaultValue) {
|
|
- return this.entityBottom > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F;
|
|
+ return (this.entity == null ? -Double.MAX_VALUE : entity.getY()) > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F; // Airplane
|
|
}
|
|
|
|
public Optional<Entity> getEntity() {
|
|
- return this.entity;
|
|
+ return Optional.ofNullable(this.entity); // Airplane
|
|
}
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index 99a94898316b0601f55b333c15a9926eae24f8a2..c3a256d0ccb9fb44a5a1f9ffa2d48542ef4e857b 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -251,7 +251,7 @@ import javax.annotation.Nullable; // Paper
|
|
import javax.annotation.Nonnull; // Paper
|
|
|
|
public final class CraftServer implements Server {
|
|
- private final String serverName = "Paper"; // Paper
|
|
+ private final String serverName = "Airplane"; // Paper // Airplane
|
|
private final String serverVersion;
|
|
private final String bukkitVersion = Versioning.getBukkitVersion();
|
|
private final Logger logger = Logger.getLogger("Minecraft");
|
|
@@ -1035,6 +1035,11 @@ public final class CraftServer implements Server {
|
|
plugin.getDescription().getName(),
|
|
"This plugin is not properly shutting down its async tasks when it is being shut down. This task may throw errors during the final shutdown logs and might not complete before process dies."
|
|
));
|
|
+ getLogger().log(Level.SEVERE, String.format("%s Stacktrace", worker.getThread().getName()));
|
|
+ StackTraceElement[] stackTrace = worker.getThread().getStackTrace();
|
|
+ for (StackTraceElement element : stackTrace) {
|
|
+ getLogger().log(Level.SEVERE, " " + element.toString());
|
|
+ }
|
|
}
|
|
}
|
|
// Paper end
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java
|
|
index 0b3b46348ac9195bff1492ffc11fcbff7d3f5c6f..4010052c53f3a2831b4d5aa1c041d85897856acb 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java
|
|
@@ -43,6 +43,6 @@ public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe
|
|
data.set(i, toNMS(ingred.get(i), true));
|
|
}
|
|
|
|
- MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftItemStack.asNMSCopy(this.getResult()), data));
|
|
+ MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftItemStack.asNMSCopy(this.getResult()), data, true));
|
|
}
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
|
|
index 909b2c98e7a9117d2f737245e4661792ffafb744..9da898c6f44832b4421b8c2745e3121bd13a71ab 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
|
|
@@ -22,7 +22,8 @@ public class MinecraftInternalPlugin extends PluginBase {
|
|
private boolean enabled = true;
|
|
|
|
private final String pluginName;
|
|
- private PluginDescriptionFile pdf;
|
|
+ private org.bukkit.plugin.PluginLogger logger;
|
|
+ private PluginDescriptionFile pdf; // Airplane
|
|
|
|
public MinecraftInternalPlugin() {
|
|
this.pluginName = "Minecraft";
|
|
@@ -75,7 +76,12 @@ public class MinecraftInternalPlugin extends PluginBase {
|
|
|
|
@Override
|
|
public PluginLogger getLogger() {
|
|
- throw new UnsupportedOperationException("Not supported.");
|
|
+ // Airplane start
|
|
+ if (this.logger == null) {
|
|
+ this.logger = new org.bukkit.plugin.PluginLogger(this); // Airplane
|
|
+ }
|
|
+ return this.logger;
|
|
+ // Airplane end
|
|
}
|
|
|
|
@Override
|
|
@@ -85,7 +91,7 @@ public class MinecraftInternalPlugin extends PluginBase {
|
|
|
|
@Override
|
|
public Server getServer() {
|
|
- throw new UnsupportedOperationException("Not supported.");
|
|
+ return org.bukkit.Bukkit.getServer(); // Airplane - impl
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
|
index 424f3a9a645d57ad43c52932f5b388b5f146b9f0..d68872771c5d29afc19bf7cd74e970c714267b87 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
|
|
@@ -394,7 +394,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
|
|
|
@Override
|
|
public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
|
|
- return new com.destroystokyo.paper.PaperVersionFetcher();
|
|
+ return new gg.airplane.AirplaneVersionFetcher(); // Airplane
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
|
index d752720f2f234b9dbd2117333fee1bfad663ec02..9868b3a9a35cea9689c76ea9b62f2732ab61c94c 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
|
@@ -11,6 +11,7 @@ public class ServerShutdownThread extends Thread {
|
|
|
|
@Override
|
|
public void run() {
|
|
+ try { gg.airplane.flare.ProfilingManager.stop(); } catch (Throwable t) {} // Airplane - shut down Flare if it's running
|
|
try {
|
|
// Paper start - try to shutdown on main
|
|
server.safeShutdown(false, false);
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
|
|
index 774556a62eb240da42e84db4502e2ed43495be17..1788d79ea489e446d3d9f541693d4ba3dfc26015 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
|
|
@@ -11,7 +11,7 @@ public final class Versioning {
|
|
public static String getBukkitVersion() {
|
|
String result = "Unknown-Version";
|
|
|
|
- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties");
|
|
+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/gg.airplane/airplane-api/pom.properties"); // Tuinity // Airplane
|
|
Properties properties = new Properties();
|
|
|
|
if (stream != null) {
|
|
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
|
index 9c456cce42ef9d1654df9047d6fc1e0da13dc1c9..3cc2cf10efbe05cf91876f760c786ab4d2217858 100644
|
|
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
|
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
|
@@ -37,6 +37,10 @@ import co.aikar.timings.MinecraftTimings;
|
|
import net.minecraft.world.entity.schedule.Activity;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.phys.AABB;
|
|
+// Airplane start
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+import java.util.List;
|
|
+// Airplane end
|
|
|
|
public class ActivationRange
|
|
{
|
|
@@ -210,6 +214,21 @@ public class ActivationRange
|
|
for (int i = 0; i < entities.size(); i++) {
|
|
Entity entity = entities.get(i);
|
|
ActivationRange.activateEntity(entity);
|
|
+
|
|
+ // Airplane start
|
|
+ if (gg.airplane.AirplaneConfig.dearEnabled && entity.getType().dabEnabled) {
|
|
+ Vec3 playerVec = player.position();
|
|
+ Vec3 entityVec = entity.position();
|
|
+ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z;
|
|
+ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ);
|
|
+ entity.activatedPriority = squaredDistance > gg.airplane.AirplaneConfig.startDistanceSquared ?
|
|
+ Math.max(1, Math.min(squaredDistance >> gg.airplane.AirplaneConfig.activationDistanceMod, gg.airplane.AirplaneConfig.maximumActivationPrio)) :
|
|
+ 1;
|
|
+ } else {
|
|
+ entity.activatedPriority = 1;
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
}
|
|
// Paper end
|
|
}
|
|
@@ -226,12 +245,12 @@ public class ActivationRange
|
|
if ( MinecraftServer.currentTick > entity.activatedTick )
|
|
{
|
|
if ( entity.defaultActivationState )
|
|
- {
|
|
+ { // Airplane - diff on change
|
|
entity.activatedTick = MinecraftServer.currentTick;
|
|
return;
|
|
}
|
|
if ( entity.activationType.boundingBox.intersects( entity.getBoundingBox() ) )
|
|
- {
|
|
+ { // Airplane - diff on change
|
|
entity.activatedTick = MinecraftServer.currentTick;
|
|
}
|
|
}
|
|
@@ -279,7 +298,7 @@ public class ActivationRange
|
|
if ( entity instanceof LivingEntity )
|
|
{
|
|
LivingEntity living = (LivingEntity) entity;
|
|
- if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper
|
|
+ if ( living.onClimableCached() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper // Airplane - use cached
|
|
{
|
|
return 1; // Paper
|
|
}
|